JWT
認証

【JWT】 入門


JWTとは


  • 公式サイト


  • JSON Web Tokenの略  

  • 電子署名により、改ざん検知できる。

  • 認証用のトークンなどで用いられる。


構成



  • ヘッダペイロード署名の3つから成る。

  • それぞれは、Base64でエンコードされている

  • それぞれは、 . (ドット) で結合されている。


JWTの構文

[ヘッダ].

[ペイロード].
[署名]


実際のJWT

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
t42p4AHef69Tyyi88U6+p0utZYYrg7mmCGhoAd7Zffs


作り方

 1. アルゴリズムTokenのタイプを、ヘッダに設定

アルゴリズム
説明

HMAC SHA-256
256ビットのハッシュ値を生成する関数


ヘッダ

{

"alg": "HS256",
"typ": "JWT"
}

 2. ヘッダをBase64でエンコード


ヘッダをBase64でエンコード

echo -n '{"alg":"HS256","typ":"JWT"}' | base64



Base64エンコードされたヘッダ

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9


 3. ペイロードを設定

クレーム名
説明

issued at
JWT を発行した時刻

subject
JWT の主語となる主体の識別子


ペイロード

{

"sub": "1234567890",
"iat": 1516239022
}

 4. ペイロードをBase64でエンコード


ペイロードをBase64でエンコード

echo -n '{"sub":"1234567890","iat":1516239022}' | base64



Base64エンコードされたヘッダ

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ


 5. ヘッダとペイロードを . (ドット) で結合し、署名なしTokenを生成


署名なしトークン

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ

 6. 署名なしTokenに対し、秘密鍵とHMAC-SHA256を用いて署名を生成


署名なしTokenに、HMAC-SHA256を適応

echo -n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ' | \

openssl dgst -binary -sha256 -hmac 'secret' | \
base64


署名

t42p4AHef69Tyyi88U6+p0utZYYrg7mmCGhoAd7Zffs


 7. 署名なしTokenと署名を . (ドット) で結合


JWT

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
t42p4AHef69Tyyi88U6+p0utZYYrg7mmCGhoAd7Zffs


実装

JWTの生成と検証を、Javascriptで実装する。


前提条件

秘密鍵の値を、secretとする


JWTの生成


JWTの生成

const crypto = require('crypto')

const base64 = json => {
const jsonStr = JSON.stringify(json)
const jsonB64 = Buffer.from(jsonStr).toString('base64')
const jsonB64NoPadding = jsonB64.replace(/={1,2}$/, '')
return jsonB64NoPadding
}

const HMAC_SHA256 = (key, data) => {
const hash = crypto.createHmac('sha256', key).update(data).digest('base64')
const hashNoPadding = hash.replace(/={1,2}$/, '')
return hashNoPadding
}

const header = { alg: 'HS256', typ: 'JWT' }
const payload = { sub: '1234567890', iat:1516239022 }
const key = 'secret'
const unsignedToken = `${base64(header)}.${base64(payload)}`
const signature = HMAC_SHA256(key, unsignedToken)
const jwt = `${unsignedToken}.${signature}`

console.log(jwt)



出力結果

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
t42p4AHef69Tyyi88U6+p0utZYYrg7mmCGhoAd7Zffs


  • JSONをBase64エンコーディング

    alt


  • HMAC-SHA256より、JWTを生成


alt


JWTの検証


JWTの検証

const crypto = require('crypto')

const HMAC_SHA256 = (key, data) => {
const hash = crypto.createHmac('sha256', key).update(data).digest('base64')
const hashNoPadding = hash.replace(/={1,2}$/, '')
return hashNoPadding
}

const key = 'secret'
const jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.t42p4AHef69Tyyi88U6+p0utZYYrg7mmCGhoAd7Zffs'

const splits = jwt.split('.')
const unsignedToken = [splits[0], splits[1]].join('.')
const signature = splits[2]

console.log(HMAC_SHA256(key, unsignedToken) === signature)



出力結果

true


alt


認証におけるJWTの利用

新規登録/ログインより、ユーザはJWTを取得する。

APIを利用する際は、JWTを付与してリクエストを行う。

例として、JWTを利用し、ユーザ照会APIにリクエストを行う。


前提条件


  • ペイロードは、ユーザIDと有効期限の情報をもつ


ペイロード

{

"user_id": "1234567890",
"iat": 1516239022
}


  • APサーバは、以下のAPIを提供している。

Method
URL
Parameter
Response
説明

GET
/user
Authorization
ユーザ名を返す
ユーザ情報を返す

Method
URL
Body
Response
説明

POST
/register
id, password
Tokenを返す
ユーザ登録を行う

POST
/login
id, password
Tokenを返す
ログインを行う


新規登録する場合


  1. ユーザ登録

    name=hoge, password=hogeとし、 /registerにPOSTリクエストする。

    ② APサーバは、DBに対し、新規登録をリクエストする。

    ③ DBは、ユニークなIDを設定しつつ、name, passwordの登録を行う。

    ④ DBは、APサーバに対し、ユーザIDを返す。

    ⑤ APサーバは、ユーザIDと秘密鍵より、Tokenを生成する。

    ⑥ APサーバは、ユーザにTokenを返す。

    alt


  2. ユーザ情報をリクエスト

    AuthorizationにJWTを設定し、 /userにGETリクエストする。

    ② APサーバは、秘密鍵を用いてTokenを検証する。

    ③ APサーバは、TokenからユーザIDを取得する。

    ④ APサーバは、DBに対し、IDに対するユーザ情報をリクエストする。

    ⑤ DBは、IDに対するユーザ情報を返す。

    ⑥ APサーバは、ユーザにユーザ情報を返す。

    alt



ログインする場合


  1. ログイン

    name=hoge, password=hogeとし、 /loginにPOSTリクエストする。

    ② APサーバは、DBに対し、ユーザの確認をリクエストする。

    ④ DBは、APサーバに対し、対応するユーザIDを返す。

    ⑤ APサーバは、ユーザIDと秘密鍵より、Tokenを生成する。

    ⑥ APサーバは、ユーザにTokenを返す。

    alt


  2. ユーザ情報をリクエスト

    AuthorizationにJWTを設定し、 /userにGETリクエストする。

    ② APサーバは、秘密鍵を用いてTokenを検証する。

    ③ APサーバは、TokenからユーザIDを取得する。

    ④ APサーバは、DBに対し、IDに対するユーザ情報をリクエストする。

    ⑤ DBは、IDに対するユーザ情報を返す。

    ⑥ APサーバは、ユーザにユーザ情報を返す。

    alt



まとめ

JWTを実装しました。実装することで、理解が深まると思います。

また、認証におけるJWTの利用方法についてまとめました。

間違い・指摘等があればコメントお願いします。


参考文献