API GatewayにLambda(Node.js)でBASIC認証かける

2021.01.20

API Gatewayに対して、Lambda オーソライザーという機能を使ってBASIC認証をかけてみました。個人的ハマりポイントもあったので、備忘録的に書いておきます。

前提

  • Lambda関数のランタイムはすべて「Node.js 12.x」を選択します
  • API GatewayはREST APIタイプを利用します

まずはLambdaでレスポンス作成

Lambda関数を「一から作成」でランタイム「Node.js 12.x」を選択して作成します。

すると下記のようなコードで関数が生成されます。

exports.handler = async (event) => {
    // TODO implement
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

これでいったんデプロイ。

API Gatewayの準備

1.APIの作成

先ほど作ったlambda関数をAPI Gatewayに紐付けます。

まずはAPI Gatewayコンソールに移動して、APIを新規作成します。

タイプは「REST API」を選択。

API Gateway

2.メソッドの作成

左サイドバー[リソース]-[アクション]-[メソッドの作成]から[GET]メソッドを作成し、バックエンドとして先ほど作成したLambda関数を指定します。

API Gateway

3.ステージの作成とデプロイ

API Gatewayではステージ機能を使って環境ごとに分けて管理ができます。

左サイドバー[リソース]-[アクション]-[API のデプロイ]から、ステージ名[dev]でデプロイします。

API Gateway

[URL の呼び出し]としてエンドポイントURLが生成されますので、アクセスすると「"Hello from Lambda!"」の文字が返ってきます。

% curl https://voubot37fi.execute-api.eu-central-1.amazonaws.com/dev
{"statusCode":200,"body":"\"Hello from Lambda!\""}%     

簡単なAPI Gateway - Lambdaアプリは準備ができました。

BASIC認証用Lambda実装

Lambdaオーソライザーを利用してBASIC認証を実装します。

Lambdaコンソールに移動し、新たにLambda関数を作成します。

こちらもランタイムはNode.js 12.xです。

exports.handler = function (event, context, callback) {
  var authorizationHeader = event.headers.authorization

  if (!authorizationHeader) return callback('Unauthorized')

  var encodedCreds = authorizationHeader.split(" ")[1]
  var plainCreds = (new Buffer(encodedCreds, 'base64')).toString().split(':')
  var username = plainCreds[0]
  var password = plainCreds[1]

  if (!(username === 'admin' && password === 'password')) return callback('Unauthorized')

  var authResponse = buildAllowAllPolicy(event, username)

  callback(null, authResponse)
}

function buildAllowAllPolicy (event, principalId) {
  const policy = {
    principalId: principalId,
    policyDocument: {
      Version: '2012-10-17',
      Statement: [
        {
          Action: 'execute-api:Invoke',
          Effect: 'Allow',
          Resource: event.methodArn
        }
      ]
    }
  }
  return policy
}

同様の実装例を他に検索してみると、event.headers.authorizationが大文字['Authorization']になっている記事が多かったのですが、自分の場合それだとブラウザのBASIC認証が通らなかったので、小文字にしています(AWSチュートリアルでも小文字ですし)。

この関数もデプロイします。

API Gatewayでの設定

レスポンスヘッダーの追加

API Gatewayコンソールに移動し、左サイドバー[ゲートウェイのレスポンス]-[権限がありません]を選択し、レスポンスヘッダとして、
・レスポンスヘッダー: WWW-Authenticate
・値: 'Basic'
を追加します(値はシングルクォーテーション込みなのを忘れずに)。

API Gateway

オーソライザーの作成

次に、左サイドバー[オーソライザー]-[新しいオーソライザーの作成]でオーソライザーを作成します。

先ほど作成したBASIC認証用のLambda関数をセットし、[Lambda イベントペイロード]は[リクエスト]、[IDソース]としてヘッダーに[Authorization]を記述します。[認可のキャッシュ]もチェックを外します。

API Gateway

先述のAuthorization大文字小文字問題と異なり、こちらは大文字'Authorization'でもBASIC認証が通るのですが、Lambdaのほうを小文字にしている関係で、こちらも小文字にしないと次に行うAPI Gateway側のテストが通りません。

作成後、[テスト]からAPI Gatewayのテストが行えるのでやってみます。

先ほどのBASIC認証用Lambda関数でセットしたユーザー名とパスワードを繋げた状態でBase64エンコードして、リクエストパラメータにセットします。

api gateway test

レスポンス200が返ってきているので成功しました。

メソッドリクエストにオーソライザー追加

左サイドバー[リソース]-[GET]-[メソッドリクエスト]を編集し、[認可]に先ほど作成したオーソライザーを追加します。

これで準備ができたので、APIをデプロイし、再度URLにアクセスしてみます。

無事BASIC認証のポップアップが現れました。

参考URL

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html

https://dev.classmethod.jp/articles/http-api-support-iam-and-lambda-authorizer/

https://dev.classmethod.jp/articles/getting-start-api-gateway/

https://qiita.com/diaphragm/items/b87d700ef36fa62c1aa8

https://medium.com/@Da_vidgf/http-basic-auth-with-api-gateway-and-serverless-5ae14ad0a270