deno.land / x / jose@v5.2.4 / lib / jwt_claims_set.ts

jwt_claims_set.ts
نووسراو ببینە
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import 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}
jose

Version Info

Tagged at
a month ago