Lambda AuthorizerがDenyを返すとき、APIのResponse Headerに「Access-Control-Allow-Origin」を追加する with OpenAPI
ログインして利用するWebアプリとAPIを開発しているとき、アクセストークンの認証失敗時、または、Lambda AuthorizerでDeny
と判断することがあります。
これらのとき、API Response HeaderにAccess-Control-Allow-Origin
を追加してみました。
おすすめの方
- Lambda Authorizerで認証失敗時、API Response Headerに
Access-Control-Allow-Origin
を追加したい方 - AWS SAMとOpenAPIでAPI GatewayとLambdaをデプロイしたい方
Lambda Authorizerをデプロイする
sam init
sam init \ --runtime python3.9 \ --name Lambda-Authorizer-Response-Header-Sample \ --app-template hello-world \ --no-tracing \ --package-type Zip
SAMテンプレート
Access-Control-Allow-Origin
の値は、パラメータで指定しています。
必要に応じて、AWS Systems Managerのパラメータストアを利用するなどしてください。
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Lambda-Authorizer-Response-Header-Sample Parameters: WebAppFqdn: Type: String Default: example.com Resources: MyApi: Type: AWS::Serverless::Api Properties: StageName: dev DefinitionBody: Fn::Transform: Name: AWS::Include Parameters: Location: s3://cm-fujii.genki-deploy/Lambda-Authorizer-Response-Header-Sample-Stack/api.yaml # Lambda Authorizer AuthorizerFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello_world/ Handler: authorizer.lambda_handler Runtime: python3.9 Timeout: 5 AuthorizerFunctionLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub /aws/lambda/${AuthorizerFunction} AuthorizerFunctionPermission: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt AuthorizerFunction.Arn Principal: apigateway.amazonaws.com SourceArn: !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${MyApi}/authorizers/* HelloWorldFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello_world/ Handler: app.lambda_handler Runtime: python3.9 Timeout: 5 Events: HelloWorld: Type: Api Properties: Path: /hello Method: get RestApiId: !Ref MyApi HelloWorldFunctionLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub /aws/lambda/${HelloWorldFunction} Outputs: HelloWorldApi: Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/dev/hello"
### OpenAPI
x-amazon-apigateway-gateway-responses
を使って、API Gatewayのゲートウェイレスポンスをカスタマイズしています。
ゲートウェイレスポンスのタイプは、必要に応じて追加などしてください。
openapi: 3.0.1 info: title: sample api version: 1.0.0 # https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/supported-gateway-response-types.html x-amazon-apigateway-gateway-responses: UNAUTHORIZED: responseParameters: gatewayresponse.header.Access-Control-Allow-Origin: Fn::Sub: "'https://${WebAppFqdn}'" ACCESS_DENIED: responseParameters: gatewayresponse.header.Access-Control-Allow-Origin: Fn::Sub: "'https://${WebAppFqdn}'" paths: /hello: get: tags: - hello responses: 200: $ref: "#/components/responses/200" 403: $ref: "#/components/responses/403" 500: $ref: "#/components/responses/500" security: - MyLambdaAuthorizer: [] x-amazon-apigateway-integration: type: aws_proxy uri: 'Fn::Sub': >- arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HelloWorldFunction.Arn}/invocations httpMethod: POST responses: default: statusCode: 200 passthroughBehavior: when_no_templates contentHandling: CONVERT_TO_TEXT components: securitySchemes: MyLambdaAuthorizer: type: apiKey name: Authorization in: header x-amazon-apigateway-authtype: custom x-amazon-apigateway-authorizer: authorizerUri: 'Fn::Sub': >- arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AuthorizerFunction.Arn}/invocations authorizerResultTtlInSeconds: 0 type: token responses: 200: description: Success 403: description: User or client is not authorized. 500: description: Internal Server Error
Lambda Authorizer
今回は、常にDenyを返します。
def lambda_handler(event, context): return { 'principalId': '*', 'policyDocument': { 'Version': '2012-10-17', 'Statement': [ { 'Action': 'execute-api:Invoke', 'Effect': 'Deny', 'Resource': event['methodArn'] } ] } }
Lambdaコード
import json def lambda_handler(event, context): return { "statusCode": 200, "body": json.dumps({ "message": "hello world", }), }
デプロイ
aws s3 cp \ api.yaml \ s3://cm-fujii.genki-deploy/Lambda-Authorizer-Response-Header-Sample-Stack/api.yaml sam package \ --output-template-file packaged.yaml \ --s3-bucket cm-fujii.genki-deploy sam deploy \ --template-file packaged.yaml \ --stack-name Lambda-Authorizer-Response-Header-Sample-Stack \ --s3-bucket cm-fujii.genki-deploy \ --capabilities CAPABILITY_NAMED_IAM \ --no-fail-on-empty-changeset
動作確認
APIエンドポイントを取得する
aws cloudformation describe-stacks \ --stack-name Lambda-Authorizer-Response-Header-Sample-Stack \ --query 'Stacks[].Outputs'
Authorizationなし
Response HeaderにAccess-Control-Allow-Origin
が含まれています。
curl -D - https://xxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello HTTP/2 401 content-type: application/json content-length: 26 ... access-control-allow-origin: https://example.com x-amzn-errortype: UnauthorizedException ... {"message":"Unauthorized"}
Authorizationあり
こちらも、Response HeaderにAccess-Control-Allow-Origin
が含まれています。
curl -D - https://xxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello \ --header 'authorization: xxx' HTTP/2 403 content-type: application/json content-length: 82 ... access-control-allow-origin: https://example.com x-amzn-errortype: AccessDeniedException ... {"message":"User is not authorized to access this resource with an explicit deny"}
さいごに
API Gatewayのゲートウェイレスポンスを利用して、Response Headerを追加してみました。 API Gatewayの細かい機能を使いこなしたいですね。