From 63e45cabfc76491a50f410288cfdab040afffe55 Mon Sep 17 00:00:00 2001 From: pearl-truss <67110378+pearl-truss@users.noreply.github.com> Date: Tue, 2 Jan 2024 13:54:41 -0500 Subject: [PATCH] MCR-3834 : API Auth: Create Lambda Authorizer to validate incoming JWTs (#2146) * Add createAPIToken endpoint and jwtLib service * initial authorizer * create lambda authorizer * revert changes to apollo_gql.ts * create new health check endpoint with the authorizer attached * pr fixes: distinguish the health check path used for the authorizer, update authorizer to return deny policies on error * explictly make userID string or undefined, improve err msg language --------- Co-authored-by: MacRae Linton --- services/app-api/serverless.yml | 14 ++++ .../handlers/third_party_API_authorizer.ts | 71 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 services/app-api/src/handlers/third_party_API_authorizer.ts diff --git a/services/app-api/serverless.yml b/services/app-api/serverless.yml index a020028d12..2a55857481 100644 --- a/services/app-api/serverless.yml +++ b/services/app-api/serverless.yml @@ -160,6 +160,20 @@ functions: method: get cors: true + third_party_api_authorizer: + handler: src/handlers/third_party_API_authorizer.main + + jwthealth: + handler: src/handlers/health_check.main + events: + - http: + path: jwt_health_check + method: get + cors: true + authorizer: + name: third_party_api_authorizer + identitySource: method.request.header.Authorization + otel: handler: src/handlers/otel_proxy.main events: diff --git a/services/app-api/src/handlers/third_party_API_authorizer.ts b/services/app-api/src/handlers/third_party_API_authorizer.ts new file mode 100644 index 0000000000..a3741ba476 --- /dev/null +++ b/services/app-api/src/handlers/third_party_API_authorizer.ts @@ -0,0 +1,71 @@ +import type { + APIGatewayAuthorizerResult, + APIGatewayTokenAuthorizerEvent, + PolicyDocument, + APIGatewayTokenAuthorizerHandler, +} from 'aws-lambda' +import { newJWTLib } from '../jwt' + +// Hard coding this for now, next job is to run this config to this app. +const jwtLib = newJWTLib({ + issuer: 'fakeIssuer', + signingKey: 'notrandom', + expirationDurationS: 90 * 24 * 60 * 60, // 90 days +}) + +export const main: APIGatewayTokenAuthorizerHandler = async ( + event +): Promise => { + const authToken = event.authorizationToken.replace('Bearer ', '') + try { + // authentication step for validating JWT token + const userId = await jwtLib.userIDFromToken(authToken) + + if (userId instanceof Error) { + const msg = 'Invalid auth token' + console.error(msg) + + return generatePolicy(undefined, event) + } + + console.info({ + message: 'third_party_API_authorizer succeeded', + operation: 'third_party_API_authorizer', + status: 'SUCCESS', + }) + + return generatePolicy(userId, event) + } catch (err) { + console.error( + 'unexpected exception attempting to validate authorization', + err + ) + return generatePolicy(undefined, event) + } +} + +const generatePolicy = function ( + userId: string | undefined, + event: APIGatewayTokenAuthorizerEvent +): APIGatewayAuthorizerResult { + // If the JWT is verified as valid, send an Allow policy + // this will allow the request to go through + // otherwise a Deny policy is returned which restricts access + const policyDocument: PolicyDocument = { + Version: '2012-10-17', // current version of the policy language + Statement: [ + { + Action: 'execute-api:Invoke', + Effect: userId ? 'Allow' : 'Deny', + Resource: event['methodArn'], + }, + ], + } + + const response: APIGatewayAuthorizerResult = { + principalId: userId || '', + policyDocument, + } + + return response +}