【Java版】CognitoのIDトークンのJWT検証とダミートークンの作成
前回、AWS SDK を使って Cognito 認証を実現しました。
その際、取得した ID トークンについて JWT 形式の文字列を検証してみようということになりました。
今回は、この JWT の文字列の中身の確認とダミートークンの作成について紹介していきます。
JWT形式について
JWT は JSON Web Token(ウェブトークン)の略で、ドット区切りの文字列で構成されています。
・ヘッダ
・ペイロード
・署名
ドットでセパレートされたそれぞれの文字列は Base64 エンコードされているので、デコードすることで中身の確認ができます。
AWS でも以下のドキュメントが用意されているので、署名も含めて検証される場合は参考にしてください。
ID トークンの署名は、JWT トークンのヘッダーとペイロードに基づいて計算されます。Web API のアプリケーション外で使用するときは、常にこの署名を検証してからトークンを受け入れる必要があります。
JSON web トークンの検証
アプリケーションの認証方法として Amazon Cognito ユーザープールを使用したいと考えています。クライアントからアプリケーションに送信された ID とアクセストークンを検証する安全な方法は何ですか?
ウェブトークンの署名を復号して検証する方法
ヘッダ部の確認
参考までに、ヘッダ部を見てみましょう。
$ echo 'xxxxxxxxxx' | base64 -D | jq .
parse error: Unfinished string at EOF at line 1, column 69
おっと、JSON 文字列の最後のカッコが抜けているようですね。
{“kid”: “xxxxxxxxxxxxxxxxxxxx”,”alg”: “RS256”
Base64 エンコードされているものの、どうやらパディング部分が省略されているので必要に応じて「=」を追加してみると良さそうです。
$ echo 'xxxxxxxxxx==' | base64 -D | jq .
結果、以下の JSON が確認できると思います。
{
"kid": "xxxxxxxxxxxxxxxxxxxx",
"alg": "RS256"
}
「alg」はトークンの署名に使用されるアルゴリズムで、RS256 は SHA-256 を持つ RSA 署名のこと。
ペイロード部の確認
ペイロード部にはトークンの有効期限や所有者(ユーザ)に関連する情報があります。
{
"sub": "xxxxxxxxxx",
"aud": "xxxxxxxxxx",
"event_id": "xxxxxxxxxx",
"token_use": "id",
"auth_time": 1646060400,
"iss": "https://cognito-idp.[リージョン].amazonaws.com/[ユーザープールID]",
"cognito:username": "xxxxxxxxxx",
"custom:client_id": "xxxxxxxxxx",
"exp": 1646060400,
"iat": 1646060400,
"email": "xxxxxxxxxx"
}
この中でチェックしておくと良い項目は以下の 4 つです。
・aud(対象者)
・token_use
・iss(Issuer)
・exp(有効期限)
ダミートークンの作成
ダミートークンはライブラリを使うと簡単にできますが、アルゴリズムによって署名の部分がややこしいです。
HMAC256 だと楽なのですが、RS256 は少し面倒かな・・・。
今回は java-jwt のライブラリを利用してみます。
Gradle で利用する場合は以下の通り。
implementation("com.auth0:java-jwt:3.18.3")
HMAC256
HMAC256 は任意の文字列で Algorithm オブジェクトが生成できるのでそのまま使えます。
Algorithm algorithm = Algorithm.HMAC256("secret");
return JWT.create()
.withAudience("xxxxxxxxxx")
.withIssuer("https://cognito-idp.[リージョン].amazonaws.com/[ユーザープールID]")
.withClaim("token_use", "id")
.withExpiresAt(new Date())
.sign(algorithm);
RS256
無理やりダミーの署名を用意しましょうか。
KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
keygen.initialize(1024);
KeyPair keyPair = keygen.generateKeyPair();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey);
return JWT.create()
.withAudience("xxxxxxxxxx")
.withIssuer("https://cognito-idp.[リージョン].amazonaws.com/[ユーザープールID]")
.withClaim("token_use", "id")
.withExpiresAt(new Date())
.sign(algorithm);
まとめ
Cognito 認証で取得した ID トークンの JWT の中身を確認してみました。
メインはトークンの有効期限チェックになると思いますが、その他の情報も知っておくといいですね。
また ID トークンに見せかけた、ダミーの JWT の作成にもチャレンジしました。
純粋にトークンを作成したい場合や、テスト用のトークン作成用途として使えそうです。
JWT 形式の文字列は、利用用途が増えてきているので、どのような形式になっているのか理解しておきたいですね。