Add testable date times (#50)

* refactor: replaces DateTime.now() with clock.now() for testability

* test: add tests for verify exp

* Refactor JWT expiration tests

---------

Co-authored-by: Jonas Roussel <go.jroussel@gmail.com>
This commit is contained in:
Robson Silva
2023-12-28 20:57:59 -03:00
committed by GitHub
parent 881fe9af60
commit aab2ace88f
3 changed files with 60 additions and 7 deletions

View File

@ -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<String, dynamic>.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;

View File

@ -17,7 +17,9 @@ dependencies:
convert: ^3.1.1
collection: ^1.17.1
ed25519_edwards: ^0.3.1
clock: ^1.1.1
dev_dependencies:
fake_async: ^1.3.1
lints: ^2.1.1
test: ^1.24.6

50
test/header_test.dart Normal file
View File

@ -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<JWTExpiredException>()),
);
});
});
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,
}),
);
});
},
);
});
});
});
}