コンテンツにスキップ

リフレッシュトークン

メソッド

REST メソッドを採用しています。

HTTP メソッド

POST: トークン更新

命名規則

クエリパラメータとノードの命名を統一し、可読性を向上させるため、リクエスト時の URI と JSON 内のノードには snake_case を使用します。

リクエストとレスポンス

ヘッダー

メタ情報はレスポンスボディではなく、HTTP ヘッダーに設定されます。

リクエストヘッダー

  • Content-Type: application/json

レスポンスヘッダー

  • Content-Type: application/json

リフレッシュトークン

URI

/api/v1/auth/refresh_token

リクエストボディ

リクエストボディは JSON です。

管理者用 Cognito アプリクライアントに クライアントシークレット(COGNITO_ADMIN_CLIENT_SECRET)がある場合、Cognito は正しい SECRET_HASH を要求します。これはユーザー名文字列から計算されます。実運用では、ログイン(または /me)レスポンスの cognito_sub(JWT の sub)を送ることを推奨します。ユーザープールの設定によっては email のみでは拒否されることがあります(メールエイリアスでのサインインなど)。両方送る場合、ハッシュ計算には cognito_sub を優先します。

{
  "refresh_token": "eyJjdGkiOiI0YjM1ZjQ2NS0yM2E0LTRiZTAtYjM2Mi01...",
  "email": "admin@example.com",
  "cognito_sub": "1111-aaaa-2222-bbbb-3333-cccc4444dddd"
}

バリデーションルール

フィールド ルール
refresh_token 必須。空でない文字列であること。
email 任意。メール形式。クライアントシークレットがある場合は、cognito_sub と 少なくともどちらか一方が必須。
cognito_sub 任意。空でない文字列。ログイン / JWT のユーザー sub。コンフィデンシャルクライアント(シークレットあり)では推奨。

レスポンス(200 OK)

レスポンスは JSON です。

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJjdGkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ...",
  "user": {
    "id": 1,
    "name": "Admin User",
    "cognito_sub": "1111-aaaa-2222-bbbb",
    "email": "admin@example.com",
    "role": {
      "id": 1,
      "name": "admin"
    }
  }
}

Cognito が新しいリフレッシュトークンを返さない場合、refresh_token は省略されることがあります。

認証要件

このエンドポイントは認証を要求しません。リクエストボディにリフレッシュトークンを含めます。Authorization ヘッダーは不要です。

例外処理

例外時のステータスコードは次のとおりです。

説明 ステータスコード ステータス名
リクエストボディが不正、またはバリデーションエラー 400 Bad Request
リフレッシュトークンが無効または期限切れ 401 Unauthorized
アプリクライアントにシークレットがあるのに email と cognito_sub の両方が欠けている 401 Unauthorized
更新後の ID トークンから必須クレームが取得できない(email や sub など) 401 Unauthorized
管理者が見つかりません 401 Unauthorized
サーバ内部エラー 500 Internal Server Error

処理フロー

シーケンス図

sequenceDiagram
    participant Client
    participant API as Hono Router
    participant Validate as Zod Validation
    participant Service as Auth Service
    participant Cognito as AWS Cognito
    participant DB as PostgreSQL Database

    Client->>API: POST /api/v1/auth/refresh_token
    API->>Validate: Validate body (refresh_token, optional email, cognito_sub)
    Validate-->>API: Validation passed

    API->>Service: refreshToken(refresh_token, email, cognito_sub, ip, user_agent)
    Service->>Cognito: initiateAuth(REFRESH_TOKEN_AUTH, SECRET_HASH if client secret)

    alt Token Refresh Success
        Cognito-->>Service: New Access Token + ID Token
        Service->>Service: Verify ID token, extract sub and email
        Service->>DB: Find admin by cognito_sub
        DB-->>Service: Admin record
        Service->>DB: Create session (hashed token, ip, user_agent, expires_at)
        DB-->>Service: Session created
        Service-->>API: { token, refresh_token?, user }
        API-->>Client: 200 OK { token, refresh_token?, user }
    else Token Refresh Failed
        Cognito-->>Service: NotAuthorizedException
        Service-->>API: throw UnauthorizedError
        API-->>Client: 401 Unauthorized
    end

Routes 層

ルーティングはここで行います。Zod スキーマでリクエストボディを検証し、クライアント IP と User-Agent を取り出してから認証サービスに委譲します。

ソース: src/routes/v1/auth.ts

Services 層

ビジネスロジックの説明です。クライアントシークレットがある場合は SECRET_HASH 用のユーザー名文字列を解決します(cognito_sub を優先、なければ正規化した email)。続けて Cognito の REFRESH_TOKEN_AUTH を呼び出し、新しい ID トークンを検証して管理者情報を抽出し、データベースにセッションを作成し、新しいアクセストークン・(あれば)新しいリフレッシュトークン・ユーザー情報を返します。

ソース: src/services/auth.ts

Repositories 層

データベースおよび外部サービスへのアクセスです。Cognito の InitiateAuthCommand(REFRESH_TOKEN_AUTH)でリフレッシュを行い、SECRET_HASH は呼び出し側が渡したユーザー名文字列から計算します(リフレッシュトークン文字列そのものではありません)。Cognito sub による管理者検索、アクセストークンの SHA-256 ハッシュを用いたセッション作成を扱います。

ソース: src/repositories/

セキュリティ

  • リフレッシュトークンは使い捨てです。更新のたびに新しいリフレッシュトークンが発行されます
  • コンフィデンシャルクライアントでは cognito_sub(またはプールで有効な email)を送り、SECRET_HASH を Cognito と一致させます。ログイン時に得た cognito_sub をリフレッシュトークンと一緒に保持してください
  • サインイン用 email はパスワード/パスワードリセット系フローで正規化(trim + 小文字化)され、USERNAME / SECRET_HASH の一貫性が保たれます
  • クライアント IP アドレスと User-Agent は監査用に記録されます
  • セッショントークンはデータベースに SHA-256 ハッシュで保存されます
  • 更新失敗はリクエストコンテキスト付きでログに記録されます
  • 期限切れまたは失効したリフレッシュトークンは Cognito によって拒否されます