diff --git a/examples/api-gateway-authorizer/runtime/user.fn.ts b/examples/api-gateway-authorizer/runtime/user.fn.ts index df0074c..af61ed6 100644 --- a/examples/api-gateway-authorizer/runtime/user.fn.ts +++ b/examples/api-gateway-authorizer/runtime/user.fn.ts @@ -1,13 +1,17 @@ import type { LambdaConfig } from "@notation/aws/lambda.fn"; import { handle, json } from "@notation/aws/lambda.fn"; -import { EventWithJWTToken } from "@notation/aws/shared"; +import { + JWTAuthorizedApiGatewayHandler, + EventWithJWTToken, +} from "@notation/aws/shared"; import { Context } from "aws-lambda"; -export const getUserHandler = handle.jwtAuthorizedApiRequest( - (event: EventWithJWTToken, context: Context) => { - return json({ userId: "" }); - }, -); +export const getUserHandler: JWTAuthorizedApiGatewayHandler = + handle.jwtAuthorizedApiRequest( + (event: EventWithJWTToken, context: Context) => { + return json({ userId: "" }); + }, + ); export const config: LambdaConfig = { service: "aws/lambda", diff --git a/packages/aws/src/api-gateway/route.ts b/packages/aws/src/api-gateway/route.ts index 5ebc172..ad9c175 100644 --- a/packages/aws/src/api-gateway/route.ts +++ b/packages/aws/src/api-gateway/route.ts @@ -6,20 +6,16 @@ import * as aws from "@notation/aws.iac"; import { lambda } from "src/lambda"; import { api } from "./api"; import { AuthorizerConfig, JWTAuthorizerConfig, NO_AUTH } from "./auth"; -import { mapAuthConfig, mapAuthType, toApiGatewayHandler } from "./utils"; +import { mapAuthConfig, mapAuthType } from "./utils"; export const jwtAuthorizedRoute = ( apiGroup: ReturnType, method: string, // todo: http methods only path: `/${string}`, auth: JWTAuthorizerConfig, - handler: JWTAuthorizedApiGatewayHandler, + handler: ApiGatewayHandler, ) => { - // We require a 'JWTAuthorizedApiGatewayHandler' to be passed in for type safety (since only a jwtAuthorizedRoute notation resource - // will have the JWT header.) However, the AWS lambda resource itself must have the API gateway handler type for compatibility. - const apiGatewayHandler = toApiGatewayHandler(handler); - - return routeResource(apiGroup, method, path, auth, apiGatewayHandler); + return routeResource(apiGroup, method, path, auth, handler); }; export const route = ( @@ -38,7 +34,6 @@ const routeResource = ( auth: AuthorizerConfig, handler: ApiGatewayHandler, ) => { - const apiResource = apiGroup.findResource(aws.apiGateway.Api)!; // at compile time becomes infra module diff --git a/packages/aws/src/api-gateway/router.ts b/packages/aws/src/api-gateway/router.ts index 7713797..4584295 100644 --- a/packages/aws/src/api-gateway/router.ts +++ b/packages/aws/src/api-gateway/router.ts @@ -25,8 +25,7 @@ export const jwtAuthenticatedUserRouter = ( jwtAuthorizerConfig: JWTAuthorizerConfig, ) => { const createRouteCallback = - (method: string) => - (path: `/${string}`, handler: JWTAuthorizedApiGatewayHandler) => { + (method: string) => (path: `/${string}`, handler: ApiGatewayHandler) => { return jwtAuthorizedRoute( apiGroup, method, diff --git a/packages/aws/src/api-gateway/utils.ts b/packages/aws/src/api-gateway/utils.ts index fb429a1..bea4974 100644 --- a/packages/aws/src/api-gateway/utils.ts +++ b/packages/aws/src/api-gateway/utils.ts @@ -1,10 +1,4 @@ -import { decomposeUnverifiedJwt } from "aws-jwt-verify/jwt"; import { AuthorizerConfig, JWTAuthorizerConfig } from "./auth"; -import { APIGatewayProxyEventV2, Context } from "aws-lambda"; -import type { - ApiGatewayHandler, - JWTAuthorizedApiGatewayHandler, -} from "src/shared"; export const mapAuthConfig = (config: JWTAuthorizerConfig) => { const jwtType: "JWT" = "JWT"; @@ -23,24 +17,3 @@ export const mapAuthConfig = (config: JWTAuthorizerConfig) => { export const mapAuthType = (config: AuthorizerConfig) => { return config?.type === "jwt" ? "JWT" : "NONE"; }; - -export const toApiGatewayHandler = ( - handler: JWTAuthorizedApiGatewayHandler, -): ApiGatewayHandler => { - const apiGatewayHandler: ApiGatewayHandler = (event: APIGatewayProxyEventV2, context: Context) => { - const authorizationHeader = event.headers.Authorization!; - - // Remove the 'Bearer ' prefix that preceeds the token itself - const jwtToken = authorizationHeader?.slice(7); - const jwt = decomposeUnverifiedJwt(jwtToken); - - const eventWithJwt = { - token: jwt.payload, - event: event, - }; - - return handler(eventWithJwt, context); - }; - - return apiGatewayHandler -}; diff --git a/packages/aws/src/lambda.fn/handlers.ts b/packages/aws/src/lambda.fn/handlers.ts index 673be47..009c24a 100644 --- a/packages/aws/src/lambda.fn/handlers.ts +++ b/packages/aws/src/lambda.fn/handlers.ts @@ -1,3 +1,5 @@ +import { decomposeUnverifiedJwt } from "aws-jwt-verify/jwt"; +import { APIGatewayProxyEventV2, Context } from "aws-lambda"; import type { ApiGatewayHandler, DynamoDbBatchHandler, @@ -14,10 +16,9 @@ export const handle = { async (...args) => handler(...args), - jwtAuthorizedApiRequest: - (handler: JWTAuthorizedApiGatewayHandler): JWTAuthorizedApiGatewayHandler => - async (...args) => - handler(...args), + jwtAuthorizedApiRequest: ( + handler: JWTAuthorizedApiGatewayHandler, + ): ApiGatewayHandler => toApiGatewayHandler(handler), eventBridgeScheduledEvent: ( @@ -42,3 +43,27 @@ export const handle = { async (...args) => handler(...args), }; + +const toApiGatewayHandler = ( + handler: JWTAuthorizedApiGatewayHandler, +): ApiGatewayHandler => { + const apiGatewayHandler: ApiGatewayHandler = ( + event: APIGatewayProxyEventV2, + context: Context, + ) => { + const authorizationHeader = event.headers.Authorization!; + + // Remove the 'Bearer ' prefix that preceeds the token itself + const jwtToken = authorizationHeader?.slice(7); + const jwt = decomposeUnverifiedJwt(jwtToken); + + const eventWithJwt = { + token: jwt.payload, + event: event, + }; + + return handler(eventWithJwt, context); + }; + + return apiGatewayHandler; +}; diff --git a/packages/aws/test/lambda.fn.test.ts b/packages/aws/test/lambda.fn.test.ts index 101727d..30bdf3a 100644 --- a/packages/aws/test/lambda.fn.test.ts +++ b/packages/aws/test/lambda.fn.test.ts @@ -9,7 +9,15 @@ beforeEach(() => { test("handlers wrap user-provided handlers", async () => { const fn = async () => ({ body: "{}" }); for (const handler of Object.values(handle)) { - const result = await handler(fn)({} as any, {} as any); + const result = await handler(fn)( + { + headers: { + Authorization: + "Bearer eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJuYW1lIjogIkpvaG4gRG9lIiwgImlhdCI6IDE1MTYyMzkwMjJ9.vBbO0bfWhxupD6Gp6gIyWzgSZDvQewYV23j9LKm7nV8", + }, + } as any, + {} as any, + ); expect(result).toEqual({ body: "{}" }); } });