@@ -58,11 +58,13 @@ async function verifyJWT() {
58
58
if ( jwt_verification_type === 'hmac' ) {
59
59
isValid = await verifyHMAC ( jwt_ , jwt_verification_key , header . alg ) ;
60
60
} else if ( jwt_verification_type === 'x509' ) {
61
- isValid = await verifyRSJWT ( jwt_ , jwt_verification_key , header . alg ) ;
61
+ isValid = await verifyX509 ( jwt_ , jwt_verification_key , header . alg ) ;
62
62
} else if ( jwt_verification_type === 'jwks' ) {
63
- isValid = await verifyWithJWKS ( jwt_ , JSON . parse ( jwt_verification_key ) ) ;
63
+ isValid = await verifyJWKS ( jwt_ , JSON . parse ( jwt_verification_key ) ) ;
64
64
} else if ( jwt_verification_type === 'jwks_url' ) {
65
- isValid = await verifyWithJWKS ( jwt_ , await fetchJWKS ( jwt_verification_key ) ) ;
65
+ const response = await fetch ( jwt_verification_key ) ;
66
+ if ( ! response . ok ) throw new Error ( 'Failed to fetch JWKS.' ) ;
67
+ isValid = await verifyJWKS ( jwt_ , await response . json ( ) ) ;
66
68
} else {
67
69
throw new Error ( 'Unsupported verification method.' ) ;
68
70
}
@@ -75,24 +77,35 @@ async function verifyJWT() {
75
77
76
78
function atobUrl ( input ) {
77
79
input = input . replace ( / - / g, '+' ) . replace ( / _ / g, '/' ) ;
78
- const pad = input . length % 4 ? '=' . repeat ( 4 - ( input . length % 4 ) ) : '' ;
80
+ const pad = '===' . slice ( 0 , ( 4 - input . length % 4 ) % 4 ) ;
79
81
return atob ( input + pad ) ;
80
82
}
81
83
82
84
function base64UrlToUint8Array ( base64UrlString ) {
83
85
const binary = atobUrl ( base64UrlString ) ;
84
- const len = binary . length ;
85
- const bytes = new Uint8Array ( len ) ;
86
- for ( let i = 0 ; i < len ; i ++ ) {
86
+ const bytes = new Uint8Array ( binary . length ) ;
87
+
88
+ for ( let i = 0 ; i < binary . length ; i ++ ) {
87
89
bytes [ i ] = binary . charCodeAt ( i ) ;
88
90
}
91
+
89
92
return bytes ;
90
93
}
91
94
92
- async function verifyHMAC ( jwt , secret , alg = 'HS256' ) {
95
+ function pemToArrayBuffer ( pem ) {
96
+ const binary = atob ( pem . replace ( / - - - - - [ ^ - ] + - - - - - / g, '' ) . replace ( / \s + / g, '' ) ) ;
97
+ const buffer = new Uint8Array ( binary . length ) ;
98
+
99
+ for ( let i = 0 ; i < binary . length ; i ++ ) {
100
+ buffer [ i ] = binary . charCodeAt ( i ) ;
101
+ }
102
+
103
+ return buffer . buffer ;
104
+ }
105
+
106
+ async function verifyHMAC ( jwt_ , secret , alg = 'HS256' ) {
93
107
const encoder = new TextEncoder ( ) ;
94
- const algoMap = { HS256 : 'SHA-256' , HS384 : 'SHA-384' , HS512 : 'SHA-512' } ;
95
- const algo = algoMap [ alg ] ;
108
+ const algo = { HS256 : 'SHA-256' , HS384 : 'SHA-384' , HS512 : 'SHA-512' } [ alg ] ;
96
109
if ( ! algo ) throw new Error ( 'Unsupported HMAC algorithm: ' + alg ) ;
97
110
98
111
const key = await crypto . subtle . importKey (
@@ -102,97 +115,53 @@ async function verifyHMAC(jwt, secret, alg = 'HS256') {
102
115
false ,
103
116
[ 'verify' ]
104
117
) ;
105
-
106
- const data = encoder . encode ( jwt . split ( '.' ) . slice ( 0 , 2 ) . join ( '.' ) ) ;
107
- const signature = base64UrlToUint8Array ( jwt . split ( '.' ) [ 2 ] ) ;
118
+ const data = encoder . encode ( jwt_ . split ( '.' ) . slice ( 0 , 2 ) . join ( '.' ) ) ;
119
+ const signature = base64UrlToUint8Array ( jwt_ . split ( '.' ) [ 2 ] ) ;
108
120
109
121
return await crypto . subtle . verify ( 'HMAC' , key , signature , data ) ;
110
122
}
111
123
112
- async function verifyRSJWT ( jwt , pem , alg = 'RS256' ) {
113
- const algoMap = {
114
- RS256 : 'SHA-256' ,
115
- RS384 : 'SHA-384' ,
116
- RS512 : 'SHA-512'
117
- } ;
118
- const algo = algoMap [ alg ] ;
124
+ async function verifyX509 ( jwt_ , pem , alg = 'RS256' ) {
125
+ const encoder = new TextEncoder ( ) ;
126
+ const algo = { RS256 : 'SHA-256' , RS384 : 'SHA-384' , RS512 : 'SHA-512' } [ alg ] ;
119
127
if ( ! algo ) throw new Error ( 'Unsupported RSA algorithm: ' + alg ) ;
120
128
121
- const keyData = pemToArrayBuffer ( pem ) ;
122
129
const key = await crypto . subtle . importKey (
123
130
'spki' ,
124
- keyData ,
131
+ pemToArrayBuffer ( pem ) ,
125
132
{ name : 'RSASSA-PKCS1-v1_5' , hash : { name : algo } } ,
126
133
false ,
127
134
[ 'verify' ]
128
135
) ;
129
-
130
- const encoder = new TextEncoder ( ) ;
131
- const data = encoder . encode ( jwt . split ( '.' ) . slice ( 0 , 2 ) . join ( '.' ) ) ;
132
- const signature = base64UrlToUint8Array ( jwt . split ( '.' ) [ 2 ] ) ;
136
+ const data = encoder . encode ( jwt_ . split ( '.' ) . slice ( 0 , 2 ) . join ( '.' ) ) ;
137
+ const signature = base64UrlToUint8Array ( jwt_ . split ( '.' ) [ 2 ] ) ;
133
138
134
139
return await crypto . subtle . verify ( 'RSASSA-PKCS1-v1_5' , key , signature , data ) ;
135
140
}
136
141
137
- function pemToArrayBuffer ( pem ) {
138
- const b64Lines = pem . replace ( / - - - - - [ ^ - ] + - - - - - / g, '' ) . replace ( / \s + / g, '' ) ;
139
- const binary = atob ( b64Lines ) ;
140
- const len = binary . length ;
141
- const buffer = new ArrayBuffer ( len ) ;
142
- const view = new Uint8Array ( buffer ) ;
143
- for ( let i = 0 ; i < len ; i ++ ) {
144
- view [ i ] = binary . charCodeAt ( i ) ;
145
- }
146
- return buffer ;
147
- }
148
-
149
- async function verifyWithJWKS ( jwt , jwks ) {
150
- const header = JSON . parse ( atobUrl ( jwt . split ( '.' ) [ 0 ] ) ) ;
151
- const kid = header . kid ;
152
- if ( ! kid ) throw new Error ( 'No "kid" found in JWT header.' ) ;
142
+ async function verifyJWKS ( jwt_ , jwks ) {
143
+ const header = JSON . parse ( atobUrl ( jwt_ . split ( '.' ) [ 0 ] ) ) ;
144
+ if ( ! header . kid ) throw new Error ( 'No "kid" found in JWT header.' ) ;
153
145
154
- const jwk = jwks . keys . find ( k => k . kid === kid ) ;
146
+ const jwk = jwks . keys . find ( k => k . kid === header . kid ) ;
155
147
if ( ! jwk ) throw new Error ( 'Matching "kid" not found in JWKS.' ) ;
148
+ if ( jwk . kty !== 'RSA' ) throw new Error ( 'Only RSA keys are supported.' ) ;
156
149
157
- const algoMap = {
158
- RS256 : 'SHA-256' ,
159
- RS384 : 'SHA-384' ,
160
- RS512 : 'SHA-512'
161
- } ;
162
-
163
- const algo = algoMap [ header . alg ] ;
164
- if ( ! algo ) throw new Error ( 'Unsupported algorithm: ' + header . alg ) ;
165
-
166
- const key = await importJWK ( jwk , algo ) ;
167
150
const encoder = new TextEncoder ( ) ;
168
- const data = encoder . encode ( jwt . split ( '.' ) . slice ( 0 , 2 ) . join ( '.' ) ) ;
169
- const signature = base64UrlToUint8Array ( jwt . split ( '.' ) [ 2 ] ) ;
170
-
171
- return await crypto . subtle . verify ( 'RSASSA-PKCS1-v1_5' , key , signature , data ) ;
172
- }
173
-
174
- async function fetchJWKS ( url ) {
175
- const response = await fetch ( url ) ;
176
- if ( ! response . ok ) throw new Error ( 'Failed to fetch JWKS.' ) ;
177
- return await response . json ( ) ;
178
- }
179
-
180
- async function importJWK ( jwk , hashAlgo ) {
181
- if ( jwk . kty !== 'RSA' ) throw new Error ( 'Only RSA keys are supported in this example.' ) ;
182
-
183
- const publicKey = {
184
- kty : jwk . kty ,
185
- n : jwk . n ,
186
- e : jwk . e
187
- } ;
151
+ const algo = { RS256 : 'SHA-256' , RS384 : 'SHA-384' , RS512 : 'SHA-512' } [ header . alg ] ;
152
+ if ( ! algo ) throw new Error ( 'Unsupported algorithm: ' + header . alg ) ;
188
153
189
- return await crypto . subtle . importKey (
154
+ const key = await crypto . subtle . importKey (
190
155
'jwk' ,
191
- publicKey ,
192
- { name : 'RSASSA-PKCS1-v1_5' , hash : { name : hashAlgo } } ,
156
+ { kty : jwk . kty , n : jwk . n , e : jwk . e } ,
157
+ { name : 'RSASSA-PKCS1-v1_5' , hash : { name : algo } } ,
193
158
false ,
194
159
[ 'verify' ]
195
160
) ;
161
+ const data = encoder . encode ( jwt_ . split ( '.' ) . slice ( 0 , 2 ) . join ( '.' ) ) ;
162
+ const signature = base64UrlToUint8Array ( jwt_ . split ( '.' ) [ 2 ] ) ;
163
+
164
+ return await crypto . subtle . verify ( 'RSASSA-PKCS1-v1_5' , key , signature , data ) ;
196
165
}
197
166
198
167
$ ( document ) . on ( "change" , "#jwt_verification_type" , function ( ) {
0 commit comments