@@ -20,6 +20,9 @@ class JWT {
2020 /// value is a timestamp (number of seconds since epoch) in UTC if
2121 /// [issueAtUtc] is true, it is compared to the value of the 'iat' claim.
2222 /// Verification fails if the 'iat' claim is before [issueAt] .
23+ ///
24+ /// If the embedded `payload` is not a JSON map (but rather just a plain string),
25+ /// none of the verifications are executed. In that case only the signature is verified.
2326 static JWT verify (
2427 String token,
2528 JWTKey key, {
@@ -35,6 +38,13 @@ class JWT {
3538 }) {
3639 try {
3740 final parts = token.split ('.' );
41+
42+ if (parts.length != 3 ) {
43+ throw JWTInvalidException (
44+ 'token does not use JWS Compact Serialization' ,
45+ );
46+ }
47+
3848 final header = jsonBase64.decode (base64Padded (parts[0 ]));
3949
4050 if (header == null || header is ! Map <String , dynamic >) {
@@ -54,10 +64,11 @@ class JWT {
5464 throw JWTInvalidException ('invalid signature' );
5565 }
5666
57- dynamic payload;
67+ Object payload;
5868
5969 try {
60- payload = jsonBase64.decode (base64Padded (parts[1 ]));
70+ payload =
71+ jsonBase64.decode (base64Padded (parts[1 ])) as Map <String , dynamic >;
6172 } catch (ex) {
6273 payload = utf8.decode (base64Url.decode (base64Padded (parts[1 ])));
6374 }
@@ -194,18 +205,15 @@ class JWT {
194205 ///
195206 /// This also sets [JWT.audience] , [JWT.subject] , [JWT.issuer] , and
196207 /// [JWT.jwtId] even though they are not verified. Use with caution.
208+ ///
209+ /// This methods only supports map payloads. For `String` payloads use `verify` .
197210 static JWT decode (String token) {
198211 try {
199212 final parts = token.split ('.' );
200- var header = jsonBase64.decode (base64Padded (parts[0 ]));
201-
202- dynamic payload;
213+ final header = jsonBase64.decode (base64Padded (parts[0 ]));
203214
204- try {
205- payload = jsonBase64.decode (base64Padded (parts[1 ]));
206- } catch (ex) {
207- payload = utf8.decode (base64Url.decode (base64Padded (parts[1 ])));
208- }
215+ final payload =
216+ (jsonBase64.decode (base64Padded (parts[1 ])) as Map <String , dynamic >);
209217
210218 final audiance = _parseAud (payload['aud' ]);
211219 final issuer = payload['iss' ]? .toString ();
@@ -240,16 +248,36 @@ class JWT {
240248
241249 /// JSON Web Token
242250 JWT (
243- this . payload, {
251+ Object payload, {
244252 this .audience,
245253 this .subject,
246254 this .issuer,
247255 this .jwtId,
248256 this .header,
249- });
257+ }) {
258+ this .payload = payload;
259+ }
260+
261+ late Object _payload;
250262
251- /// Custom claims
252- dynamic payload;
263+ /// The token's payload, either as a `Map<String, dynamic>` or plain `String`
264+ /// (in case it was not a JSON-encoded map).
265+ ///
266+ /// If it's a map, it has all claims, containing the utilized registered claims
267+ /// as well custom ones added.
268+ Object get payload => _payload;
269+
270+ void set payload (Object value) {
271+ if (value is String ) {
272+ _payload = value;
273+ } else if (value is Map ) {
274+ _payload = Map <String , dynamic >.from (value);
275+ } else {
276+ throw Exception (
277+ 'Unexpected `payload` type `${value .runtimeType }`, must be either `String` or `Map<String, *>`' ,
278+ );
279+ }
280+ }
253281
254282 /// Audience claim
255283 Audience ? audience;
@@ -281,7 +309,8 @@ class JWT {
281309 bool noIssueAt = false ,
282310 }) {
283311 try {
284- if (payload is Map <String , dynamic > || payload is Map <dynamic , dynamic >) {
312+ var payload = this .payload;
313+ if (payload is Map <String , dynamic >) {
285314 try {
286315 payload = Map <String , dynamic >.from (payload);
287316
0 commit comments