この記事の目次
chevron_right
JWTとは?
chevron_right
参考
chevron_right
JWKとは?
chevron_right
参考
chevron_right
JWKとJWTを使った認証の流れ
chevron_right
実践
chevron_right
フロントエンド
chevron_right
必要モジュールのインストール
chevron_right
コード
chevron_right
バックエンド
chevron_right
必要ライブラリのインストール
chevron_right
コード
JWTとは?
いろんな情報を3つの要素に分けて、BASE64エンコードして、ドット(.)でつないだ文字列。
参考
JWKとは?
JWTを作るときに暗号化をした場合、その暗号化に用いられたアルゴリズムや公開鍵情報が書かれたJSONファイル
参考
JWKとJWTを使った認証の流れ
AWSのCognitoを使った場合の流れです。
CognitoはJWTを「idToken」という名前で表しています。
- JavaScriptを用いてCognitoの認証ページを叩く
- Cognitoが認証を行う
- idTokenをCognitoが発行し、リダイレクト先のページにリダイレクト。このときidTokenがブラウザのlocal storageに書き込まれる
- idTokenをPHPに送信
- PHPがidTokenを受け取り、JWKを使ってidTokenが正しいかをチェック
という流れになります。
実践
フロントエンド
あんまりフロントは得意じゃないので、間違っていたらすいません。
(一応動作は確認済み)
必要モジュールのインストール
$ npm install aws-amplify
コード
import { Auth } from 'aws-amplify'
// Congnitoにアクセスするための情報
const conf = {
userPoolId: 'AWSが発行するuserPoolId',
userPoolWebClientId: 'AWSが発行するuserPoolWebClientId',
region: 'AWSのリージョン',
oauth: {
domain: 'AWS Cognitoのドメイン',
scope: ['openid'],
redirectSignIn: 'CognitoにアクセスするページのURL(http://localhost:3000など)',
redirectSignOut: 'サインアウト時にリダイレクトされるページのURL',
responseType: 'code',
}
};
// 認証を行う場合
const checkFlg = window.confirm('認証しますか?');
if (checkFlg) {
// 上で定義したコンフィグを設定
Auth.configure(conf);
// SAML認証を使ってSingIN(これが実行されると、Cognitoが発行する認証ページにリダイレクトされる)
Auth.federatedSignIn();
}
// Cognito認証後にリダイレクトされた場合(window.confirm('認証しますか?');でNOを選択した場合)、
// これを再度実行することにより、localstorageにidToken情報が書き込まれる
Auth.configure(conf);
バックエンド
必要ライブラリのインストール
$ composer require firebase/php-jwt
$ composer require phpseclib/phpseclib
コード
$jwks = json_decode(file_get_contents('JWKSのURL'), true);
// JSON Web Tokenをデコードしてヘッダーのkidを見つけます
$tks = explode('.', self::ID_TOKEN);
if (count($tks) !== 3) {
throw new Exception('JWTのフォーマットがおかしいです');
}
$jwtHeader = $tks[0];
$jwt_header = json_decode(JWT::urlsafeB64Decode($jwtHeader), true);
if (empty($jwt_header["kid"])) {
throw new Exception("JSON Web Tokenにkidがありません");
}
// JSON Web Keysをデコードしてjwtのkidと合致するjwkから公開鍵を取得します
$publicKey = "";
foreach ($jwks["keys"] as $jwk) {
if ($jwk["kid"] == $jwt_header["kid"]) {
// 公開鍵取得
$publicKey = $this->createPubKey($jwk);
break;
}
}
if ( !$publicKey ) {
throw new Exception("公開鍵が取得出来ません");
}
// cognitoが発行したidTokenが正しいものかを公開鍵を使って検証する
$key = new Key($publicKey, 'RS256');
$decoded = JWT::decode(self::ID_TOKEN, $key);
// ここでエクセプションが出なければ使っていいidToken
// ログイン情報などはpayloadの中にいるので、そこから取り出す
$payload = json_decode(JWT::urlsafeB64Decode($tks[1]), true);
var_dump($payload);
die;
/**
* 公開鍵を作成する関数
*/
private function createPubKey(array $jwk): string
{
return PublicKeyLoader::load([
'e' => new BigInteger(base64_decode($jwk['e']), 256),
'n' => new BigInteger(base64_decode(strtr($jwk['n'], '-_', '+/'), true), 256)
])->toString('PKCS8');
}
この記事を書いた人
Nな人(えぬなひと)。
Nは本名から取っています。
Laravelが大好きなPHPerで、WEBを作るときはLaravelを技術スタックに絶対推すマン。
PHP、Pythonと、昔はperlを書いていたP言語エンジニア。
最近はNimを書いたりしています。
Nは本名から取っています。
Laravelが大好きなPHPerで、WEBを作るときはLaravelを技術スタックに絶対推すマン。
PHP、Pythonと、昔はperlを書いていたP言語エンジニア。
最近はNimを書いたりしています。