からんころんと技術探求

エンジニアの絵空事

ライブラリを利用せずにTypeScriptでJWT(JSON Web Token)をデコードする

モチベーション

認証基盤周りを触っていると頻出する、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()関数

  1. base64Url文字列 を base64 decode するために "+" => "/", "_" => "-" に書き換えます。
  2. base64 decode 対象の文字列長が4の倍数以外の場合は、4の倍数になるように "=" でパディングします。
  3. atob()関数を利用して、base64 decodeします。
  4. 目的の状態になるように便利関数を利用してデータを変換します。
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

www.typescriptlang.org