@@ -2,7 +2,6 @@ import 'dart:convert';
22
33import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart' ;
44import 'package:http/http.dart' as http;
5- import 'package:jose/jose.dart' ;
65import 'package:meta/meta.dart' ;
76
87const algorithmRS256 = 'RS256' ;
@@ -41,19 +40,19 @@ abstract class SignatureVerifier {
4140}
4241
4342abstract class KeyFetcher {
44- Future <JsonWebKeyStore > fetchPublicKeys ();
43+ Future <Map < String , JWTKey > > fetchPublicKeys ();
4544}
4645
4746class UrlKeyFetcher implements KeyFetcher {
4847 UrlKeyFetcher (this .clientCert);
4948
5049 final Uri clientCert;
5150
52- JsonWebKeyStore ? _publicKeys;
51+ Map < String , JWTKey > ? _publicKeys;
5352 late DateTime _publicKeysExpireAt;
5453
5554 @override
56- Future <JsonWebKeyStore > fetchPublicKeys () async {
55+ Future <Map < String , JWTKey > > fetchPublicKeys () async {
5756 if (_shouldRefresh ()) return refresh ();
5857 return _publicKeys! ;
5958 }
@@ -63,7 +62,7 @@ class UrlKeyFetcher implements KeyFetcher {
6362 return _publicKeysExpireAt.isBefore (DateTime .now ());
6463 }
6564
66- Future <JsonWebKeyStore > refresh () async {
65+ Future <Map < String , JWTKey > > refresh () async {
6766 final response = await http.get (clientCert);
6867 final json = jsonDecode (response.body) as Map <String , Object ?>;
6968 final error = json['error' ];
@@ -91,26 +90,27 @@ class UrlKeyFetcher implements KeyFetcher {
9190 }
9291 }
9392
94- final store = _publicKeys = JsonWebKeyStore () ;
93+ final keys = < String , JWTKey > {} ;
9594
9695 for (final entry in json.entries) {
97- final key = JsonWebKey .fromPem (entry.value! as String , keyId: entry.key);
98- store.addKey (key);
96+ final pem = entry.value! as String ;
97+ // Google X.509 certs are RSA
98+ keys[entry.key] = RSAPublicKey .cert (pem);
9999 }
100100
101- return store ;
101+ return _publicKeys = keys ;
102102 }
103103}
104104
105105class JwksFetcher implements KeyFetcher {
106106 JwksFetcher (this .jwksUrl);
107107 final Uri jwksUrl;
108- JsonWebKeyStore ? _publicKeys;
108+ Map < String , JWTKey > ? _publicKeys;
109109 int _publicKeysExpireAt = 0 ;
110110 static const int hourInMilliseconds = 6 * 60 * 60 * 1000 ; // 6 hours
111111
112112 @override
113- Future <JsonWebKeyStore > fetchPublicKeys () async {
113+ Future <Map < String , JWTKey > > fetchPublicKeys () async {
114114 if (_shouldRefresh) return refresh ();
115115
116116 return _publicKeys! ;
@@ -121,27 +121,36 @@ class JwksFetcher implements KeyFetcher {
121121 _publicKeysExpireAt <= DateTime .now ().millisecondsSinceEpoch;
122122 }
123123
124- Future <JsonWebKeyStore > refresh () async {
124+ Future <Map < String , JWTKey > > refresh () async {
125125 final response = await http.get (jwksUrl);
126126 if (response.statusCode != 200 ) {
127127 throw Exception ('Failed to fetch JWKS' );
128128 }
129129
130130 final jwks = jsonDecode (response.body) as Map <String , dynamic >;
131- final keys = JsonWebKeySet .fromJson (jwks).keys;
131+ final jwkList = jwks['keys' ] as List <dynamic >? ;
132+
133+ if (jwkList == null ) {
134+ throw Exception ('Invalid JWKS: missing "keys" array' );
135+ }
132136
133137 // Reset expire time
134138 _publicKeysExpireAt = 0 ;
135139
136- // Extract signing keys
137- final store = _publicKeys = JsonWebKeyStore ();
138- keys.forEach (store.addKey);
140+ final newKeys = < String , JWTKey > {};
141+ for (final jwkJson in jwkList) {
142+ final jwk = jwkJson as Map <String , dynamic >;
143+ final kid = jwk['kid' ] as String ? ;
144+ if (kid != null ) {
145+ newKeys[kid] = JWTKey .fromJWK (jwk);
146+ }
147+ }
139148
140149 // Set new expiration time
141150 _publicKeysExpireAt =
142151 DateTime .now ().millisecondsSinceEpoch + hourInMilliseconds;
143152
144- return store ;
153+ return _publicKeys = newKeys ;
145154 }
146155}
147156
@@ -175,10 +184,15 @@ class PublicKeySignatureVerifier implements SignatureVerifier {
175184 );
176185 }
177186
178- final store = await keyFetcher.fetchPublicKeys ();
187+ final keys = await keyFetcher.fetchPublicKeys ();
188+ final key = keys[kid];
189+
190+ if (key == null ) {
191+ throw JwtException (JwtErrorCode .noMatchingKid, 'no-matching-kid-error' );
192+ }
179193
180194 try {
181- await JsonWebToken . decodeAndVerify (token, store );
195+ JWT . verify (token, key );
182196 } catch (e, stackTrace) {
183197 Error .throwWithStackTrace (
184198 JwtException (
@@ -194,7 +208,9 @@ class PublicKeySignatureVerifier implements SignatureVerifier {
194208 } on JWTException catch (e) {
195209 throw JwtException (
196210 JwtErrorCode .unknown,
197- e is JWTUndefinedException ? e.message : '${e .runtimeType }: e.message' ,
211+ e is JWTUndefinedException
212+ ? e.message
213+ : '${e .runtimeType }: ${e .message }' ,
198214 );
199215 }
200216 }
0 commit comments