前回作成したサーバーレスLaravelですが、BASIC認証を付与しようとしたら躓きました。
API Gatewayを経由すると、WWW-Authenticate
ヘッダーがx-amazn-remapped-www-authenticateに置き換えらます。その結果BASIC認証を求めるポップを表示できず無条件で401 Unauthorized
エラーだけを表示するWEBになります。
つまり問題はLambdaではなく、API Gatewayです。ALBでURLを設定すればこの問題はありません。ALB経由のLambdaを使ったサーバーレスLaravelのBASIC認証は通常のLaravelと同じです。53eda06
API Gateway経由でもBASIC認証を導入するには以下のように変更します。
- 別途新規のLambda関数を作成し、API Gateway上にオーソライザーとして登録する。
- オーソライザーのレスポンスに
WWW-Authenticate
を付与する
オーソライザー用LambdaはメインのLambdaと同じ言語である必要は無いという意味で言語不問です。この記事では新規にプロジェクトを作成し、nodejsのオーソライザーを付与します。上記Laravelはオーソライザーも素のPHPのLambda関数で実装したかったですが、うまくいかずnodejsで実装しています。cbb1ad6
しかしながら、この方法だと- http: 'ANY /{proxy+}'
などダイナミックなルーティングをした場合に
An error occurred: ApiGatewayResourceUuidVarhtml - Resource's path part only allow a-zA-Z0-9._- and curly braces at the beginning and the end.
というエラーが発生します。つまり静的なパスにしか設定できません。AWSフォーラムを見たところ、既知の問題のようです。実務アプリでは例えば401ではなく/loginにリダイレクトさせ、ブラウザーにBASIC認証用のヘッダーを持たせる責務を1つの静的パスに集約させるなどの対策が必要です。それかAPI Gatewayからロードバランサーに切り替えるなど。
$ # https://github.com/umihico/authorizer-demo/commit/c0d1263
$ mkdir authorizer-demo
$ cd authorizer-demo
$ serverless create --template aws-nodejs --name authorizer-demo #プロジェクト作成
API Gatewayを設定して、まずオーソライザー無し動作確認します。6b95a36
functions:
hello:
handler: handler.hello
+ events:
+ - http:
+ path: hello
+ method: get
# The following are a few example events you can configure
# NOTE: Please make sure to change your handler code to work with those events
# Check the event documentation for details
$ serverless deploy
$ curl https://7xcxsu7mp5.execute-api.us-east-1.amazonaws.com/dev/hello
{message: "Go Serverless v1.0! Your function executed successfully!",input: {省略}}
BASIC認証の前にトークンを使った認証を作ってテストします。dcb8407
'use strict';
+ module.exports.auth = (event, context, callback) => {
+ var token = event.headers["Authorization"];
+ if (token == 'password') {
+ callback(null, {
+ principalId: 'user',
+ policyDocument: {
+ Version: '2012-10-17',
+ Statement: [{
+ Action: 'execute-api:Invoke',
+ Effect: 'Allow',
+ Resource: "*",
+ }]
+ }
+ });
+ } else {
+ callback('Unauthorized');
+ }
+ };
module.exports.hello = async event => {
return {
statusCode: 200,
- http:
path: hello
method: get
+ authorizer:
+ name: auth
+ resultTtlInSeconds: 0
+ type: request
+ auth:
+ handler: handler.auth
# The following are a few example events you can configure
# NOTE: Please make sure to change your handler code to work with those events
# Check the event documentation for details
$ curl https://7xcxsu7mp5.execute-api.us-east-1.amazonaws.com/dev/hello
{message: "Unauthorized"}
$ curl --header "Authorization: password" https://7xcxsu7mp5.execute-api.us-east-1.amazonaws.com/dev/hello
{message: "Go Serverless v1.0! Your function executed successfully!",input: {省略}}
$ curl --header "Authorization: dummy" https://7xcxsu7mp5.execute-api.us-east-1.amazonaws.com/dev/hello
{message: "Unauthorized"}
正しいパスワードがセットされているときのみ動作してくれるようになりました。
WEBアクセスしたらBASIC認証のポップを出してくれるようにリソースを設定します。e167c22
'use strict';
module.exports.auth = (event, context, callback) => {
- var token = event.headers["Authorization"];
- if (token == 'password') {
+ let Authorization = event.headers.Authorization;
+ if (!Authorization) return callback('Unauthorized');
+ let [username, password] = (new Buffer(Authorization.split(' ')[1], 'base64')).toString().split(':');
+ if (username === 'admin' && password === 'secret4') {
callback(null, {
principalId: 'user',
policyDocument: {
# environment:
# variable2: value2
+ resources:
+ Resources:
+ GatewayResponse:
+ Type: 'AWS::ApiGateway::GatewayResponse'
+ Properties:
+ ResponseParameters:
+ gatewayresponse.header.WWW-Authenticate: "'Basic'"
+ ResponseType: UNAUTHORIZED
+ RestApiId:
+ Ref: 'ApiGatewayRestApi'
+ StatusCode: '401'
# you can add CloudFormation resource templates here
#resources:
# Resources:
デプロイしてURLにアクセスするとBASIC認証を求めるポップを表示してくれるようになりました。
参考
- https://medium.com/@Da_vidgf/http-basic-auth-with-api-gateway-and-serverless-5ae14ad0a270
- https://forum.serverless.com/t/http-basic-auth-with-lambda-api-gateway/4305
- https://github.com/serverless-projects/serverless-authorizers/
- https://forums.aws.amazon.com/thread.jspa?threadID=233746
- https://dev.classmethod.jp/articles/lambda-authorizer/