ログインして利用するWebアプリとAPIを開発しているとき、アクセストークンの認証失敗時、または、Lambda AuthorizerでDeny
と判断することがあります。
このとき、デフォルトでは、ステータスコード403が返ります。
今回は、Lambda AuthorizerでDeny
と判定したとき、APIのステータスコードを401にしてみます。
おすすめの方
- Lambda Authorizerで認証失敗時、403以外のステータスコードを返したい方
- AWS SAMとOpenAPIでAPI GatewayとLambdaをデプロイしたい方
Lambda Authorizerをデプロイする
sam init
sam init \
--runtime python3.9 \
--name Lambda-Authorizer-Status-Code-Sample \
--app-template hello-world \
--no-tracing \
--package-type Zip
SAMテンプレート
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Lambda-Authorizer-Status-Code-Sample
Resources:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: dev
DefinitionBody:
Fn::Transform:
Name: AWS::Include
Parameters:
Location: s3://cm-fujii.genki-deploy/Lambda-Authorizer-Status-Code-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
api.yaml
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:
ACCESS_DENIED:
statusCode: 401
paths:
/hello:
get:
tags:
- hello
responses:
200:
$ref: "#/components/responses/200"
401:
$ref: "#/components/responses/401"
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
401:
description: Unauthorized
403:
description: User or client is not authorized.
500:
description: Internal Server Error
Lambda Authorizer
今回は、常にDenyを返します。
authorizer.py
def lambda_handler(event, context):
return {
'principalId': '*',
'policyDocument': {
'Version': '2012-10-17',
'Statement': [
{
'Action': 'execute-api:Invoke',
'Effect': 'Deny',
'Resource': event['methodArn']
}
]
}
}
Lambdaコード
app.py
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-Status-Code-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-Status-Code-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-Status-Code-Sample-Stack \
--query 'Stacks[].Outputs'
Authorizationなし
401が返ってきました。
$ curl -D - https://aaa.execute-api.ap-northeast-1.amazonaws.com/dev/hello
HTTP/2 401
...
{"message":"Unauthorized"}
Authorizationあり
何もしなければ403が返ってきますが、401が返ってきました。期待通りです。
curl -D - https://aaa.execute-api.ap-northeast-1.amazonaws.com/dev/hello \
--header 'authorization: xxx'
HTTP/2 401
...
{"message":"User is not authorized to access this resource with an explicit deny"}
さいごに
API Gatewayのゲートウェイレスポンスを利用して、ステータスコードを401に変更してみました。 API Gatewayのゲートウェイレスポンスは、ほかにもいろいろな種類や変更できる箇所があるので、必要に応じて活用したいです。