@veloze/jwt
Version:
74 lines (61 loc) • 2.14 kB
JavaScript
import * as jose from 'jose'
import { HttpError } from 'veloze/src/HttpError.js'
import { decodeJwt } from './decodeJwt.js'
/**
* @typedef {import('veloze').Request} Request
* @typedef {import('veloze').Response} Response
* @typedef {import('veloze').Handler} Handler
* @typedef {import('jose').JWTVerifyOptions} JWTVerifyOptions
* @typedef {import('jose').CryptoKey} CryptoKey
* @typedef {import('./decodeJwt.js').DecodedJWT} DecodedJWT
*
* @typedef {object & JWTVerifyOptions} JwtOptions
* @property {string|Buffer|KeyLike|GetKeyLikeFn} secret
* @property {string} [requestProperty='auth']
*
* @typedef {(decodedToken: DecodedJWT) => Promise<CryptoKey>} GetKeyLikeFn
*/
/**
* @param {JwtOptions} options
* @returns {Handler}
*/
export function jwtAuth(options) {
const { secret, requestProperty = 'auth', ...verifyOptions } = options || {}
if (!secret) throw new TypeError('need secret')
const _secret =
typeof secret === 'string' ? new TextEncoder().encode(secret) : secret
const getKey = typeof secret === 'function' ? secret : async () => _secret
return async function _jwtAuth(req, _res) {
const { authorization } = req.headers
if (!authorization) {
throw new HttpError(401)
}
const [type, token] = String(authorization).split(' ', 2)
if (type.toLowerCase() !== 'bearer') {
throw new HttpError(401, 'Bearer token authorization expected')
}
if (!token) {
throw new HttpError(401, 'No bearer token found')
}
/** @type {DecodedJWT} */
let decodedToken
let key
try {
decodedToken = decodeJwt(token)
key = await getKey(decodedToken)
} catch (/** @type {Error|any} */ err) {
throw new HttpError(401, 'Invalid Token', err)
}
if (!key) {
throw new HttpError(401)
}
try {
await jose.jwtVerify(token, key, verifyOptions)
} catch (/** @type {Error|any} */ err) {
throw new HttpError(401, 'Invalid Token', err)
}
req[requestProperty] = decodedToken.payload
await immediate()
}
}
const immediate = () => new Promise((resolve) => setImmediate(() => resolve(0)))