はじめに
なんか丁度いい記事がなかなか見つからなかったので書いてみました。
クライアントがAzure Active DirectoryからJWTを取得して、それを Authorization: Bearer
に付与してリクエストしてくるケースにおいて、受け側のAPIでJWTのVefiryをしたい
これをどうやるのが良さそうか・・・と色々調べたんですが、ジャストフィットするようなサンプルが見つからなかったので。
callbackつかって非同期処理してるやつが多いんですよね。それだと色々とやりにくいんで。
結論
自分はこんな感じで実装しました。なにかの参考になれば。
Nuxt3の server/api/token.get.ts
として保存してますので、エンドポイント的には GET localhost:3000/api/token
になります。
エラー判定や型は若干(というかかなり)適当です。あくまで主目的が開発ベンダーさんに依頼するためのサンプルだったというのもあり。
import * as jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa'
export default defineEventHandler(async (event) => {
const req = event.node.req
const authHeaderValue = req.headers['authorization']
if (!authHeaderValue) {
console.log('No authorization header specified')
return
}
var token,
tokenHeaderBase64Encoded,
tokenHeader,
kid : string
try {
// Authorization: Bearer [JWT]となるので、Bearer部分を除外
token = authHeaderValue.split(' ')[1]
console.log(`token: ${token}`)
// Header部(.つなぎの最初)を抽出
tokenHeaderBase64Encoded = token.split('.')[0]
console.log(`tokenHeaderBase64Encoded: ${tokenHeaderBase64Encoded}`)
// Base64デコードしてkid部を抜き出す
tokenHeader = Buffer.from(tokenHeaderBase64Encoded, 'base64').toString()
console.log(`tokenHeader: ${tokenHeader}`)
kid = JSON.parse(tokenHeader).kid
console.log(`kid: ${kid}`)
} catch (error) {
throw createError({
statusCode: 401,
statusMessage: 'Token structure is not valid',
message: error.message || ''
})
}
// 取得したkid部からKey情報を取りに行く
const client = jwksClient({
jwksUri: 'https://login.microsoftonline.com/common/discovery/keys'
})
var signingKey: string
try {
const key = await client.getSigningKey(kid)
console.log(`key: ${JSON.stringify(key)}`)
signingKey = key.publicKey || key.rsaPublicKey;
console.log(`signingKey: ${signingKey}`)
} catch (error) {
throw createError({
statusCode: 401,
statusMessage: 'Unable to get correct key information from JWT',
message: error.message || ''
})
}
const config = useRuntimeConfig()
console.log(`Runtime Config: ${JSON.stringify(config)}`)
const verifyOptions = {
audience: `api://${config.clientId}`
// audience: 'invalid audience' // for test
}
console.log(`Verify Options: ${JSON.stringify(verifyOptions)}`)
// Verifyメソッドを同期型で呼ぶことでdecode結果直接受領
try {
const decoded = jwt.verify(token, signingKey, verifyOptions)
console.log(`Decoded JWT: (${JSON.stringify(decoded)}`)
return {
message: 'JWT has correctly verified'
}
} catch (error) {
throw createError({
statusCode: 401,
statusMessage: 'JWT is not valid',
message: error.message || ''
})
}
})