From 808bff91059056b7fb76f8c54693f87d36ec1375 Mon Sep 17 00:00:00 2001 From: robsonsilv4 Date: Sun, 24 Dec 2023 09:29:51 -0300 Subject: [PATCH 1/3] refactor: replaces DateTime.now() with clock.now() for testability --- lib/src/jwt.dart | 15 ++++++++------- pubspec.yaml | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/src/jwt.dart b/lib/src/jwt.dart index b23f2e8..2f607e8 100644 --- a/lib/src/jwt.dart +++ b/lib/src/jwt.dart @@ -2,12 +2,13 @@ import 'dart:collection'; import 'dart:convert'; import 'dart:typed_data'; +import 'package:clock/clock.dart'; import 'package:collection/collection.dart'; import 'algorithms.dart'; import 'exceptions.dart'; -import 'keys.dart'; import 'helpers.dart'; +import 'keys.dart'; class JWT { /// Verify a token. @@ -64,7 +65,7 @@ class JWT { final exp = DateTime.fromMillisecondsSinceEpoch( payload['exp'] * 1000, ); - if (exp.isBefore(DateTime.now())) { + if (exp.isBefore(clock.now())) { throw JWTExpiredException(); } } @@ -74,7 +75,7 @@ class JWT { final nbf = DateTime.fromMillisecondsSinceEpoch( payload['nbf'] * 1000, ); - if (nbf.isAfter(DateTime.now())) { + if (nbf.isAfter(clock.now())) { throw JWTNotActiveException(); } } @@ -87,7 +88,7 @@ class JWT { final iat = DateTime.fromMillisecondsSinceEpoch( payload['iat'] * 1000, ); - if (!iat.isAtSameMomentAs(DateTime.now())) { + if (!iat.isAtSameMomentAs(clock.now())) { throw JWTInvalidException('invalid issue at'); } } @@ -265,12 +266,12 @@ class JWT { try { payload = Map.from(payload); - if (!noIssueAt) payload['iat'] = secondsSinceEpoch(DateTime.now()); + if (!noIssueAt) payload['iat'] = secondsSinceEpoch(clock.now()); if (expiresIn != null) { - payload['exp'] = secondsSinceEpoch(DateTime.now().add(expiresIn)); + payload['exp'] = secondsSinceEpoch(clock.now().add(expiresIn)); } if (notBefore != null) { - payload['nbf'] = secondsSinceEpoch(DateTime.now().add(notBefore)); + payload['nbf'] = secondsSinceEpoch(clock.now().add(notBefore)); } if (audience != null) payload['aud'] = audience!.toJson(); if (subject != null) payload['sub'] = subject; diff --git a/pubspec.yaml b/pubspec.yaml index d609838..ca814bf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: convert: ^3.1.1 collection: ^1.17.1 ed25519_edwards: ^0.3.1 + clock: ^1.1.1 dev_dependencies: lints: ^2.1.1 From fa4369b86f8b8a77327b88327c07c8f0681ff999 Mon Sep 17 00:00:00 2001 From: robsonsilv4 Date: Sun, 24 Dec 2023 10:46:01 -0300 Subject: [PATCH 2/3] test: add tests for verify exp --- pubspec.yaml | 1 + test/verify_test.dart | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/pubspec.yaml b/pubspec.yaml index ca814bf..be72b9e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -20,5 +20,6 @@ dependencies: clock: ^1.1.1 dev_dependencies: + fake_async: ^1.3.1 lints: ^2.1.1 test: ^1.24.6 diff --git a/test/verify_test.dart b/test/verify_test.dart index 3c51331..7f10de1 100644 --- a/test/verify_test.dart +++ b/test/verify_test.dart @@ -1,6 +1,8 @@ import 'dart:convert'; +import 'package:clock/clock.dart'; import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart'; +import 'package:fake_async/fake_async.dart'; import 'package:test/test.dart'; final hsKey = SecretKey('secret passphrase'); @@ -196,5 +198,46 @@ void main() { expect(jwt?.payload, equals({'foo': 'bar'})); }); }); + + //-------// + // exp // + //-------// + group('.verify exp', () { + test('should be expired', () { + final duration = Duration(hours: 1); + final token = JWT({'foo': 'bar'}).sign(hsKey, expiresIn: duration); + fakeAsync((async) { + async.elapse(duration); + expect( + () => JWT.verify(token, hsKey), + throwsA(isA()), + ); + }); + }); + + test('should be still valid', () { + withClock( + Clock.fixed(DateTime(2023)), + () { + final token = JWT({'foo': 'bar'}).sign( + hsKey, + expiresIn: Duration(hours: 1), + ); + fakeAsync((async) { + async.elapse(Duration(minutes: 30)); + final jwt = JWT.verify(token, hsKey); + expect( + jwt.payload, + equals({ + 'foo': 'bar', + 'iat': 1672542000, + 'exp': 1672545600, + }), + ); + }); + }, + ); + }); + }); }); } From 441972d0f338057be057f24d42739707ffcf8ccf Mon Sep 17 00:00:00 2001 From: Jonas Roussel Date: Thu, 28 Dec 2023 13:21:41 +0100 Subject: [PATCH 3/3] Refactor JWT expiration tests --- test/header_test.dart | 50 +++++++++++++++++++++++++++++++++++++++++++ test/verify_test.dart | 43 ------------------------------------- 2 files changed, 50 insertions(+), 43 deletions(-) create mode 100644 test/header_test.dart diff --git a/test/header_test.dart b/test/header_test.dart new file mode 100644 index 0000000..dd85860 --- /dev/null +++ b/test/header_test.dart @@ -0,0 +1,50 @@ +import 'package:clock/clock.dart'; +import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart'; +import 'package:fake_async/fake_async.dart'; +import 'package:test/test.dart'; + +final hsKey = SecretKey('secret passphrase'); + +void main() { + group("JWT Header", () { + //--------------------// + // Expiration (exp) // + //--------------------// + group('exp', () { + test('should be expired', () { + final duration = Duration(hours: 1); + final token = JWT({'foo': 'bar'}).sign(hsKey, expiresIn: duration); + + fakeAsync((async) { + async.elapse(duration); + expect( + () => JWT.verify(token, hsKey), + throwsA(isA()), + ); + }); + }); + + test('should be still valid', () { + withClock( + Clock.fixed(DateTime(2023)), + () { + final duration = Duration(hours: 1); + final token = JWT({'foo': 'bar'}).sign(hsKey, expiresIn: duration); + + fakeAsync((async) { + async.elapse(Duration(minutes: 30)); + expect( + JWT.verify(token, hsKey).payload, + equals({ + 'foo': 'bar', + 'iat': 1672527600, + 'exp': 1672531200, + }), + ); + }); + }, + ); + }); + }); + }); +} diff --git a/test/verify_test.dart b/test/verify_test.dart index 7f10de1..3c51331 100644 --- a/test/verify_test.dart +++ b/test/verify_test.dart @@ -1,8 +1,6 @@ import 'dart:convert'; -import 'package:clock/clock.dart'; import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart'; -import 'package:fake_async/fake_async.dart'; import 'package:test/test.dart'; final hsKey = SecretKey('secret passphrase'); @@ -198,46 +196,5 @@ void main() { expect(jwt?.payload, equals({'foo': 'bar'})); }); }); - - //-------// - // exp // - //-------// - group('.verify exp', () { - test('should be expired', () { - final duration = Duration(hours: 1); - final token = JWT({'foo': 'bar'}).sign(hsKey, expiresIn: duration); - fakeAsync((async) { - async.elapse(duration); - expect( - () => JWT.verify(token, hsKey), - throwsA(isA()), - ); - }); - }); - - test('should be still valid', () { - withClock( - Clock.fixed(DateTime(2023)), - () { - final token = JWT({'foo': 'bar'}).sign( - hsKey, - expiresIn: Duration(hours: 1), - ); - fakeAsync((async) { - async.elapse(Duration(minutes: 30)); - final jwt = JWT.verify(token, hsKey); - expect( - jwt.payload, - equals({ - 'foo': 'bar', - 'iat': 1672542000, - 'exp': 1672545600, - }), - ); - }); - }, - ); - }); - }); }); }