1Introduction and Overview
Purpose and Scope
This comprehensive guide provides a detailed roadmap for designing, developing, integrating, and deploying AWS Amplify hosting for Single Page Applications (SPAs) that enable corporate users to access AWS Machine Learning services. The guide focuses on establishing a secure, scalable, and efficient architecture that integrates with enterprise identity systems while leveraging AWS's powerful ML offerings including SageMaker, Bedrock, and Lambda processors for AI capabilities.
This guide serves as a blueprint for organizations looking to provide their employees with secure access to advanced AI capabilities while maintaining corporate security standards and governance requirements.
The scope encompasses:
- Front-end development using the Angular framework
- Authentication via Entra ID integration through AWS Cognito
- Secure API access to internal ML endpoints
- Multi-layered security architecture
- Implementation and deployment workflows
- Best practices for corporate environments
Target Audience
This guide is intended for the following roles. Readers should have intermediate to advanced knowledge of AWS services, authentication mechanisms, and modern web application development practices.
- Solution Architects responsible for designing ML service access solutions
- Cloud Engineers implementing AWS services and security controls
- Full Stack Developers working on Angular SPAs and AWS integrations
- DevOps Engineers managing deployments and CI/CD pipelines
- Security Engineers ensuring compliance with corporate security requirements
- IT Managers overseeing the implementation of AI capabilities within their organization
Prerequisites
To successfully implement the solutions described in this guide, the following prerequisites are necessary.
Knowledge Prerequisites
- Understanding of Angular framework and SPA development
- Familiarity with AWS services, particularly Amplify, Cognito, API Gateway, Lambda, SageMaker, and Bedrock
- Knowledge of authentication protocols (OAuth 2.0, OIDC) and JWT token validation
- Experience with cloud security best practices
- Basic understanding of ML/AI service consumption patterns
Technical Prerequisites
- AWS account with appropriate permissions to create and manage required services
- Entra ID (formerly Azure AD) tenant with administrative access
- Node.js and Angular development environment
- AWS CLI and Amplify CLI configured locally
- Access to internal ML model endpoints and permissions to AWS Bedrock services
- Git repository for source code management
Architecture Benefits
- Multi-layer defense at each tier
- Edge protection via WAF + CloudFront
- Entra ID + Cognito federation
- Lambda authorizer token validation
- Fine-grained IAM access control
- Encrypted data throughout
- Global CloudFront edge delivery
- Static asset caching
- Optimized API routing
- Scalable Amplify hosting
- Async ML service consumption
- Serverless Lambda scalability
- Built-in CI/CD with Amplify
- Simplified UAT/PROD deployments
- Centralized monitoring & logging
- Automated demand-based scaling
- Managed service overhead reduction
- Democratized ML/AI access
- Corporate identity UX consistency
- Serverless cost optimization
- Accelerated AI feature delivery
- Enterprise-grade compliance
2Architecture Overview
High-Level Architecture Diagrams
Overall System Architecture
Angular — runs in browser
Edge delivery & protection
User Pool + Federation
Corporate IdP (SAML)
API Protection
ML Orchestration
Figure 1: Overall System Architecture
Network Security Architecture
CDN + SSL Termination
2nd WAF Layer
Figure 2: Network Security Architecture
Component Descriptions
The architecture consists of the following key components, each serving a specific purpose in the overall system.
| Component | Description | Role in Architecture |
|---|---|---|
| AWS Amplify Hosting | Fully managed service for hosting web applications | Hosts the Angular SPA with built-in CI/CD, global CDN, and HTTPS |
| Angular SPA | Single Page Application built with Angular framework | Provides the user interface for accessing ML services |
| Amazon CloudFront | Content delivery network service | Distributes application content globally and integrates with WAF |
| AWS WAF | Web Application Firewall | Protects against common web exploits at both edge and API levels |
| Entra ID | Microsoft's cloud identity service (formerly Azure AD) | Corporate identity provider for user authentication |
| Amazon Cognito | User authentication and authorization service | Identity federation with Entra ID and token issuance for AWS services |
| API Gateway | Managed service for creating, publishing, and securing APIs | Entry point for backend ML service access with WAF protection |
| Lambda Authorizer | Custom authorization logic for API Gateway | Validates JWT tokens and assigns IAM policies for backend access |
| Lambda Processors | Serverless compute for processing ML requests | Calls Bedrock Foundational Model APIs and processes responses |
| Amazon SageMaker | Fully managed ML service | Hosts custom internal ML models with endpoint access |
| AWS Bedrock | Fully managed foundation model service | Provides access to foundation models from Anthropic (Claude), Amazon (Titan, Nova), Meta (Llama), Mistral AI, Cohere, AI21 Labs, Stability AI, and Writer. Note: OpenAI models are not available on Amazon Bedrock. |
| IAM Roles | Identity and Access Management roles | Controls permissions for accessing AWS services and resources |
AWS Amplify CloudFront Architecture
AWS Amplify Hosting automatically provisions and configures a CloudFront distribution to serve your SPA, providing several integration benefits.
- Global content delivery via edge locations
- Automatic cache management for static assets
- HTTPS enforcement with managed SSL certificates
- Edge computing for optimized delivery
- AWS WAF integration for edge security
- Customizable cache behaviors and TTL settings
- WAF Web ACL associations for threat protection
- Geo-restriction capabilities
- Field-level encryption for sensitive data
- Origin access identity for S3 backend protection
- Custom HTTP security headers
- DDoS protection via AWS Shield
CloudFront Configuration for SPA
The CloudFront distribution configured by Amplify is optimized for Single Page Applications with the following settings.
{
"Origin": {
"Domain": "amplifyapp.com",
"ID": "amplify-hosted-app",
"OriginPath": "/prod/[appId]"
},
"DefaultCacheBehavior": {
"ViewerProtocolPolicy": "redirect-to-https",
"AllowedMethods": ["GET", "HEAD", "OPTIONS"],
"DefaultTTL": 86400,
"MaxTTL": 31536000,
"FunctionAssociations": [
{ "EventType": "viewer-request", "FunctionARN": "arn:aws:cloudfront::function:SPA-Router" }
]
},
"CustomErrorResponses": [
{ "ErrorCode": 404, "ResponseCode": 200, "ResponsePagePath": "/index.html" }
],
"WebACLId": "arn:aws:wafv2:us-east-1:[account]:global/webacl/AmplifyAppProtection/[id]",
"Enabled": true,
"PriceClass": "PriceClass_All"
}
- SPA routing support via custom 404→200 redirect to
index.html - HTTPS enforcement with
redirect-to-httpsviewer protocol policy - Optimized caching strategy for static assets
- WAF integration through
WebACLId - Global distribution through
PriceClass_All
API Gateway and WAF Architecture
While the frontend is protected by CloudFront + WAF, the backend ML services require an additional protection layer provided by a dedicated WAF-protected API Gateway with Lambda authorization.
User's Browser
JWT Validation
Figure 3: API Gateway and WAF Architecture
Private API Gateway
The Private API Gateway is configured to:
- Restrict access to internal network and authorized clients only
- Use resource policies to enforce access control
- Integrate with Lambda Authorizers for token validation
- Implement endpoint routing to appropriate ML services
- Enable detailed API request logging and monitoring
- Implement request throttling to prevent abuse
WAF Configuration
The WAF Web ACL for the API Gateway includes:
- Rate-based rules to prevent DDoS attacks
- IP-based access control rules
- SQL injection protection
- Cross-site scripting (XSS) protection
- Geo-matching rules to restrict access by location
- Custom rules for specific security requirements
"types": ["PRIVATE"]) is accessible only from within a VPC — not from a public browser. An Amplify-hosted Angular SPA runs in the user's browser outside any VPC, so it cannot directly call a Private API Gateway endpoint. Choose one of the following patterns to resolve this:
- Regional API Gateway (recommended for browser SPAs): Use a Regional endpoint type protected by WAF, Cognito authorizer, and resource policies.
- VPC-peered proxy: Place an NLB or ALB inside a VPC to proxy requests from CloudFront to the Private API Gateway via a VPC Endpoint.
- AWS PrivateLink + CloudFront Origin: Route CloudFront to an internal origin using PrivateLink, keeping the API truly private.
API Gateway Endpoint Configuration
{
"apiId": "abc123def456",
"endpointConfiguration": {
"types": ["PRIVATE"],
"vpcEndpointIds": ["vpce-0abc123def456789"]
},
"policy": {
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:region:account-id:api-id/*",
"Condition": {
"StringEquals": { "aws:SourceVpce": "vpce-0abc123def456789" }
}
}]
}
}
Architecture Layers
The solution is organized into four distinct layers, promoting separation of concerns, better security, and easier maintenance.
- AWS Amplify Hosting — Managed hosting with built-in CI/CD capabilities
- Angular SPA — User interface for accessing ML services
- Amplify Libraries — Client-side auth, API, and AWS service integrations
- CloudFront Distribution — Automatically configured for content delivery and edge security
- SSL/TLS Termination — HTTPS enforcement with managed certificates
Key Responsibilities
- User interface presentation and interaction
- Client-side authentication flow handling
- Token management and secure storage
- API requests and response rendering
- Azure Entra ID — Enterprise identity provider, handles user authentication and directory
- AWS Cognito User Pool — Identity federation with Entra ID and JWT token issuance
- Identity Federation — OIDC-based integration between Entra ID and Cognito
- IAM Roles — Authorization and access control for AWS resources
Key Responsibilities
- User authentication through corporate credentials
- Identity federation between Entra ID and AWS
- JWT token issuance, management, and refresh
- Role-based access control
- AWS WAF — Web Application Firewall at CloudFront and API Gateway levels
- API Gateway — Managed API service configured for private access
- Lambda Authorizers — Custom JWT validation and IAM permission assignment
- API Routes — Endpoint definitions for different ML services
Key Responsibilities
- Request routing to appropriate backend services
- Token validation and authorization
- Rate limiting, throttling, and threat protection
- Request/response transformation and logging
- SageMaker Endpoints — Managed hosting for custom internal ML models
- Bedrock Foundation Models — Access to models from Anthropic, Amazon, Meta, Mistral, Cohere, and others
- Lambda Processors — Serverless functions for Bedrock API requests and business logic
- Model Monitoring — Performance and usage tracking for ML models
Key Responsibilities
- ML model inference and processing
- Request transformation for model compatibility
- Response processing and formatting
- Usage tracking, quota management, and error handling
3Authentication and Authorization Flows
Authentication Flow Diagrams
User Authentication Flow
Amplify Hosted
signInWithRedirect()
Hosted UI
SAML redirect
Credential validation
SAML assertion received
Access + ID + Refresh tokens
via /auth/callback redirect
Amplify manages token cache
Figure 4: User Authentication Flow
API Authorization Flow
Authenticated user
Authorization: Bearer <access_token>
Block malicious traffic
Route to Lambda Authorizer
Verify RS256 signature · validate claims · check cognito:groups
User in Administrators or MLUsers
Lambda Processor invoked
Figure 5: API Authorization Flow
JWT Token Validation Process
The Lambda Authorizer performs a multi-step validation process on every incoming JWT access token before granting access to ML services.
- Extract Token — Remove the
Bearerprefix from the Authorization header - Decode Header — Decode the JWT header without verification to extract the
kid(key ID) - Fetch Public Key — Retrieve the matching RSA public key from Cognito's JWKS endpoint
- Verify Signature — Verify the token's RS256 signature using the public key
- Validate Claims — Check expiration (
exp), issue time (iat), issuer (iss). For Cognito access tokens, validateclient_id(notaud) and confirmtoken_use === "access" - Check Group Membership — Read group membership from the
cognito:groupsclaim and verify required groups - Generate IAM Policy — Return an Allow or Deny policy to API Gateway with enriched user context
client_id rather than the standard aud claim for audience identification. Group membership is stored under cognito:groups (not groups). Configuring JWT libraries with audience: CLIENT_ID will cause InvalidAudienceError on access tokens — disable audience verification in the library and validate client_id manually.
Sample JWT Access Token Payload
{
"sub": "user123",
"iss": "https://cognito-idp.region.amazonaws.com/us-east-1_example",
"client_id": "clientidexample",
"token_use": "access",
"scope": "openid profile email",
"auth_time": 1684858239,
"exp": 1684861839,
"iat": 1684858239,
"username": "[email protected]",
"cognito:groups": ["ML_Users", "Data_Scientists"]
}
Lambda Authorizer Implementation
const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
const client = jwksClient({
jwksUri: `https://cognito-idp.${process.env.REGION}.amazonaws.com/${process.env.USER_POOL_ID}/.well-known/jwks.json`,
cache: true,
cacheMaxAge: 600000 // 10 minutes
});
const getSigningKey = (kid) => new Promise((resolve, reject) => {
client.getSigningKey(kid, (err, key) => {
if (err) return reject(err);
resolve(key.publicKey || key.rsaPublicKey);
});
});
// Generate an IAM policy document for API Gateway
const generatePolicy = (principalId, effect, resource, context = {}) => ({
principalId,
policyDocument: {
Version: '2012-10-17',
Statement: [{ Action: 'execute-api:Invoke', Effect: effect, Resource: resource }]
},
context
});
// Return true if user belongs to an allowed group for this method
const determineAccess = (groups, methodArn) => {
const allowedGroups = ['Administrators', 'MLUsers'];
return groups.some(g => allowedGroups.includes(g));
};
exports.handler = async (event) => {
try {
const token = event.authorizationToken.replace('Bearer ', '');
const decoded = jwt.decode(token, { complete: true });
if (!decoded) throw new Error('Invalid token format');
const signingKey = await getSigningKey(decoded.header.kid);
// Cognito ACCESS tokens use client_id, not aud — omit audience option
const verifiedToken = jwt.verify(token, signingKey, {
issuer: `https://cognito-idp.${process.env.REGION}.amazonaws.com/${process.env.USER_POOL_ID}`,
// audience omitted intentionally — Cognito access tokens use client_id
});
// Manually validate client_id (replaces standard audience check)
if (verifiedToken.client_id !== process.env.CLIENT_ID) {
throw new Error('Invalid client_id');
}
// Group membership is in cognito:groups — NOT groups
const userGroups = verifiedToken['cognito:groups'] || [];
const allowed = determineAccess(userGroups, event.methodArn);
return generatePolicy(
verifiedToken.sub,
allowed ? 'Allow' : 'Deny',
event.methodArn,
{ username: verifiedToken.username || verifiedToken.sub, groups: userGroups.join(',') }
);
} catch (error) {
console.error('Authorization error:', error);
throw new Error('Unauthorized');
}
};
4Data Flow Architecture
End-to-End Data Flow
The end-to-end data flow describes how data travels through the system, from the initial user request to the final ML response. The following steps walk through the complete journey.
- Initial Request — User accesses the SPA through a web browser
- Content Delivery — CloudFront delivers the SPA application files from the nearest edge location
- Authentication — User authenticates through Entra ID via Cognito federation and receives JWT tokens
- Local Interaction — User interacts with the Angular SPA interface to submit an ML request
- API Request Preparation — SPA prepares the request payload and attaches the JWT access token to the Authorization header
- WAF Filtering — The API Gateway WAF evaluates the request against rule sets; malicious traffic is rejected
- Lambda Authorization — Lambda Authorizer validates the JWT, checks group membership, and generates an IAM policy
- Backend Routing — Depending on the request type: either a direct SageMaker endpoint call (custom models) or a Lambda Processor invocation (Bedrock models)
- Response Processing — ML service responses are processed, formatted, and returned through API Gateway
- Result Delivery — Results are displayed in the Angular SPA for the user
Data Transformation Points
| Transformation Point | Transformation Type | Purpose |
|---|---|---|
| Client-side (Angular SPA) | User input formatting | Prepare data in format suitable for API requests |
| API Gateway | Request/response mapping | Transform between frontend and backend data formats |
| Lambda Processor | Model-specific formatting | Transform generic requests into Converse API format for Bedrock |
| SageMaker | Model inference transformation | Raw model output processed into structured response |
| Bedrock | Foundation model response | Standardized response via Converse API across all model families |
SageMaker Integration Flow
For custom internal ML models, requests are routed directly to SageMaker real-time inference endpoints.
Figure 6: SageMaker Integration Flow
Bedrock Integration Flow
For foundation model access, a Lambda Processor invokes the Bedrock Converse API, which provides a unified interface across all supported model families.
Figure 7: Bedrock Integration Flow
Bedrock Lambda Processor
const { BedrockRuntimeClient, ConverseCommand } = require("@aws-sdk/client-bedrock-runtime");
const bedrockClient = new BedrockRuntimeClient({ region: process.env.AWS_REGION || "us-east-1" });
exports.handler = async (event) => {
const { prompt, model = "anthropic.claude-3-5-sonnet-20241022-v2:0", maxTokens = 1000, temperature = 0.7, systemPrompt } = JSON.parse(event.body);
const params = {
modelId: model,
messages: [{ role: "user", content: [{ text: prompt }] }],
inferenceConfig: { maxTokens, temperature, topP: 0.9 }
};
if (systemPrompt) params.system = [{ text: systemPrompt }];
const response = await bedrockClient.send(new ConverseCommand(params));
const completion = response.output?.message?.content?.[0]?.text || "";
return {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model,
completion,
stopReason: response.stopReason,
usage: {
input_tokens: response.usage?.inputTokens || 0,
output_tokens: response.usage?.outputTokens || 0,
total_tokens: (response.usage?.inputTokens || 0) + (response.usage?.outputTokens || 0)
}
})
};
};
Supported Bedrock Model Families
| Model Family | Converse API | Recommended Model ID | Notes |
|---|---|---|---|
| Anthropic Claude 3/3.5 | Supported | anthropic.claude-3-5-sonnet-20241022-v2:0 | Use Converse API. Legacy Human/Assistant format deprecated. |
| Amazon Nova / Titan | Supported | amazon.nova-pro-v1:0 | Nova is current-gen; Titan Text is legacy but available. |
| Meta Llama 3 | Supported | meta.llama3-70b-instruct-v1:0 | Open-weight; Llama 3 replaces Llama 2 on Bedrock. |
| Mistral AI | Supported | mistral.mistral-large-2402-v1:0 | Strong multilingual performance. |
| AI21 Jamba | Supported | ai21.jamba-1-5-large-v1:0 | Replaces the Jurassic series. |
5Security Architecture
Multi-Layer Security Model
The architecture implements defense-in-depth with security controls at every layer. No single point of failure can expose the ML services backend.
| Layer | Controls | Threats Mitigated |
|---|---|---|
| Edge (CloudFront + WAF) | WAF Web ACL, geo-restrictions, rate limiting, DDoS protection | Bots, DDoS, SQL injection, XSS, geo-based attacks |
| Identity (Cognito + Entra ID) | OIDC federation, MFA, short-lived JWT tokens, session management | Credential theft, unauthorized access, account takeover |
| API (API Gateway + Lambda Authorizer) | JWT validation, IAM policy generation, request throttling, input validation | Token replay, privilege escalation, API abuse |
| Compute (Lambda) | Least-privilege IAM roles, VPC isolation, encrypted environment variables | Lateral movement, data exfiltration, over-privileged access |
| ML Services (SageMaker + Bedrock) | VPC endpoints, IAM resource policies, CloudWatch monitoring | Unauthorized model access, data leakage, cost abuse |
- AWS Managed Rules — Core Rule Set (CRS)
- AWS Managed Rules — Known Bad Inputs
- Rate-based rules (per IP threshold)
- Geo-match rules for regional restrictions
- Custom rules for application-specific patterns
- IP set allow/deny lists
- Lambda Authorizer: read-only Cognito JWKS access
- Lambda Processors: specific SageMaker and Bedrock ARN permissions only
- No wildcard
*resource permissions in production - Separate execution roles per Lambda function
- Regular IAM access reviews via Access Analyzer
JWT Token Security Implementation
Token Validation in Python (Lambda Authorizer)
import jwt, requests, os
from functools import lru_cache
REGION = os.environ['COGNITO_REGION']
POOL_ID = os.environ['USER_POOL_ID']
CLIENT_ID = os.environ['CLIENT_ID']
JWKS_URL = f'https://cognito-idp.{REGION}.amazonaws.com/{POOL_ID}/.well-known/jwks.json'
@lru_cache(maxsize=1)
def get_jwks():
return requests.get(JWKS_URL, timeout=10).json()
def get_public_key(kid):
for key in get_jwks()['keys']:
if key['kid'] == kid:
return jwt.algorithms.RSAAlgorithm.from_jwk(key)
raise ValueError(f'Key not found: {kid}')
def validate_token(token):
header = jwt.get_unverified_header(token)
public_key = get_public_key(header['kid'])
# Cognito access tokens use client_id not aud — disable audience verification
claims = jwt.decode(
token, public_key, algorithms=['RS256'],
options={'verify_aud': False},
issuer=f'https://cognito-idp.{REGION}.amazonaws.com/{POOL_ID}'
)
if claims.get('client_id') != CLIENT_ID:
raise ValueError('Invalid client_id')
if claims.get('token_use') != 'access':
raise ValueError('Invalid token_use')
return claims
def lambda_handler(event, context):
token = event.get('authorizationToken', '').replace('Bearer ', '')
claims = validate_token(token)
username = claims.get('username', claims.get('sub'))
# Group membership is in cognito:groups (not groups)
groups = claims.get('cognito:groups', [])
if not any(g in ['Administrators', 'MLUsers'] for g in groups):
return generate_policy(username, 'Deny', event['methodArn'])
return generate_policy(username, 'Allow', event['methodArn'], {
'username': username,
'email': claims.get('email', ''),
'groups': ','.join(groups),
'sub': claims.get('sub', '')
})
Data Protection and Compliance
- TLS 1.2 minimum for all HTTPS connections (CloudFront Security Policy:
TLSv1.2_2021) - TLS 1.3 negotiated where clients support it
- Certificate management via AWS Certificate Manager
- API Gateway SSL termination
- VPC endpoint encryption for internal traffic
- S3 buckets encrypted with AES-256 or SSE-KMS
- CloudWatch Logs encrypted with KMS CMK
- Lambda environment variables encrypted at rest
- AWS KMS with customer-managed keys
- Automated key rotation policies
dataTraceEnabled: true on API Gateway stages in production. This setting logs the full request and response payloads (including ML inputs/outputs) to CloudWatch, creating a significant security and compliance risk. Use structured access logging with accessLogDestination instead.
CORS Configuration
In production, always specify explicit allowed origins rather than using the wildcard *. The origins should correspond to your actual Amplify hosting domain and any custom domains in use.
defaultCorsPreflightOptions: {
// Explicitly list allowed origins — never use Cors.ALL_ORIGINS in production
allowOrigins: [
'https://your-app.amplifyapp.com', // Amplify hosted domain
'https://your-custom-domain.com' // Custom domain (if applicable)
],
allowMethods: ['GET', 'POST', 'OPTIONS'],
allowHeaders: ['Content-Type', 'Authorization', 'X-Amz-Date', 'X-Api-Key'],
allowCredentials: true
}
6Implementation Guide
Setting Up AWS Amplify
AWS Amplify Hosting can be connected to your Git repository (GitHub, GitLab, Bitbucket, or AWS CodeCommit) for automated CI/CD deployments on every push to your configured branch.
Amplify CLI Initialization
# Install and configure Amplify CLI
npm install -g @aws-amplify/cli
amplify configure
# Initialize Amplify in your Angular project
cd your-angular-project
amplify init
# Add hosting (choose Amplify Console for CI/CD)
amplify add hosting
amplify publish
Amplify Build Specification
The amplify.yml build spec is for frontend only. Infrastructure (CDK/CloudFormation) deployments must run in a separate CI/CD pipeline — not inside Amplify Hosting, which lacks the broad IAM permissions CDK requires.
version: 1
frontend:
phases:
preBuild:
commands:
- cd frontend
- npm ci
build:
commands:
- npm run build:prod
artifacts:
baseDirectory: frontend/dist/ml-app
files:
- '**/*'
cache:
paths:
- frontend/node_modules/**/*
Configuring Cognito User Pools
import * as cognito from 'aws-cdk-lib/aws-cognito';
this.userPool = new cognito.UserPool(this, 'MLAppUserPool', {
selfSignUpEnabled: false,
signInAliases: { email: true },
passwordPolicy: {
minLength: 12,
requireLowercase: true, requireUppercase: true,
requireDigits: true, requireSymbols: true
},
mfa: cognito.Mfa.REQUIRED,
mfaSecondFactor: { sms: true, otp: true },
accountRecovery: cognito.AccountRecovery.EMAIL_ONLY
});
this.userPoolClient = new cognito.UserPoolClient(this, 'MLAppClient', {
userPool: this.userPool,
generateSecret: false,
authFlows: { userSrp: true },
oAuth: {
flows: { authorizationCodeGrant: true },
scopes: [cognito.OAuthScope.OPENID, cognito.OAuthScope.EMAIL, cognito.OAuthScope.PROFILE],
callbackUrls: ['https://your-app.amplifyapp.com/auth/callback'],
logoutUrls: ['https://your-app.amplifyapp.com/auth/logout']
},
accessTokenValidity: cdk.Duration.hours(1),
idTokenValidity: cdk.Duration.hours(1),
refreshTokenValidity: cdk.Duration.days(30)
});
Integrating with Entra ID
const samlProvider = new cognito.CfnUserPoolIdentityProvider(this, 'EntraIDProvider', {
userPoolId: this.userPool.userPoolId,
providerName: 'EntraID',
providerType: 'SAML',
providerDetails: {
MetadataURL: 'https://login.microsoftonline.com/[tenant-id]/federationmetadata/2007-06/federationmetadata.xml',
IDPSignout: 'true'
},
attributeMapping: {
email: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress',
given_name: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname',
family_name: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname',
'custom:department': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/department'
}
});
Angular SPA Development
Environment Configuration
export const environment = {
production: false,
cognito: {
userPoolId: 'us-east-1_XXXXXXXXX',
userPoolWebClientId: 'XXXXXXXXXXXXXXXXXXXXXXXXXX',
// domainPrefix is the Cognito hosted UI prefix — NOT the userPoolId
domainPrefix: 'my-company-ml-app-dev',
region: 'us-east-1',
identityPoolId: 'us-east-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
},
api: {
baseUrl: 'https://api.example.com/dev',
region: 'us-east-1'
}
};
Implementing Authentication
Use AWS Amplify v6 modular imports for authentication. The legacy import { Auth } from 'aws-amplify' pattern from Amplify v5 is no longer supported in v6 — use named function imports from aws-amplify/auth instead.
import { Amplify } from 'aws-amplify';
import { environment } from '../environments/environment';
// Amplify v6 configuration structure
Amplify.configure({
Auth: {
Cognito: {
userPoolId: environment.cognito.userPoolId,
userPoolClientId: environment.cognito.userPoolWebClientId,
identityPoolId: environment.cognito.identityPoolId,
loginWith: {
oauth: {
// Use domainPrefix — NOT userPoolId — for the OAuth domain
domain: `${environment.cognito.domainPrefix}.auth.${environment.cognito.region}.amazoncognito.com`,
scopes: ['email', 'openid', 'profile'],
redirectSignIn: [window.location.origin + '/auth/callback'],
redirectSignOut: [window.location.origin + '/auth/logout'],
responseType: 'code'
}
}
}
}
});
// Amplify v6: modular imports from 'aws-amplify/auth'
import { signIn, signOut, getCurrentUser, fetchAuthSession, signInWithRedirect } from 'aws-amplify/auth';
@Injectable({ providedIn: 'root' })
export class AuthService {
private currentUserSubject = new BehaviorSubject<User | null>(null);
async initializeAuth(): Promise<void> {
try {
const { username, userId } = await getCurrentUser();
const session = await fetchAuthSession();
const payload = session.tokens?.idToken?.payload;
this.currentUserSubject.next({
username, userId,
email: payload?.['email'] as string || '',
groups: (payload?.['cognito:groups'] as string[]) || []
});
} catch { /* no active session */ }
}
async signInWithSAML(): Promise<void> {
await signInWithRedirect({ provider: { custom: 'EntraID' } });
}
async getAccessToken(): Promise<string> {
const session = await fetchAuthSession();
return session.tokens?.accessToken?.toString() ?? '';
}
async signOut(): Promise<void> {
await signOut();
this.currentUserSubject.next(null);
}
}
API Gateway Configuration
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as logs from 'aws-cdk-lib/aws-logs';
this.api = new apigateway.RestApi(this, 'MLAppAPI', {
restApiName: 'ML App API',
endpointConfiguration: { types: [apigateway.EndpointType.REGIONAL] },
defaultCorsPreflightOptions: {
allowOrigins: ['https://your-app.amplifyapp.com'],
allowMethods: ['GET', 'POST', 'OPTIONS'],
allowHeaders: ['Content-Type', 'Authorization', 'X-Amz-Date'],
allowCredentials: true
},
deployOptions: {
stageName: 'prod',
throttlingRateLimit: 1000,
throttlingBurstLimit: 2000,
loggingLevel: apigateway.MethodLoggingLevel.ERROR,
dataTraceEnabled: false, // Never enable in production
metricsEnabled: true,
accessLogDestination: new apigateway.LogGroupLogDestination(
new logs.LogGroup(this, 'ApiAccessLogs', { retention: logs.RetentionDays.THIRTY_DAYS })
),
accessLogFormat: apigateway.AccessLogFormat.jsonWithStandardFields()
}
});
Lambda Authorizer Implementation
See the complete Python Lambda Authorizer implementation in Section 5 — JWT Token Security Implementation.
ML Services Integration
See the complete Bedrock Converse API implementation in Section 4 — Bedrock Integration Flow. For SageMaker integration, invoke real-time endpoints via the AWS SDK using the endpoint name from your deployed SageMaker model.
import boto3, json
sagemaker_runtime = boto3.client('sagemaker-runtime', region_name=os.environ['AWS_REGION'])
def invoke_sagemaker_endpoint(endpoint_name, payload):
response = sagemaker_runtime.invoke_endpoint(
EndpointName=endpoint_name,
ContentType='application/json',
Accept='application/json',
Body=json.dumps(payload)
)
result = json.loads(response['Body'].read().decode())
return result