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?

【AWS初心者向け】Lambda Authorizerで学ぶAPI認証の基礎とハンズオン実装

Last updated at Posted at 2026-01-14

こんな経験ありませんか?

API開発を進めていると、こんな壁にぶつかることがあります。

  • 「API Keyだけでは不安。もっと柔軟な認証がしたい...」
  • 「Cognitoだけで完結できればいいけど、カスタムロジックも入れたい」
  • 「JWTトークンは使いたいけど、検証処理をどこに書けばいいの?」

こうした課題を解決するのが、Lambda Authorizerです!

Lambda Authorizerを使うと「こんなに柔軟に認証処理を実装することができます!

この記事では、Lambda Authorizerの基礎から実装まで手を動かしながら学べる内容になっています!

Lambda Authorizerとは

Lambda AuthorizerはAPI Gatewayのカスタム認証機能です。

Lambdaで認証ロジックを実装することで、外部のIdP(Identity Provider)や独自の認証基盤と連携することができます。

Lambda Authorizerの特徴は以下の通りです:

  • 柔軟性: 任意の認証ロジックを実装可能
  • 統合性: Cognito、Auth0、Oktaなど様々なIdPと連携
  • 効率性: 認証結果をキャッシュしてパフォーマンス向上
  • コスト: Lambda実行分のみ課金(キャッシュ活用で削減可能)

他の認証方式との比較

認証方式 柔軟性 実装難易度 ユースケース
API Key 簡単 開発環境、社内API
IAM認証 難しい AWS内部サービス間通信
Cognito Authorizer 普通 Cognitoで完結する認証
Lambda Authorizer 普通〜難しい カスタム認証、外部IdP連携

Lambda Authorizerを選ぶべき場面

  • JWTトークンの検証が必要
  • 独自の認証ロジック(例:デバイス制限、IP制限)を実装したい
  • 複数のIdPを統合したい
  • 認証時にカスタムデータを注入したい

この記事で得られること

この記事を読むことで、以下のスキルが身につきます:

✅ Lambda Authorizerの仕組みと動作原理
✅ REQUESTタイプとTOKENタイプの使い分け
✅ Cognito OAuth2を使った認証基盤の構築
✅ JWT検証の実装パターン
✅ AWS CDKによるインフラのコード化

前提知識と環境

この記事は以下の知識がある方を想定しています:

  • AWSの基礎(Lambda、API Gateway、Cognitoの概念を理解している)
  • TypeScript/JavaScriptの基本構文
  • ターミナルの基本操作

必要な環境:

  • Node.js 24.x(または18.x以上)
  • AWS CLI(設定済み)
  • AWS CDK 2.x
  • エディタ(VS Code推奨)

インストール確認:

node --version  # v24.x.x
aws --version   # aws-cli/2.x.x
cdk --version   # 2.x.x

Lambda Authorizerの仕組み

認証フローの全体像

Lambda Authorizerを使った認証は、以下の流れで行われます:

重要なポイント:

  1. クライアント認証: OAuth2のClient Credentials Flowでアクセストークンを取得
  2. トークン送信: AuthorizationヘッダーにBearer <トークン>形式で送信
  3. Lambda Authorizer実行: API Gatewayが自動的に呼び出し
  4. JWT検証: トークンの署名、有効期限、発行者をチェック
  5. IAMポリシー生成: Allow/Denyを含むポリシーを返却
  6. コンテキスト注入: カスタムデータをバックエンドLambdaに渡す
  7. キャッシュ: 同じトークンの検証結果を一定時間保存

REQUESTタイプ vs TOKENタイプ

Lambda Authorizerには2つのタイプがあります。

項目 REQUESTタイプ TOKENタイプ
入力データ リクエスト全体(ヘッダー、パス、クエリ等) トークンのみ
キャッシュキー 指定した複数のパラメータ トークン文字列
ユースケース 複雑な認証(IP、デバイス制限等) シンプルなトークン検証
実装難易度 やや複雑 シンプル
柔軟性 高い 低い

今回はREQUESTタイプを使用します。 理由は以下の通りです:

  • リクエスト全体にアクセス可能(将来的な拡張性)
  • キャッシュキーを細かく制御できる
  • 実際のプロダクションでよく使われる

TOKENタイプは、純粋にJWT検証だけを行う場合に適しています。

IAMポリシーの役割

Lambda Authorizerが返すIAMポリシーは「このリクエストを許可するか?」を決定します。

// Lambda Authorizerが返すレスポンス
{
  principalId: "user-123",  // ユーザー識別子
  policyDocument: {         // IAMポリシー
    Version: "2012-10-17",
    Statement: [{
      Action: "execute-api:Invoke",
      Effect: "Allow",      // Allow または Deny
      Resource: "arn:aws:execute-api:..."  // 対象のARN
    }]
  },
  context: {                // カスタムデータ(省略可)
    userId: "123",
    role: "ADMIN"
  }
}

contextパラメータが重要です。

ここに設定したデータはバックエンドLambdaでevent.requestContext.authorizerから取得できます。ユーザーID、ロール、権限情報などを渡すことで、バックエンド側で認可処理を実装できます。

今回構築するシステム

システム構成

以下のアーキテクチャを構築します:

技術スタック

カテゴリ 技術 用途
IaC AWS CDK 2.x インフラのコード化
認証基盤 Amazon Cognito OAuth2/OpenID Provider
関数実行 AWS Lambda (Node.js 24.x) サーバーレス処理
API管理 API Gateway (REST API) エンドポイント提供
JWT検証 aws-jwt-verify JWT署名・有効期限チェック
言語 TypeScript 型安全な開発

セキュリティ設計のポイント

今回の実装で考慮するセキュリティ要素:

  1. JWT署名検証: Cognitoの公開鍵で署名を検証
  2. トークン有効期限: アクセストークン60分、リフレッシュトークン1日
  3. スコープ制御: Resource Serverでスコープを定義
  4. キャッシュTTL: 開発時は0秒、本番は30分程度
  5. HTTPS通信: 全ての通信をTLS暗号化

ハンズオン実装

それでは、実際に手を動かして実装していきましょう!このチュートリアルでは、既存のGitリポジトリをクローンして、すぐにLambda Authorizerを体験できます。

Step 1: リポジトリのクローンと依存関係のインストール

まず、サンプルプロジェクトをクローンします。

# リポジトリをクローン
git clone https://github.com/mashharuki/lambda-authorizer-sample.git
cd lambda-authorizer-sample

# 依存関係をインストール
pnpm install

プロジェクト構造を確認しましょう:

tree -L 2 -I node_modules
lambda-authorizer-example/
├── bin/
│   └── app.ts                      # CDKアプリのエントリーポイント
├── lib/
│   ├── cognito-stack.ts            # Cognito構成
│   ├── api-gateway-stack.ts        # API Gateway + Lambda構成
│   └── lambda-authorizer-main-stack.ts  # メインスタック
├── src/
│   ├── custom-auth-lambda.ts       # Lambda Authorizer実装
│   └── api-lambda.ts               # 保護されたAPI実装
├── package.json
├── tsconfig.json
└── cdk.json

Step 2: Cognitoドメインプレフィックスの変更(重要)

Cognitoのドメインプレフィックスは、世界中で一意である必要があります。  
デプロイ前に変更しましょう。

lib/cognito-stack.tsを開き、以下の行を編集:

// 26行目付近
const domain = this.cognitoUserPool.addDomain('awesome-cognito-domain', {
  cognitoDomain: {
    domainPrefix: '<yourname>-awesome-domain',  // ← ここを自分のユニークな値に変更
  },
});

例:

domainPrefix: 'my-name-lambda-auth-20260114',  // 日付を入れると一意になりやすい

Step 3: CDKブートストラップ(初回のみ)

AWS CDKを初めて使う場合、ブートストラップが必要です。

# AWS環境を確認
aws sts get-caller-identity

# CDKブートストラップ(初回のみ)
cdk bootstrap

Step 4: デプロイ

いよいよデプロイです!

# デプロイ実行
pnpm run cdk deploy '*'

# 確認を求められたら「y」を入力

デプロイには3〜5分かかります。

完了すると、CloudFormation Outputsが表示されます。

✅  LambdaAuthorizerMainStack

Outputs:
LambdaAuthorizerMainStack.UserPoolId = ap-northeast-1_XXXXXXXXX
LambdaAuthorizerMainStack.UserPoolAppClientId = 1a2b3c4d5e6f7g8h9i0j
LambdaAuthorizerMainStack.CognitoDomain = my-name-lambda-auth-20260114
LambdaAuthorizerMainStack.TokenEndpoint = https://my-name-lambda-auth-20260114.auth.ap-northeast-1.amazoncognito.com/oauth2/token
LambdaAuthorizerMainStack.ApiGatewayUrl = https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/
LambdaAuthorizerMainStack.ApiGatewayEndpoint = https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/awesomeapi
LambdaAuthorizerMainStack.AuthorizerLambdaArn = arn:aws:lambda:ap-northeast-1:xxxx:function:awesome-auth-lambda
LambdaAuthorizerMainStack.ApiLambdaArn = arn:aws:lambda:ap-northeast-1:xxxx:function:awesome-api-lambda

これらの値をメモしておいてください
次のステップで使用します。

動作確認

デプロイが完了したら、実際にAPIを呼び出してみましょう。

Step 1: Client Secretの取得

App ClientのClient SecretはAWS Consoleから取得します。

  1. AWS Consoleにログイン
  2. Cognito サービスに移動
  3. 作成したUser Poolを選択
  4. 左メニューの 「アプリの統合」「アプリケーションクライアント」 をクリック
  5. 作成したApp Clientを選択
  6. 「クライアントシークレット」「表示」 をクリック
  7. 表示されたシークレットをコピー

Step 2: アクセストークンの取得

OAuth2のClient Credentials Flowでトークンを取得します。

# 環境変数を設定(値は自分の環境に合わせて変更)
export TOKEN_ENDPOINT="https://my-tutorial-app.auth.ap-northeast-1.amazoncognito.com/oauth2/token"
export CLIENT_ID="1a2b3c4d5e6f7g8h9i0j"
export CLIENT_SECRET="取得したClient Secret"

# トークンを取得
curl -X POST $TOKEN_ENDPOINT \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -u "${CLIENT_ID}:${CLIENT_SECRET}" \
  -d "grant_type=client_credentials&scope=awesomeapi-resource-server/awesomeapi.read"

レスポンス例:

{
  "access_token": "eyJraWQiOiJ...(長いJWT文字列)...HJw",
  "expires_in": 3600,
  "token_type": "Bearer"
}

access_tokenの値をコピーしてください。

Step 3: APIを呼び出す

取得したトークンを使ってAPIを呼び出します。

# 環境変数を設定
export API_ENDPOINT="https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/awesomeapi"
export ACCESS_TOKEN="取得したアクセストークン"

# 認証ありでAPIを呼び出す
curl -X GET $API_ENDPOINT \
  -H "Authorization: Bearer ${ACCESS_TOKEN}"

成功すると以下のようなレスポンスが返ります:

{
  "message": "Hello from protected resource!",
  "userId": "123",
  "role": "ADMIN",
  "timestamp": "2026-01-11T12:34:56.789Z"
}

Step 4: 認証なしでエラーを確認

トークンなしでAPIを呼び出すと、401エラーが返ることを確認します。

# 認証なしで呼び出す
curl -X GET $API_ENDPOINT

レスポンス:

{
  "message": "Unauthorized"
}

Step 5: CloudWatch Logsで確認

Lambda Authorizerの実行ログをCloudWatch Logsで確認できます。

  1. AWS Consoleで CloudWatch に移動
  2. 左メニューの 「ログ」「ロググループ」 をクリック
  3. /aws/lambda/tutorial-auth-lambda を選択
  4. 最新のログストリームを開く

ログには以下のような情報が記録されています:

Lambda Authorizer invoked: {
  "headers": {
    "authorization": "Bearer eyJraWQiOi..."
  },
  "methodArn": "arn:aws:execute-api:..."
}

JWT verified successfully: {
  "sub": "1a2b3c4d-...",
  "client_id": "1a2b3c4d5e6f7g8h9i0j",
  "token_use": "access",
  ...
}

Authorizer response: {
  "principalId": "1a2b3c4d-...",
  "policyDocument": {...},
  "context": {...}
}

デバッグ時はこのログが非常に役立ちます。

コードの解説(実装のポイント)

ここまでで、Lambda Authorizerを使った認証システムが動作しました。次に、各コンポーネントの実装について詳しく見ていきましょう。

Cognitoスタックのポイント

lib/cognito-stack.tsでは、OAuth2認証基盤を構築しています。

// Resource Serverの作成
const resourceServer = new cognito.UserPoolResourceServer(
  this,
  'awesome-resource-server',
  {
    identifier: 'awesomeapi-resource-server',
    userPool: this.cognitoUserPool,
    scopes: [awesomeApiReadScope],  // スコープを定義
  }
);

重要なポイント:

  1. Resource Server: APIをスコープで管理する単位。今回はawesomeapi.readスコープを定義
  2. App Client: OAuth2のクライアント。generateSecret: trueでClient Secretを生成
  3. Client Credentials Flow: マシン間通信用のOAuth2フロー。ユーザーログイン不要
  4. トークン有効期限: アクセストークン60分、リフレッシュトークン1日

Lambda Authorizer関数のポイント

src/custom-auth-lambda.tsでは、JWT検証と認可処理を実装しています。

// JWT検証用のVerifierを初期化
const cognitoJwtVerifier = CognitoJwtVerifier.create({
  userPoolId: process.env.USERPOOL_ID || '',
  clientId: process.env.CLIENT_ID,
  tokenUse: 'access',  // アクセストークンを検証
});

export const handler = async function (event: any): Promise<APIGatewayAuthorizerResult> {
  // Step 1: Authorizationヘッダーからトークンを抽出
  const authHeader = event.headers['authorization'] || '';
  const authToken = authHeader.replace(/^Bearer\s+/i, '');

  try {
    // Step 2: JWTを検証
    const decodedJWT = await cognitoJwtVerifier.verify(authToken);

    // Step 3: IAMポリシーを生成
    const policyDocument: PolicyDocument = {
      Version: '2012-10-17',
      Statement: [{
        Action: 'execute-api:Invoke',
        Effect: 'Allow',  // アクセスを許可
        Resource: event['methodArn'],
      }],
    };

    // Step 4: カスタムコンテキストを作成
    const context = {
      userId: '123',
      companyId: '456',
      role: 'ADMIN',
    };

    // Step 5: レスポンスを返す
    return {
      principalId: decodedJWT.sub,
      policyDocument,
      context,
    };
  } catch (err) {
    throw new Error('Unauthorized');
  }
};

実装のポイント:

  1. トークン抽出: Bearer プレフィックスを削除(大文字小文字を区別しない)
  2. JWT検証: aws-jwt-verifyライブラリが署名、有効期限、発行者を自動検証
  3. IAMポリシー: Effect: 'Allow'で認可、Denyで拒否
  4. カスタムコンテキスト: バックエンドLambdaに渡したいデータを設定
  5. エラーハンドリング: 検証失敗時はUnauthorizedをスロー(API Gatewayが401を返す)

API Lambda関数のポイント

src/api-lambda.tsでは、保護されたAPIリソースを実装しています。

export const handler = async function (event: APIGatewayProxyEventV2): Promise<APIGatewayProxyResultV2> {
  // Lambda Authorizerから渡されたコンテキストを取得
  const authorizer = (event.requestContext as any).authorizer;

  // contextに設定した値を利用できる
  const userId = authorizer?.userId;
  const role = authorizer?.role;

  return {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Hello from protected resource(Lambda Authorizer)',
      userId: userId,
      role: role,
    }),
  };
};

実装のポイント:

  • event.requestContext.authorizerから、Lambda Authorizerで設定したコンテキストにアクセス
  • ここで取得した情報を使って、詳細な認可処理を実装可能

API Gatewayスタックのポイント

lib/api-gateway-stack.tsでは、API GatewayとLambda Authorizerを統合しています。

// Lambda Authorizer(REQUESTタイプ)を作成
const authorizer = new apigw.RequestAuthorizer(this, 'awesome-api-request-authorizer', {
  handler: authLambda,
  identitySources: [apigw.IdentitySource.header('authorization')],
  resultsCacheTtl: cdk.Duration.minutes(30),
});

// APIメソッドにAuthorizerを紐付け
awesomeApiResource.addMethod(
  'GET',
  new apigw.LambdaIntegration(apiLambda),
  {
    authorizer: authorizer,
    authorizationType: apigw.AuthorizationType.CUSTOM,
  }
);

重要な設定:

  1. RequestAuthorizer: REQUESTタイプを使用(リクエスト全体にアクセス可能)
  2. identitySources: キャッシュキーとなるパラメータ(authorizationヘッダー)
  3. resultsCacheTtl: キャッシュの有効期限(開発時はDuration.seconds(0)推奨)
  4. authorizationType: CUSTOMを指定してLambda Authorizerを使用

REQUESTタイプを選んだ理由

今回はREQUESTタイプを採用しました。理由は以下の通りです:

理由 説明
将来の拡張性 IPアドレス制限、デバイス制限など、ヘッダー以外の情報も活用できる
キャッシュ制御 複数のパラメータを組み合わせてキャッシュキーを作成可能
実践的 本番環境でよく使われるパターン

TOKENタイプは、シンプルなJWT検証のみを行う場合に適しています。

クリーンアップ(リソースの削除)

チュートリアルが終わったら、AWSリソースを削除して課金を停止しましょう。

CDKでの削除

# スタックを削除
pnpm run cdk destroy '*'

# 確認を求められたら「y」を入力

クライアントアプリケーションはマネジメントコンソールから削除する必要があります!

手動確認(任意)

念のため、AWS Consoleで以下を確認してください:

  1. CloudFormation

    • スタック「LambdaAuthorizerMainStack」が削除されているか確認
  2. Cognito

    • User Poolが削除されているか確認
  3. Lambda

    • awesome-auth-lambdaawesome-api-lambdaが削除されているか確認
  4. API Gateway

    • APIが削除されているか確認

CDKブートストラップリソース(残しても可)

CDKブートストラップで作成されたリソース(S3バケット等)は残ります。
これらは他のCDKプロジェクトでも使用されるため、通常は削除不要です。

もし完全に削除したい場合:

# CDKToolkitスタックを削除
aws cloudformation delete-stack --stack-name CDKToolkit

⚠️ 注意: 他のCDKプロジェクトがある場合は削除しないでください。

よくあるトラブルと対処法

エラー1: "Unauthorized"が返る

症状: APIを呼び出すと常に401エラーが返る

原因と対処法:

  1. トークンが期限切れ

    • アクセストークンは60分で期限切れ
    • 新しいトークンを取得し直す
  2. スコープが不一致

    # スコープを確認
    echo $ACCESS_TOKEN | cut -d'.' -f2 | base64 -d | jq .scope
    
    • awesomeapi-resource-server/awesomeapi.readが含まれているか確認
  3. JWT検証エラー

    • CloudWatch Logsでエラー内容を確認
    • USERPOOL_IDCLIENT_IDの環境変数が正しいか確認

エラー2: "Access Denied"エラー

症状: Lambda Authorizerは成功するが、バックエンドLambdaにアクセスできない

原因と対処法:

  • IAMポリシーのEffectDenyになっている
  • methodArnが正しく設定されているか確認
  • Lambda AuthorizerのコードでEffect: 'Allow'になっているか確認

エラー3: キャッシュが効きすぎる

症状: トークンを変更しても、古い結果が返る

原因と対処法:

  • resultsCacheTtlの設定を確認
  • 開発時はDuration.seconds(0)に設定
  • 本番環境では適切な値(30分程度)に設定
// 開発環境用の設定
resultsCacheTtl: cdk.Duration.seconds(0),

// 本番環境用の設定
resultsCacheTtl: cdk.Duration.minutes(30),

エラー4: "Internal Server Error"

症状: 500エラーが返る

原因と対処法:

  • Lambda関数内で例外が発生している
  • CloudWatch Logsでスタックトレースを確認
  • よくある原因:
    • aws-jwt-verifyがバンドルされていない
    • 環境変数が未設定
    • TypeScriptのビルドエラー

まとめ

この記事ではLambda Authorizerの基礎から実装まで一通り学びました!

この記事で学んだこと

✅ Lambda Authorizerの仕組みと動作原理
✅ REQUESTタイプとTOKENタイプの違い
✅ Cognito OAuth2を使った認証基盤の構築
✅ JWT検証の実装パターン
✅ AWS CDKによるインフラのコード化
✅ カスタムコンテキストの活用方法

Lambda Authorizerは柔軟な認証を実現する強力な機能です。

最初は複雑に感じるかもしれませんが、一度理解すれば様々なユースケースに応用できます。

ここまで読んでいただきありがとうございました!

参考文献

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?