deno.land / x / jose@v5.2.4 / lib / jwt_claims_set.ts
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151import type { JWTPayload, JWTClaimVerificationOptions, JWEHeaderParameters, JWSHeaderParameters,} from '../types.d.ts'import { JWTClaimValidationFailed, JWTExpired, JWTInvalid } from '../util/errors.ts'import { decoder } from './buffer_utils.ts'import epoch from './epoch.ts'import secs from './secs.ts'import isObject from './is_object.ts'
const normalizeTyp = (value: string) => value.toLowerCase().replace(/^application\//, '')
const checkAudiencePresence = (audPayload: unknown, audOption: unknown[]) => { if (typeof audPayload === 'string') { return audOption.includes(audPayload) }
if (Array.isArray(audPayload)) { // Each principal intended to process the JWT MUST // identify itself with a value in the audience claim return audOption.some(Set.prototype.has.bind(new Set(audPayload))) }
return false}
export default ( protectedHeader: JWEHeaderParameters | JWSHeaderParameters, encodedPayload: Uint8Array, options: JWTClaimVerificationOptions = {},) => { const { typ } = options if ( typ && (typeof protectedHeader!.typ !== 'string' || normalizeTyp(protectedHeader!.typ) !== normalizeTyp(typ)) ) { throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', 'typ', 'check_failed') }
let payload!: { [propName: string]: unknown } try { payload = JSON.parse(decoder.decode(encodedPayload)) } catch { // }
if (!isObject(payload)) { throw new JWTInvalid('JWT Claims Set must be a top-level JSON object') }
const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options
const presenceCheck = [...requiredClaims]
if (maxTokenAge !== undefined) presenceCheck.push('iat') if (audience !== undefined) presenceCheck.push('aud') if (subject !== undefined) presenceCheck.push('sub') if (issuer !== undefined) presenceCheck.push('iss')
for (const claim of new Set(presenceCheck.reverse())) { if (!(claim in payload)) { throw new JWTClaimValidationFailed(`missing required "${claim}" claim`, claim, 'missing') } }
if (issuer && !(<unknown[]>(Array.isArray(issuer) ? issuer : [issuer])).includes(payload.iss!)) { throw new JWTClaimValidationFailed('unexpected "iss" claim value', 'iss', 'check_failed') }
if (subject && payload.sub !== subject) { throw new JWTClaimValidationFailed('unexpected "sub" claim value', 'sub', 'check_failed') }
if ( audience && !checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience) ) { throw new JWTClaimValidationFailed('unexpected "aud" claim value', 'aud', 'check_failed') }
let tolerance: number switch (typeof options.clockTolerance) { case 'string': tolerance = secs(options.clockTolerance) break case 'number': tolerance = options.clockTolerance break case 'undefined': tolerance = 0 break default: throw new TypeError('Invalid clockTolerance option type') }
const { currentDate } = options const now = epoch(currentDate || new Date())
if ((payload.iat !== undefined || maxTokenAge) && typeof payload.iat !== 'number') { throw new JWTClaimValidationFailed('"iat" claim must be a number', 'iat', 'invalid') }
if (payload.nbf !== undefined) { if (typeof payload.nbf !== 'number') { throw new JWTClaimValidationFailed('"nbf" claim must be a number', 'nbf', 'invalid') } if (payload.nbf > now + tolerance) { throw new JWTClaimValidationFailed( '"nbf" claim timestamp check failed', 'nbf', 'check_failed', ) } }
if (payload.exp !== undefined) { if (typeof payload.exp !== 'number') { throw new JWTClaimValidationFailed('"exp" claim must be a number', 'exp', 'invalid') } if (payload.exp <= now - tolerance) { throw new JWTExpired('"exp" claim timestamp check failed', 'exp', 'check_failed') } }
if (maxTokenAge) { const age = now - payload.iat! const max = typeof maxTokenAge === 'number' ? maxTokenAge : secs(maxTokenAge)
if (age - tolerance > max) { throw new JWTExpired( '"iat" claim timestamp check failed (too far in the past)', 'iat', 'check_failed', ) }
if (age < 0 - tolerance) { throw new JWTClaimValidationFailed( '"iat" claim timestamp check failed (it should be in the past)', 'iat', 'check_failed', ) } }
return <JWTPayload>payload}
Version Info