in src/smartHandler.ts [99:177]
async verifyAccessToken(request: VerifyAccessTokenRequest): Promise<UserIdentity> {
let decodedToken: any;
if (this.config.tokenIntrospection) {
decodedToken = await introspectJwtToken(
request.accessToken,
this.config.expectedAudValue,
this.config.expectedIssValue,
this.config.tokenIntrospection,
);
} else if (this.jwksClient) {
decodedToken = await verifyJwtToken(
request.accessToken,
this.config.expectedAudValue,
this.config.expectedIssValue,
this.jwksClient,
);
} else {
throw Error(
`Authorization configuration not properly set up. Either 'tokenIntrospection' or 'jwksEndpoint' must be present`,
);
}
const fhirUserClaim = get(decodedToken, this.config.fhirUserClaimPath);
const patientContextClaim = get(decodedToken, `${this.config.launchContextPathPrefix}patient`);
const fhirServiceBaseUrl = request.fhirServiceBaseUrl ?? this.apiUrl;
// get just the scopes that apply to this request
const scopes = getScopes(decodedToken[this.config.scopeKey]);
const usableScopes = filterOutUnusableScope(
scopes,
this.config.scopeRule,
request.operation,
this.isUserScopeAllowedForSystemExport,
request.resourceType,
request.bulkDataAuth,
patientContextClaim,
fhirUserClaim,
);
if (!usableScopes.length) {
logger.warn('User supplied scopes are insufficient', {
usableScopes,
operation: request.operation,
resourceType: request.resourceType,
});
throw new UnauthorizedError('access_token does not have permission for requested operation');
}
const userIdentity: UserIdentity = clone(decodedToken);
if (request.bulkDataAuth) {
if (!userIdentity.sub) {
logger.error('A JWT token is without a `sub` claim; we cannot process the bulk action without one.');
throw new UnauthorizedError('User does not have permission for requested operation');
}
if (
!usableScopes.some((scope: string) => {
return scope.startsWith('system');
})
) {
// if requestor is relying on the "user" scope we need to verify they are coming from the correct endpoint & resourceType
const fhirUser = getFhirUser(fhirUserClaim);
if (
fhirUser.hostname !== fhirServiceBaseUrl ||
!this.bulkDataAccessTypes.includes(fhirUser.resourceType)
) {
throw new UnauthorizedError('User does not have permission for requested operation');
}
}
}
if (fhirUserClaim && usableScopes.some((scope) => scope.startsWith('user/'))) {
userIdentity.fhirUserObject = getFhirUser(fhirUserClaim);
}
if (patientContextClaim && usableScopes.some((scope) => scope.startsWith('patient/'))) {
userIdentity.patientLaunchContext = getFhirResource(patientContextClaim, fhirServiceBaseUrl);
}
userIdentity.scopes = scopes;
userIdentity.usableScopes = usableScopes;
return userIdentity;
}