OIDC implementation workflow

The authentication flow consists of the following steps:
HTTP Request with Bearer token to DICOMweb endpoint
AWS HealthImaging checks token expiry
Forward request to Lambda authorizer
Lambda validates token with Identity Provider (IdP)
Lambda returns IAM role
Policy evaluation and user authentication complete
Prerequisites
1. Lambda Authorizer Setup
Create authorizer that accepts AuthInput and returns AuthResult
Validates BearerToken (signature, expiry, scopes, issuer, and business rules)
Returns IAM Role ARN with required DICOMweb operation permissions
Must respond in ≤ 1 second (configure provisioned concurrency)
Implement token extraction:
// in Node.js
export const handler = async (event) => {
try {
const token = event.bearerToken;
const operation = event.operation;
}
}
2. Datastore Configuration
Enable feature by providing lambdaAuthorizerArn at creation
Note
Your AWS account is billed for Lambda invocations and duration. For more information, see AWS Lambda pricing.
Authorization Process Details
Token Validation Rules
HealthImaging evaluates the following token claims:
exp
– Must be after the current time in UTCnbf
– Must be before the current time in UTCiat
– Must be before current time in UTC and NOT earlier than 12 hours before (maximum token lifetime)
Event and Response Schemas
AHI invokes your function with the following input and expects the following output.
Authorizer input
{
"datastoreId": "{datastore id}",
"operation": "{Healthimaging API name e.g. GetDICOMInstance}",
"bearerToken": "{access token}"
}
Authorizer output
{
"isTokenValid": {true or false},
"roleArn": "{role arn or empty string meaning to deny the request explicitly}"
}
Request Processing
Initial Request Handling:
If no Authorization: Bearer header → request proceeds to SigV4 auth
If Bearer token present:
Resolves datastore's LambdaAuthorizerArn
Invokes authorizer using Forward Access Sessions (FAS)
Lambda Authorization Process:
Receives AuthInput with datastoreId, operation, and bearerToken
Must complete validation within 1 second
Returns AuthResult with validation status and role ARN
Implementation Flow
Client-Side Authentication Flow
User Authentication: Direct user to IdP's authorization endpoint
Token Acquisition: Exchange authorization code for ID and Access tokens (JWT)
API Call: Include access token in HTTP Authorization Bearer header
Token Validation: Full validation process by HealthImaging and Lambda authorizer
Setup Steps
Lambda Authorizer Implementation
Implement AuthInput/AuthResult interface
Validate token (signature, expiration, issuer, audience, scopes)
Return decision and IAM Role ARN
IAM Configuration
Create policy with minimum DICOMweb operation permissions
Create role with trust policy for
medical-imaging.
region
.amazonaws.comConfigure Lambda execution permissions
Add resource policy with AllowHealthLakeInvocation statement for datastore ARN
The authorizer should have the following resource policy statement:
{
"Sid": "health-imaging",
"Effect": "Allow",
"Principal": {
"Service": "medical-imaging.region.amazonaws.com"
},
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:region
:123456789012:function:LambdaAuthorizerName"
}
For the IAM role the auth lambda returned, it must have the following trust relationship policy:
{
"Effect": "Allow",
"Principal": {
"Service": "medical-imaging.region
.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
Concurrency Configuration
Set up provisioned concurrency for ≤ 1 second SLO
Implement cold-start mitigations if needed
Lambda Authorizer Template
import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';
const CACHE_TTL = 10 * 60 * 1000;
const client = jwksClient({
jwksUri: '{Jwks Url}',
cache: true,
cacheMaxEntries: 5,
cacheMaxAge: 600000,
rateLimit: true,
jwksRequestsPerMinute: 10
});
export const handler = async (event) => {
try {
console.log(event);
const token = event.bearerToken;
const decoded = jwt.decode(token, { complete: true });
if (!decoded || !decoded.header.kid) {
console.log('Invalid token structure');
return generatePolicy(null, false);
}
const key = await client.getSigningKey(decoded.header.kid);
const signingKey = key.getPublicKey();
const payload = jwt.verify(token, signingKey, {
issuer: '{issuer to be verified}',
algorithms: ['RS256'],
// Additional verification parameters as needed
});
return generatePolicy(payload.sub, true);
} catch (error) {
console.error('Authorization error:', error);
return generatePolicy(null, false);
}
};
function generatePolicy(user, isValid) {
return {
isTokenValid: isValid,
roleArn: user ? `arn:aws:iam::123456789012:role/${user}` : ""
};
}
Final Validation by HealthImaging
After receiving AuthResult, HealthImaging:
Verifies all token claims (nbf, exp, iat)
Validates Role ARN format
Assumes the role
Signs the original request with SigV4 on user's behalf
Processes the DICOMweb request
Exceptions
Condition | AHI response |
---|---|
Lambda Authorizer does not exist or is invalid | 424 Authorizer Misconfiguration |
Authorizer terminated due to execution failure | 424 Authorizer Failed |
Any other unmapped authorizer error | 424 Authorizer Failed |
Authorizer returned invalid/ill-formed response | 424 Authorizer Misconfiguration |
Authorizer ran more than 1s | 408 Authorizer Timeout |
Token is expired or otherwise invalid | 403 Invalid or Expired Token |
AHI can't federate the returned IAM Role due to authorizer misconfiguration | 424 Authorizer Misconfiguration |
Authorizer returned an empty Role | 403 Access Denied |
Returned Role is not callable (assume-role/trust misconfig) | 424 Authorizer Misconfiguration |
Request rate exceeds DICOMweb Gateway limits | 429 Too many requests |
Datastore, Return Role, or Authorizer Cross Account/Cross Region | 424 Authorizer Cross Account/Cross Region Access |