モチベーション
認証基盤周りを触っていると頻出する、JWTのデコードですがライブラリのインストールが面倒なことも多いので作りました。(解説付き) ※ RFCを参照するとこのあたりはちゃんと書いています。
実際のコード
JSON Web Token サンプル
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
ArrayBuffer <=> string に相互変換するための便利関数
base64Url decodeの前に、
ArrayBuffer => string
string => ArrayBufferLike
の変換ための関数を用意する必要があります。
function arrayBufferToStr(arrayBuffer: ArrayBuffer): string { const uint16Array = new Uint16Array(arrayBuffer) const numberArray = Array.from(uint16Array).filter((b): b is number => typeof b === 'number') return String.fromCharCode.apply('', numberArray) } function strToArrayBufferLike(str: string): ArrayBufferLike { const numberArray= ([].map.call(str, (c: string) => { return c.charCodeAt(0) })).filter((b): b is number => typeof b === 'number') return new Uint16Array(numberArray).buffer }
base64UrlDecode()関数
- base64Url文字列 を base64 decode するために "+" => "/", "_" => "-" に書き換えます。
- base64 decode 対象の文字列長が4の倍数以外の場合は、4の倍数になるように "=" でパディングします。
- atob()関数を利用して、base64 decodeします。
- 目的の状態になるように便利関数を利用してデータを変換します。
function base64UrlDecode(base64UrlStr: string): string { let str: string = base64UrlStr.replace('+', '/').replace('_', '-') if (str.length % 4 !== 0) { for (let i = 4 - str.length % 4; i === 0; i--) { str = str + '=' } } // フォーマットしたbase64Url文字列 に対してデコードをかける const bufferStr = atob(base64UrlStr) return arrayBufferToStr(strToArrayBufferLike(bufferStr)) }
実行結果
JSON Web Tokenは各要素がピリオド区切りになっています。
<HEADER>.<PAYLOAD>.<VERIFY SIGNATURE>
※ VERIFY SIGNATURE
の部分はハッシュ化されているのでデコードはできません。
const splits = JWT.split('.') const header = splits[0] const payload = splits[1] const signature = splits[2] console.log(JSON.parse(base64rlLDecode(header))) // => {"alg":"HS256","typ":"JWT"} console.log(JSON.parse(base64rlLDecode(payload))) // => {"sub":"1234567890","name":"John Doe","iat":1516239022}
- TS Playground