0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CloudFront + Lambda\@Edge + Azure Entra 認証で S3 の静的コンテンツを保護する

Last updated at Posted at 2025-02-18

CloudFront の前段で Lambda@Edge を利用し、Azure Entra (旧 Azure AD) の認証を通過したユーザーのみ S3 の静的コンテンツを閲覧できるようにする方法を紹介します。

前提条件

  • CloudFront のディストリビューションが設定済み
  • Azure Entra のサービス登録が完了し、以下の情報を取得済み
    • クライアント ID
    • テナント ID
    • クライアント シークレット
  • Lambda@Edge を CloudFront の ビューワリクエスト (Viewer Request) に設定予定

Node.js (Lambda@Edge) のコード作成

1. プロジェクトセットアップ

mkdir lambda-auth && cd lambda-auth
npm init -y
npm install @azure/msal-node querystring

2. index.js を作成

const { PublicClientApplication, CryptoProvider } = require("@azure/msal-node");
const querystring = require("querystring");

// Azure Entraの設定
const config = {
  auth: {
    clientId: "YOUR_CLIENT_ID", // Azure EntraのクライアントID
    authority: "https://login.microsoftonline.com/YOUR_TENANT_ID", // Azure EntraのテナントID
    clientSecret: "YOUR_CLIENT_SECRET", // Azure Entraのクライアントシークレット
  },
  system: {
    loggerOptions: {
      loggerCallback(loglevel, message, containsPii) {
        console.log(message);
      },
      piiLoggingEnabled: false,
      logLevel: "Info",
    },
  },
};

const pca = new PublicClientApplication(config);
const cryptoProvider = new CryptoProvider();

// 認証URLを生成する関数
function getAuthUrl(state, nonce) {
  return pca.getAuthCodeUrl({
    scopes: ["user.read"], // 必要なスコープを指定
    redirectUri: "https://your-cloudfront-domain/callback", // リダイレクトURI
    state: state,
    nonce: nonce,
  });
}

// 認証コードを交換してトークンを取得する関数
async function getTokenFromCode(authCode, state, nonce) {
  const tokenResponse = await pca.acquireTokenByCode({
    code: authCode,
    scopes: ["user.read"],
    redirectUri: "https://your-cloudfront-domain/callback",
    state: state,
    nonce: nonce,
  });
  return tokenResponse;
}

exports.handler = async (event) => {
  const request = event.Records[0].cf.request;
  const headers = request.headers;

  // 認証コードがあるかどうかを確認
  const queryString = request.querystring;
  const queryParams = querystring.parse(queryString);

  if (queryParams.code) {
    // 認証コードがある場合、トークンを取得
    try {
      const tokenResponse = await getTokenFromCode(
        queryParams.code,
        queryParams.state,
        queryParams.nonce
      );
      if (tokenResponse) {
        // 認証成功、リクエストを継続
        return request;
      }
    } catch (error) {
      console.error("Token acquisition failed:", error);
      return {
        status: "401",
        statusDescription: "Unauthorized",
        body: "Authentication failed",
      };
    }
  } else {
    // 認証コードがない場合、認証URLを生成してリダイレクト
    const state = cryptoProvider.createNewGuid();
    const nonce = cryptoProvider.createNewGuid();
    const authUrl = getAuthUrl(state, nonce);

    return {
      status: "302",
      statusDescription: "Found",
      headers: {
        location: [
          {
            key: "Location",
            value: authUrl,
          },
        ],
      },
    };
  }
};

ビルド手順と ZIP 化

zip -r lambda-auth.zip index.js node_modules package.json package-lock.json

Windows の場合:

Compress-Archive -Path index.js, node_modules, package.json, package-lock.json -DestinationPath lambda-auth.zip

AWS Lambda@Edge にデプロイ(AWS コンソール使用)

  1. AWS マネジメントコンソール にログイン
  2. Lambda サービス を開く
  3. 「関数の作成」 をクリック
    • 関数名: AuthLambda
    • ランタイム: Node.js 18.x
    • 実行ロール: 既存の Lambda 実行ロールを選択、または新規作成
  4. 作成した関数の詳細ページを開き、 「コード」タブ に移動
  5. 「アップロード」ボタンをクリックし、lambda-authzip をアップロード
  6. 「デプロイ」ボタンをクリックして反映

Lambda@Edge の設定

  1. Lambda 関数の詳細ページで 「アクション」→「バージョンの発行」 をクリック
  2. バージョン番号をメモする
  3. AWS マネジメントコンソールの CloudFront に移動
  4. CloudFront のディストリビューションを開く
  5. 「ビヘイビア」タブ で該当のビヘイビアを選択し、「編集」をクリック
  6. 「Lambda@Edge 関数の関連付け」セクションで以下を設定
    • イベントタイプ: Viewer Request
    • Lambda 関数 ARN: arn:aws:lambda:us-east-1:<YOUR_AWS_ACCOUNT_ID>:function:AuthLambda:<VERSION_NUMBER>
  7. 「保存」ボタンをクリックして適用

この Lambda 関数を使用した認証の処理の流れ

Azure Entra (旧 Azure AD) の認証フローを利用して、ユーザーが認証され、認証が成功した場合にのみ S3 の静的コンテンツを表示します。

処理の流れ

  1. ユーザーが CloudFront 経由で S3 のコンテンツにアクセスする

    • ユーザーが CloudFront の URL(例: https://your-cloudfront-domain/index.html)にアクセスします。
    • CloudFront はリクエストを Lambda 関数(オリジンリクエストトリガー)に渡します。
  2. Lambda 関数がリクエストを処理

    • Lambda 関数は、リクエストに認証コード (code) が含まれているかどうかを確認します。
    • 認証コードがない場合、Lambda 関数はユーザーを Azure Entra の認証画面にリダイレクトします。
  3. Azure Entra の認証画面にリダイレクト

    • Lambda 関数は、Azure Entra の認証 URL を生成し、ユーザーをその URL にリダイレクトします。
    • 認証 URL には、statenonceなどのセキュリティパラメータが含まれています。
    • ユーザーは Azure Entra の認証画面でログインを行います。
  4. Azure Entra が認証コードを発行

    • ユーザーが正しく認証されると、Azure Entra は認証コード (code) を生成し、指定されたリダイレクト URI(例: https://your-cloudfront-domain/callback)にリダイレクトします。
    • このリダイレクト URI は CloudFront のドメインに設定されている必要があります。
  5. CloudFront が再度 Lambda 関数を呼び出す

    • リダイレクト URI にアクセスすると、CloudFront は再度 Lambda 関数を呼び出します。
    • この時、リクエストのクエリパラメータに認証コード (code) が含まれています。
  6. Lambda 関数が認証コードをトークンに交換

    • Lambda 関数は、認証コード (code) を使用して Azure Entra からアクセストークンを取得します。
    • トークンの取得に成功した場合、認証は成功とみなされます。
  7. 認証成功時の処理

    • 認証が成功した場合、Lambda 関数はリクエストをそのまま返します。
    • CloudFront はリクエストを S3 オリジンに転送し、S3 の静的コンテンツをユーザーに返します。
  8. 認証失敗時の処理

    • 認証コードの交換に失敗した場合や、トークンの取得に失敗した場合、Lambda 関数はエラーレスポンス(例: 401 Unauthorized)を返します。
    • ユーザーにはエラーメッセージが表示されます。

フローの詳細

  1. 初回アクセス時のリダイレクト

    • ユーザーが初めてアクセスした場合、認証コードがないため、Lambda 関数は Azure Entra の認証 URL を生成し、ユーザーをリダイレクトします。
    • 例:
      const authUrl = getAuthUrl(state, nonce);
      return {
        status: "302",
        statusDescription: "Found",
        headers: {
          location: [
            {
              key: "Location",
              value: authUrl,
            },
          ],
        },
      };
      
  2. 認証コードの受け取りとトークン交換

    • ユーザーが Azure Entra で認証されると、認証コードがリダイレクト URI に付与されて CloudFront に戻ります。
    • Lambda 関数はこの認証コードを使用してトークンを取得します。
    • 例:
      const tokenResponse = await getTokenFromCode(
        queryParams.code,
        queryParams.state,
        queryParams.nonce
      );
      if (tokenResponse) {
        return request; // 認証成功、リクエストを継続
      }
      
  3. リクエストの継続

    • 認証が成功した場合、Lambda 関数はリクエストをそのまま返します。
    • CloudFront はリクエストを S3 オリジンに転送し、S3 の静的コンテンツをユーザーに返します。
  4. エラーハンドリング

    • 認証が失敗した場合、Lambda 関数はエラーレスポンスを返します。
    • 例:
      return {
        status: "401",
        statusDescription: "Unauthorized",
        body: "Authentication failed",
      };
      

重要なポイント

  • リダイレクト URI: Azure Entra のアプリケーション登録時に、リダイレクト URI を CloudFront のドメインに設定する必要があります。
  • セキュリティパラメータ: statenonceを使用して、リクエストの正当性を確認します。
  • スコープ: 必要なスコープ(例: user.read)を指定して、ユーザーの認証を行います。

このフローにより、ユーザーは Azure Entra で認証された後にのみ S3 の静的コンテンツにアクセスできるようになります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?