Skip to main content

CloudFront で Lambda@Edge を使って OpenID Connect (OIDC) 認証: Widen/cloudfront-auth

概要

CloudFront と S3 でスタティックなウェブサイトを構築し、外部の認証プロバイダ (IdP) を使って認証をおこなう方法です。

OpenID Connect (OIDC) による認証をおこなう Lambda 関数は Widen/cloudfront-auth というツールを使って生成します。Google, Microsoft, GitHub など様々な認証プロバイダに対応していますが、ここでは Google を IdP とし、特定ドメインのメールアドレスのユーザーのみをアクセス許可します。 ※このツールは 2022/08 にメンテナンスが終了し、アーカイブされました。Node.js のランタイムにバージョン 10.x を使用しており Lambda のランタイムとしては既にサポート終了済みであるためそのままではデプロイできません。ランタイム設定を新しいバージョンに変更するだけで動作しました。

OpenID Connect による認証シーケンス

CloudFront にアクセスした際のシーケンスは以下のようになります。

構築手順

前提条件

  • CloudFront と S3 を連携したウェブサイトが構築済み
  • AWS SAM CLI がインストール済み

1. 認証プロバイダ (IdP) の設定

IdP のクライアントを認証するための Client ID/Secret を払い出します。CloudFront で動作する Lambda 関数がクライアントという位置付けです。

Google の場合は以下の手順でおこないます。

  1. Google API Console にアクセス
  2. プロジェクトを作成
  3. 「Credential」からクライアントを作成
    1. 「Create credentials」→「OAuth client ID」
    2. 「Application type」に「Web application」を選択
    3. 「Name」に適当な名前を入力 (例: cloudfront-auth)
    4. 「Authorized redirect URIs」に「Callback URL」を入力 (例: https://xxxxxxxxxxxxxx.cloudfront.net/_callback)
    5. 「Client ID」と「Client secret」が発行されるので、メモしておく

Callback URL には IdP からの認証結果をリダイレクトしする URL を指定します。CloudFront が受信できて実際にサイト内に存在するファイルとは重複しないものを適当に指定します。

Google API Credentials 一覧画面

2. 認証 Lambda 関数 (Widen/cloudfront-auth) のデプロイ

チェックアウト

Widen/cloudfront-auth をチェックアウトします。

$ git clone https://github.com/Widen/cloudfront-auth.git
$ cd cloudfront-auth

Lamda 関数の生成

設定スクリプトを実行して必要事項を入力すると、それ用の Lambda 関数が生成されます。

  1. 設定スクリプトを実行

    $ ./build.sh
  2. Lambda のディストリビューション名を指定する。(任意の名前)

    >: Enter distribution name:  cloudfront-oidc-auth-xxx (例)
  3. 使用する認証プロバイダ(IdP)を選択

    >: Authentication methods:
    (1) Google
    (2) Microsoft
    (3) GitHub
    (4) OKTA
    (5) Auth0
    (6) Centrify
    (7) OKTA Native

    Select an authentication method: 1
  4. クライアント ID/Secret を入力

    >>: Client ID:  xxxxxxxx
    >>: Client Secret: xxxxxxxx
  5. Callback URL を入力

    >>: Redirect URI:  https://xxxxxxxxxxxxxx.cloudfront.net/_callback
  6. ログインユーザーのドメイン名を入力。 これに後方一致するメールアドレスのユーザーのみアクセスが許可されます。

    >>: Hosted Domain:  gmail.com
  7. 認証の有効期限を入力 デフォルトの 0 だとすぐに期限切れとなって全くアクセスできないので注意。(やらかした)

    >>: Session Duration (hours):  (0) 24
  8. 認可の設定

    >>: Authorization methods:
    (1) Hosted Domain - verify email's domain matches that of the given hosted domain
    (2) HTTP Email Lookup - verify email exists in JSON array located at given HTTP endpoint
    (3) Google Groups Lookup - verify email exists in one of given Google Groups

    Select an authorization method: 1

    アクセス可能なユーザーを設定する方法として、以下の 3 つから選択することができます。(Google の場合)

    1. ホスティングドメインに指定したドメインのメールアドレスのユーザーのみアクセスを許可 (今回はこれを選択)
    2. 特定の URL からダウンロードした JSON に書かれたメールアドレスのユーザーのみアクセスを許可
    3. 特定の Google グループのユーザーのみアクセスを許可
  9. 完了 生成された関数は、distributions/{distribution-name}/{distribution-name}.zip に出力されます。

    Done... created Lambda function distributions/cloudfront-oidc-auth-xxx/cloudfront-oidc-auth-xxx.zip

Lambda 関数のデプロイ

template.yaml を修正して、生成された Lambda 関数をデプロイします。

  1. template.yaml の {distribution_name} をディストリビューション名に書き換え

    $ DISTRIBUTION_NAME=cloudfront-oidc-auth-xxx (例)
    $ sed -i.bak "s/{distribution_name}/${DISTRIBUTION_NAME}/g" template.yaml
  2. template.yaml のランタイム設定を Node.js 16 に書き換え

    $ sed -i.bak "s/nodejs10\.x/nodejs16\.x/" template.yaml

デプロイは 手動 または AWS SAM のいずれかでできます。SAM を使った場合は、以下を実行します。Lambda@Edge 関数は us-east-1 リージョンにデプロイする必要があります。

$ sam deploy --guided

Configuring SAM deploy
======================

Looking for config file [samconfig.toml] : Not found

Setting default arguments for 'sam deploy'
=========================================
Stack Name [sam-app]: cloudfront-oidc-auth-xxx
AWS Region [us-east-1]:

...

これにより設定ファイル (samconfig.toml) が作成されます。設定を変更する場合はファイルを編集して sam deploy を実行することでスタックを更新できます。

3. CloudFront ディストリビューションに Lambda@Edge を設定

  1. ホスティングしている CloudFront ディストリビューションの設定を開く

  2. Default Behaviors を Edit

  3. 「Lambda Function Associations」に以下を追加

    CloudFront EventLambda Function ARNInclude Body
    Viewer Requestarn:aws:lambda:us-east-1:(アカウント番号):function:(関数名):1チェックしない

「Lambda Function ARN」にはバージョン付きの ARN を指定します。Lambda 関数を更新してデプロイし直した場合、バージョンが上がっていくので、その都度ここも変更する必要があります。

CloudFront の設定反映は、しばらく時間がかかるので気長に待ちましょう。

Lambda@Edge 設定画面

FAQ (やらかしメモ)

認証完了後に無限ループする

認証完了後に元のページにリダイレクトされるが、すぐに再度認証ページにリダイレクトされ、リクエストが無限ループしてしまう。実際にはブラウザが検知して数回繰り返したあとエラー画面になる。有効期限を 0 にしてしまうと、認証完了直後に期限切れとなるため、こうなってしまいます。

401 でページが参照できない

認証完了後、画面に「User (メールアドレス) is not permitted.」というエラーメッセージが表示される場合、認証はうまくいっているが認可でエラーになっている。ログインしたメールアドレスに対してアクセスが許可されているかを確認しましょう。