こんにちは、富士榮です。
前回までで登録がある程度実装できたので、細かいところを実装する前にログイン側の処理を実装してみましょう。
その前に、これまでのポストはこちらです。
- パスキーの実装をし始めてみる
- パスキー登録APIのオプションを読み解く
- さまざまな認証器でパスキー登録APIの返却値を確認する
- パスキー登録APIのレスポンスを解析する
- パスキーの登録レスポンスの検証を行う
- パスキーの登録を行う
- 登録済みのパスキーを削除する(MacOS)
登録に比べてログインは割とシンプルです。
- 登録と同じ様にサーバ側で生成したchallengeを含むoptionを生成する
- optionを指定してnavigator.credential.get()を実行する
- 認証器をアクティブ化し、navigator.credential.get()の返却値から取得できるCredentialIDをキーに登録済みの公開鍵を取得する
- 取得した公開鍵を使い同じくnavigator.credential.get()の返却地に含まれるデジタル署名等の検証を行い、成功したら認証OKとする
こんな流れです。
では実装していきましょう。
Challengeの取得
登録の時と同じですね。
// ログインする
async function loginWithPasskey(userId, userVerification) {
// challengeを取得する(後で使うのでサーバサイドで生成する)
const challenge = await _fetch(
'/passkey/getChallenge',
'GET',
{
'X-Requested-With': 'XMLHttpRequest'
}
);
// バイナリを扱うためにサーバ・クライアント間ではbase64urlエンコードした値でやり取りする
const encodedChallenge = await challenge.text();
const decodedChallenge = await b64decode(encodedChallenge);
サーバ側で生成してbase64urlエンコードされたchallengeをクライアント側でデコードしてArrayBufferの形にしてあげます。
optionパラメータを生成する
取得したchallengeを含むoptionパラメータを生成します。ユーザ認証を認証器側に求めるかどうかのスイッチもここで設定できます。
// パスキーログインのためのパラメータを生成する
const options = {
challenge: decodedChallenge,
allowCredentials: [],
userVerification: userVerification
}
navigator.credential.get()を実行する
生成したoptionを指定してAPIを実行します。
const cred = await navigator.credentials.get({
publicKey: options,
mediation: 'optional'
});
画面にはパスキーダイアログが表示されます。
結果、返却されるPublicKeyCredentialの中身を見ると今度は特徴的なプロパティとして
- userHandle
- signature
が入っていることがわかります。
このuserHandleはパスキー登録時に指定したユーザIDなので、credentialIdをキーにデータベースを検索して公開鍵を取得、このユーザIDに対して発行されたものなのかどうかの確認する、という流れになります。
JSONBinの中のcredentialIdが一致しているものを探してPublicKeyを取得します。
あとは検証の部分ですので、次回解説していきたいと思います。
0 件のコメント:
コメントを投稿