0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【TypeScript】ブラウザの標準機能だけでPKCE認証のためのコード生成は実装できる

Last updated at Posted at 2023-01-28

PKCE対応のためのcode_challenge生成をフロントエンドで行いたい

  • PKCE対応のためのコード生成をフロントエンド側で行う必要が出てきた
  • sha256のハッシュ化やランダムな文字列生成のためのライブラリは世にたくさんあるが、外部ライブラリを利用しなくとも実装は可能であると分かった
    • 具体的にはweb crypto APIというブラウザ内の機能を使う。その説明はこの記事が詳しい
  • 紛らわしいのは、node.jsにもcyrptoというAPIが存在すること
    • ここに私はひっかかった。node.jscryptoのAPIで実装を進めてしまっていた。node.jsなのでブラウザでは直接実行できない。実行するためにはBrowserifystreamifyというツールのインストールが必要になってしまう
    • Bufferを使ったエンコード例の記事がweb上には多い。しかし、Bufferはブラウザでは標準非対応である。ブラウザで動かしたいなら利用を避けたほうがいい
    • 下記の記事が、両ライブラリの違いについて詳しい
    • [Node.js][JavaScript]CryptoAPIの違いでハマったのでまとめ
  • ブラウザでも標準ライブラリだけで、PKCE認証のためのコード生成は行えることを、主要なメッセージとして本記事を投稿する

具体的なコード例

  • ここで私の実装例を書きたいところだが、実際はWouterSpaak氏のコード例をほとんど参考にした。WouterSpaakさん、本当にありがとうございます
  • コード例は大体下記のような感じである
challenge-code-service.ts
const private _code_verifier;

get code_verifier {
  return _code_verifier;
} 

//このメソッドをcode_challengeが欲しいときに呼び出す
createCodeChallengeAsync() {
  _code_verifier =  randomStr(43); //PKCEでのcode_verifierは43文字以上128文字以下である必要がある
  const shaBuffer = await sha256(code_verifier);
  const encoded = bufferToBase64UrlEncoded(shaBuffer);
  document.getElementById('encoded').textContent = encoded;
}

randomStr(len: number) {
  const arr = new Uint8Array(len);
  window.crypto.getRandomValues(arr);
  return String.fromCharCode(...toCharCodes(arr));
}

toCharCodes(arr: Uint8Array) {
  const validChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_~.';
  return arr.map(x => validChars.charCodeAt(x % validChars.length));
}

sha256 (message: string) {
  const encoder = new TextEncoder();
  const data = encoder.encode(message);
  return window.crypto.subtle.digest('SHA-256', data);
}

bufferToBase64UrlEncoded(input: ArrayBuffer) {
  const bytes = new Uint8Array(input);
  return urlEncodeBase64(window.btoa(String.fromCharCode(...bytes)));
}

urlEncodeBase64(input: string) {
  return input.replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/\=/g, '');
}
  • 工夫が必要なのは以下の機能である。すべてブラウザ搭載であるwindowおよびwindow.cryptoに紐づく関数を利用して実装できていることが確認できる
    • ランダムな文字列の生成
    • BASE64のエンコード
    • sha256によるハッシュ化
  • トークン取得のPOST時に、暗号化するまえの元の文字列であるcode_verifierが必要になる
    • こちらはメモリ上に持つ形として、ブラウザ内のストレージには保管しないこととした。破棄のタイミングの検討が必要になるため。加えて、この機能を開発したアプリケーション上、保持したcode_verifierを破棄するようなユーザー操作は想定されない。すぐにPOSTする操作が予想されるため、メモリ上に保持しておくことでも大きな問題は無いと判断している
  • BASE64でエンコードするために、作ったstring を一度ArrayBufferに戻す必要がある
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?