JWTを理解する(Cognitoを使ったログイン機能)
JWTについて理解が不十分だったため、 本記事ではJWTの仕組みや作成〜活用方法を整理しました。
例としてCognitoを使ったログイン機能を扱います。
JWTとは
Json Web Tokenの略で、ログインした際に返却される値です。
中身は以下3つです。
トークン名 | 内容 | 用途 |
---|---|---|
ID Token | ユーザー情報(email, nameなど)を含む | フロントで表示などに利用 |
Access Token | ユーザーのグループ情報・権限を含む | APIアクセス制御に使用 |
Refresh Token | トークンの期限切れ時に更新に使用 | サイレントリフレッシュなど |
ID TokenとAccess TokenはHEADER.PAYLOAD.SIGNATURE(ヘッダ.ペーロード.署名)
の形式で、Refresh TokenはJWTではなく、単なるランダム文字列です。
ID Token
ID Token にはログイン中のユーザー情報(email、nameなど)が含まれます。
ログイン済みユーザーの情報をフロントエンドで扱うために使います。
ID TokenをBase64URLでデコードすることで中身を確認できます。
ヘッダ
{
"alg": "RS256", // 署名アルゴリズム(RS256=公開鍵方式)
"typ": "JWT", // トークンの種類(通常JWT)
"kid": "abcde12345" // 公開鍵ID(JWKSから鍵を選ぶために使う)
}
ペイロード
{
"sub": "abcdefgh-1234-5678-xxxx-yyyyyyyyyy", // ユーザーID(一意な識別子)
"email": "user@example.com",
"email_verified": true,
"name": "User Name",
"aud": "xxxxxxxxxxxxxxxxxxxxxxxxxx", // アプリクライアントID
"iss": "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXXX", // 発行者(Cognito)
"exp": 1710000000, // 有効期限(UNIX時間)
"iat": 1709996400 // 発行時刻(UNIX時間)
}
Access Token
Access TokenはAPI実行時の認可・ユーザー特定に使われます。
フロントエンドからAPIを呼ぶとき、Authorizationヘッダにセットして送信し、バックエンドでデコードされます。
ID Tokenと同様にBase64URLでデコードすることで中身を確認できます。
Header
{
"alg": "RS256", // 署名アルゴリズム
"typ": "JWT", // トークンの種類
"kid": "abcde12345" // 公開鍵ID
}
Payload
{
"sub": "abcdefgh-1234-5678-xxxx-yyyyyyyyyy", // ユーザーID
"username": "user123", // Cognito上のユーザー名
"scope": "email openid profile", // 許可されたスコープ
"auth_time": 1709990000, // 認証完了した時刻
"exp": 1710000000, // 有効期限
"iat": 1709996400, // 発行時刻
"iss": "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXXX",
"token_use": "access", // これはAccess Tokenだと示す
"client_id": "xxxxxxxxxxxxxxxxxxxxxxxxxx" // アプリクライアントID
}
JWTのそれぞれの部分の使用タイミングと役割は以下のとおりです。
JWTの部分 | 使用タイミング | バックエンドでの役割と使い方 |
---|---|---|
Header | 検証前(最初) | - alg (署名アルゴリズム)を確認- kid (鍵のID)でJWKSから正しい公開鍵を選ぶ |
Payload | 検証後 | - sub (ユーザーID)でユーザー特定- scope や group で認可判断- exp で期限切れ確認 |
Signature | 検証時 | - HEADER + PAYLOAD を元にハッシュを生成し、署名と一致するかチェック(改ざんされてないか確認) |
Refresh Token
Refresh Token は、Access Token や ID Token の 有効期限が切れたときに新しいトークンを取得するために使います。
通常、ログイン時に一緒に返却され、有効期限は数日〜数週間と長めに設定されています。
Refresh Token はJWTではなく、ランダムな文字列形式です。
そのためデコードはできず、Cognitoだけがこのトークンを理解・検証できます。
JWTの作成と使い方
JWTの作成
- ユーザーは、新規登録または登録済みのユーザーIDとパスワードでアプリケーションにログインする
- アプリケーションは、ユーザーIDとパスワードが正しいかどうかをデータベースと照らし合わせて確認する
- 2が問題なければ、アプリケーションはJWTを作成する
- JWTの署名には、アプリケーションが保持している秘密鍵を使う
- 鍵の作成には共通鍵方式と公開鍵方式があるがcognitoは公開鍵方式)
- アプリケーションは、作成したJWTをユーザーに返す
- JWTを受け取ったユーザーは、ブラウザのCookieやセッションストレージに保存する
画面にユーザ情報を表示する
- ID Tokenをデコードして、email/nameなどを取得する
- 画面に表示する
APIを呼び出す
-
Access TokenをAuthorizationヘッダに付与して送信する
-
JWT検証・認可を実施する
- トークンが改ざんされていないか、公開鍵で検証する
- トークンの有効期限チェック
exp(有効期限)が現在時刻より未来かどうかをチェックし、期限切れなら401エラーを返す
有効期限内の場合
- subを使ってユーザーの特定をする
- scopeやgroupで、ユーザーにこのAPIを使う権限の有無を判定する
- 問題なければAPIを実行し、権限がなければ401エラーを返す。
有効期限切れの場合
- ユーザーに401エラーを返す
- Refresh Tokenで新しいAccess Tokenを生成する
- Refresh TokenでCognitoにAccess Tokenの再発行を要求する
- 新しいAccess Tokenを受け取る
- APIを再度呼び出す
Refresh Tokenの有効期限も切れている場合
- ユーザーは再ログインして、新しいID Token、Access Token、Refresh Tokenを受け取る
expの必要性
expの有効期限が切れていたとしても、Refresh Tokenによる期限更新により、ユーザーは再ログインなしでAPIを実行できます。
そうするとexpが不要に思えるかもしれません。
しかし、下記理由によりexpは必要です。
トークンの使い回しを防ぐ
トークン(ID Token、Access Token)が盗まれても、有効期限が短いほど被害が最小化されます。
1年有効なトークンが漏れると「1年間ずっと不正利用できる」ことになります。
強制ログアウトや権限変更を即座に反映する
ユーザーの権限や所属グループが変更された場合、「有効期限内の古いトークン」は新しい状態を反映できません。
短い有効期限(たとえば1時間ごとにトークンが新発行される)なら、最新の情報がトークンに反映されやすくなります。
再ログインの回数を減らせる
Refresh Tokenにも有効期限があります。
expをなくして、Refresh Tokenの有効期間を短くすると、Refresh Tokenの有効期限が切れるたびに再ログインしなくてはなりません。
まとめ
トークン名 | 内容 | 用途 |
---|---|---|
ID Token | ユーザー情報(email, nameなど)を含む | フロントで表示などに利用 |
Access Token | ユーザーのグループ情報・権限を含む | APIアクセス制御に使用 |
Refresh Token | トークンの期限切れ時に更新に使用 | サイレントリフレッシュなど |
- ID TokenとAccess TokenのexpとRefresh Tokenはどちらもトークンの有効期限を示し、Refresh Tokenの有効期限が切れるまでは、ユーザーは再ログインなしでAPIを実行できる
- expを短め(数時間)、Refresh Tokenを長め(数日〜数週間)に設定しておくことで、ユーザ情報のトークンへの反映をスムーズに行い、セキュリティも担保できる
参考
以下を参考にさせていただきました。
Discussion