CognitoのIDトークンをLambda関数で検証するサンプルコード

CognitoのIDトークンを簡単に検証したい。する。

目次

やりたいこと

認証基盤としてCognitoを使う。サインインした時に発行されるIDトークンについて、できるだけ簡単に検証したい。

awslabs/aws-jwt-verify: JS library for verifying JWTs signed by Amazon Cognito, and any OIDC-compatible IDP that signs JWTs with RS256, RS384, and RS512

神ライブラリがあったので使ってみる。

やってみる

IDトークンを検証するだけのAPIを作る。

API Gateway → Lambda関数

APIはAuthorizationヘッダにBearerでIDトークンをのっけたリクエストを想定。

これをSAM使って作る。

ディレクトリ構造

SAMのテンプレートをベースにして作ったので無駄ファイルもありそう。

.
├── README.md
├── buildspec.yml
├── samconfig.toml
├── src
│   ├── handlers
│   │   └── handler.mjs
│   └── package.json
└── template.yaml

buildspec.yml

SAMのテンプレートそのまま流用したので変な感じだけどキニシナイ

version: 0.2
phases:
  install:
    commands:
      # Install all dependencies (including dependencies for running tests)
      - npm install
  pre_build:
    commands:
      # Discover and run unit tests in the '__tests__' directory
      - npm run test
      # Remove all unit tests to reduce the size of the package that will be ultimately uploaded to Lambda
      - rm -rf ./__tests__
      # Remove all dependencies not needed for the Lambda deployment package (the packages from devDependencies in package.json)
      - npm prune --production
  build:
    commands:
      # Use AWS SAM to package the application by using AWS CloudFormation
      - aws cloudformation package --template template.yaml --s3-bucket $S3_BUCKET --output-template template-export.yml
artifacts:
  type: zip
  files:
    - template-export.yml

template.yaml

buildspecは.ymlなのにテンプレートファイルは.yamlだけどキニシナイ

AWSTemplateFormatVersion: 2010-09-09
Description: >-
  cognitojwt

Transform:
- AWS::Serverless-2016-10-31

Globals:
  Function:
    Tracing: Active
  Api:
    TracingEnabled: True

Resources:
  getAllItemsFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src
      Handler: handlers/handler.handler
      Runtime: nodejs18.x
      Architectures:
        - x86_64
      MemorySize: 128
      Timeout: 100
      Policies:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Description: "IdToken validation"
      Events:
        Api:
          Type: Api
          Properties:
            Path: /
            Method: GET

Outputs:
  WebEndpoint:
    Description: "API Gateway endpoint URL for Prod stage"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"

samconfig.toml

samconfigは sam deploy --guidedで作ったほうが良いと思う。が、一応載せておく。

version = 0.1
[default]
[default.deploy]
[default.deploy.parameters]
stack_name = "cognitojwt"
s3_bucket = "バケット"
s3_prefix = "cognitojwt"
region = "ap-northeast-1"
capabilities = "CAPABILITY_IAM"
image_repositories = []

スタック名とバケット名をちゃんと書いたらとおるとは思う。

src/package.json

なんか明らかにいらんもんも入ってるのはご愛嬌。基本的には aws-jwt-verify さえ入れておけばOK。

{
    "name": "delete-test-01",
    "description": "delete-test-01-description",
    "version": "0.0.1",
    "private": true,
    "dependencies": {
        "aws-jwt-verify": "^3.2.0"
    },
    "devDependencies": {
        "aws-sdk-client-mock": "^2.0.0",
        "jest": "^29.2.1"
    },
    "scripts": {
        "test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js"
    }
}

src/handlers/handler.mjs

コードは以下。UserPoolIdとclientIdを埋めちゃってね。

import { CognitoJwtVerifier } from "aws-jwt-verify";

async function verifyJwt(jwt) {
  const verifier = CognitoJwtVerifier.create({
    userPoolId: "<ユーザプールID>",
    tokenUse: "id",  // アクセストークンの時は access
    clientId: "<クライアントID>",
  });

  try {
    const payload = await verifier.verify(jwt);
    console.log("Token is valid. Payload: ", payload);
    return payload;
  } catch (err) {
    console.error(err);
    console.log("Token not valid!");
    return {message: "Token not valid"};
  }  
}

export const handler = async (event) => {
  console.info('received:', event);

  const jwt = event.headers.Authorization.split(' ')[1];
  const payload = await verifyJwt(jwt);

  const response = {
    statusCode: 200,
    body: JSON.stringify(payload)
  };

  console.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`);
  return response;
}

入力として、API Gateway経由のBearerでIDトークンのっけたリクエストを想定している。

実行する

sam build --use-container からの sam deploy --guided(2回目以降、というかsamconfig.tomlがあれば--guidedなしで)。
curlで叩く。

curl --location --request GET 'エンドポイント' \
--header 'Authorization: Bearer ここにIDトークンいれる'

うまくいくと下記のようなレスポンスが返る。

{
    "sub": "xxxx",
    "aud": "クライアントIDっぽい",
    "event_id": "xxxx",
    "token_use": "id",
    "auth_time": 1669872162,
    "iss": "https://cognito-idp.ap-northeast-1.amazonaws.com/ユーザプールID",
    "cognito:username": "サインインしたユーザ名",
    "exp": 1669888765,
    "iat": 1669885165
}

expもあるが、もし期限切れだったらライブラリのほうでちゃんとエラー返してくれる。

やってみたら忘れずにスタック削除する。

以上。

参考

自前でも頑張れるんだけれど、というか前にこのライブラリの存在知らず頑張った記憶があるんだが、たいへんだったのでライブラリ使いたい。

以下の記事助かりました。AWSの公式ドキュメントは闇だけどブログ記事は助かるの多い。

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次