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 Cognitoを用いたユーザー認証アダプター実装例

Posted at

概要

AWS SDK for JavaScript (v3) と Cognito を用いてユーザー認証・管理を行うクラス CognitoAdapter の実装例を紹介する。

実装例は以下の機能をサポートしている。

  • ユーザー登録 (signup)
  • 登録完了承認コードの確認 (confirmSignup)
  • 登録完了承認コードの再送 (resendConfirmationCode)
  • ログイン (login)
  • RefreshTokenを用いたAccessTokenの再取得 (refreshAccessToken)
  • AccessTokenの検証 (verifyAccessToken)

前提条件

  • Cognito User Pool, App Client (Client Secret有り) が設定済み
  • 環境変数として以下が設定済み
    • AWS_REGION: CognitoがデプロイされているAWSリージョン
    • AWS_COGNITO_USER_POOL_ID: 対象User PoolのID
    • AWS_COGNITO_CLIENT_ID: 対象App ClientのID
    • AWS_COGNITO_CLIENT_SECRET: 対象App Clientに紐づくClient Secret

使用ライブラリ

  • @aws-sdk/client-cognito-identity-provider: CognitoのAPIクライアント
  • aws-jwt-verify: JWTトークンの検証を行うライブラリ
  • crypto: 暗号化ライブラリ

コード例

import {
  CognitoIdentityProviderClient,
  SignUpCommand,
  ConfirmSignUpCommand,
  ResendConfirmationCodeCommand,
  InitiateAuthCommand,
} from '@aws-sdk/client-cognito-identity-provider'
import crypto from 'crypto'
import { getEnv } from '@/backend/utils/env'  // 自前の環境変数取得用のヘルパー
import { CognitoJwtVerifier } from 'aws-jwt-verify'

export class CognitoAdapter {
  private client: CognitoIdentityProviderClient
  private clientId: string
  private clientSecret: string
  private verifier: CognitoJwtVerifier

  constructor() {
    this.client = new CognitoIdentityProviderClient({
      region: getEnv('AWS_REGION'),
    })
    this.clientId = getEnv('AWS_COGNITO_CLIENT_ID')
    this.clientSecret = getEnv('AWS_COGNITO_CLIENT_SECRET')
    this.verifier = CognitoJwtVerifier.create({
      userPoolId: getEnv('AWS_COGNITO_USER_POOL_ID'),
      tokenUse: 'access',
      clientId: this.clientId,
    })
  }

  // ユーザー登録
  async signup(email: string, password: string) {
    const secretHash = this.#calculateSecretHash(email)
    const command = new SignUpCommand({
      SecretHash: secretHash,
      ClientId: this.clientId,
      Username: email,
      Password: password,
    })
    return await this.client.send(command)
  }

  // ユーザー登録承認
  async confirmSignup(email: string, confirmationCode: string) {
    const secretHash = this.#calculateSecretHash(email)
    const command = new ConfirmSignUpCommand({
      SecretHash: secretHash,
      ClientId: this.clientId,
      Username: email,
      ConfirmationCode: confirmationCode,
    })
    return await this.client.send(command)
  }

  // ユーザー登録用の認証コード再送
  async resendConfirmationCode(email: string) {
    const secretHash = this.#calculateSecretHash(email)
    const command = new ResendConfirmationCodeCommand({
      SecretHash: secretHash,
      ClientId: this.clientId,
      Username: email,
    })
    return await this.client.send(command)
  }

  // ログイン(ユーザーパスワード認証)
  async login(email: string, password: string) {
    const secretHash = this.#calculateSecretHash(email)
    const command = new InitiateAuthCommand({
      ClientId: this.clientId,
      AuthFlow: 'USER_PASSWORD_AUTH',
      AuthParameters: {
        SECRET_HASH: secretHash,
        USERNAME: email,
        PASSWORD: password,
      },
    })
    return await this.client.send(command)
  }

  // RefreshTokenを用いてAccessToken再取得
  async refreshAccessToken(refreshToken: string, sub: string) {
    const secretHash = this.#calculateSecretHash(sub)
    const command = new InitiateAuthCommand({
      ClientId: this.clientId,
      AuthFlow: 'REFRESH_TOKEN_AUTH',
      AuthParameters: {
        SECRET_HASH: secretHash,
        REFRESH_TOKEN: refreshToken,
      },
    })
    return await this.client.send(command)
  }

  // AccessTokenを検証
  async verifyAccessToken(accessToken: string) {
    return await this.verifier.verify(accessToken)
  }

  // SecretHash 計算
  #calculateSecretHash(email: string) {
    const message = email + this.clientId
    const hmac = crypto.createHmac('sha256', this.clientSecret)
    hmac.update(message)
    return hmac.digest('base64')
  }
}

主な処理のポイント

1. Clientインスタンスの生成

CognitoIdentityProviderClient を使用して、Cognitoと通信するためのクライアントを生成しています。

2. Secret Hashの生成

CognitoのApp Clientに「シークレットキー」が有効になっている場合、SECRET_HASH を計算する必要があります。
#calculateSecretHash() メソッドは、email + clientId を鍵としてHMAC-SHA256で署名し、その結果をBase64エンコードしたものをSecretHashとして返します。

3. ユーザー登録・承認フロー

signup() でユーザー登録
confirmSignup() でメール等で届いた確認コードを用いて登録を承認
resendConfirmationCode() で承認コードの再送
これらはCognitoによる標準的なユーザーオンボーディングフローを実現します。

4. ログインおよびRefresh TokenでのAccessToken再取得

login() は、USER_PASSWORD_AUTH フローでログインします。
成功時には AccessToken, IdToken, RefreshToken などが返却されます。
refreshAccessToken() により、失効前の RefreshToken を使って新たな AccessToken を取得可能です。

5. AccessToken検証

verifyAccessToken() は、aws-jwt-verify を用いてトークンがCognitoで発行されたものであることを検証します。
CognitoJwtVerifier.create() で tokenUse や userPoolId、clientId を指定しているため、不正なトークンを弾くことができます。

まとめ

本記事では、AWS Cognitoを用いたユーザー認証処理を抽象化するクラスCognitoAdapterの実装例を示しました。
このクラスを利用することで、サーバーサイドのユーザー登録、認証、トークン検証といった処理を簡潔に扱うことができます。

参考になれば幸いです。

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?