Node.js で JSON Web Token (JWT) の ID トークンを発行し、検証する処理のメモです。

JWT のメリットはアクセストークンそのものが有効期間やコンテクストを保持できるため、個別にリクエストごとにセッションIDからセッション情報を引くという機構を省くことができます。そのため、自前の API サーバにおいて、アクセストークンとして JWT の ID トークンを使えるとサーバ処理を省力化できます。

今回の例では、JWT の検証をグローバルにできるようにはせず、サーバ内でのみ行うこととします。一般的な JWT トークンは JSON Web Key (JWK) Set から提供されており、JWT の署名検証ができるようになっていますが、それは行いません。 

依存ライブラリ

Auth0 がものすごく便利なライブラリを提供してくれています。

https://github.com/auth0/node-jsonwebtoken

$ npm i -save jsonwebtoken
$ npm i -save-dev @types/jsonwebtoken

鍵の生成

公開鍵と秘密鍵を別々にすることも可能ですが、分けると管理が面倒なのと同一サーバ内で処理するため、PEMファイルで管理します。

$ openssl genrsa > auth.pem

発行と検証処理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import fs from 'fs'
import jwt from 'jsonwebtoken'

const PEM = fs.readFileSync('auth.pem')

function issueToken(subject: string): string {
  const opts: jwt.SignOptions = {
    algorithm: 'RS256',
    issuer: 'https://issuer.example.com',
    expiresIn: '1d', // https://github.com/zeit/ms
  }

  const payload = {
    sub: subject,
  }

  return jwt.sign(payload, PEM, opts)
}

export async function verifyToken(idToken: string): Promise<object> {
  const opts: jwt.VerifyOptions = {
    algorithms: ['RS256'],
    issuer: 'https://issuer.example.com',
    maxAge: '1d', // https://github.com/zeit/ms
  }

  return new Promise((resolve, reject) => {
    jwt.verify(idToken, PEM, opts, (err: Error, decoded: any) => {
      if (err) {
        reject(err)
      } else {
        resolve(decoded)
      }
    })
  })
}

// 実行確認
;(async () => {
  const idToken = issueToken('userId')
  const result = await verifyToken(idToken)
  console.log(result)
})()
  • JWT 署名時の expiresIn より時間が経過している場合は、検証時に TokenExpiredError: jwt expired がスローされます。
  • 時間内であっても発行時よりも maxAge が経過している場合は、検証時に TokenExpiredError: maxAge exceeded がスローされます。

実行結果

1
2
3
4
5
6
{
  sub: 'userId',
  iat: 1582639938,
  exp: 1582726338,
  iss: 'https://issuer.example.com'
}

まとめ

ログイン成功時に issueToken を呼んで ID トークンをクライアントに発行します。クライアントは API リクエストの Authorization ヘッダに付与して、サーバ側は verifyToken で検証することで、認証および subject(sub)フィールドなどからユーザー情報を取得することができます。