This commit is contained in:
Jonas Roussel
2021-04-13 18:45:19 +02:00
parent 97950a5d9c
commit 2fcea79c95
4 changed files with 84 additions and 50 deletions

View File

@ -1,3 +1,8 @@
## 2.1.0
- When an undefined error occur `JWTUndefinedError` is thrown containing the original error in `error` property (https://github.com/jonasroussel/jsonwebtoken/issues/9)
- **BREAKING CHANGE**: `jwt.verify` no longer support `throwUndefinedErrors` parameter
## 2.0.1 ## 2.0.1
- Fixing `JWT.sign` to include `iat` & other attributes when payload is an empty Map - Fixing `JWT.sign` to include `iat` & other attributes when payload is an empty Map

View File

@ -12,19 +12,41 @@ class JWTError extends Error {
/// An error thrown when jwt is invalid /// An error thrown when jwt is invalid
class JWTInvalidError extends JWTError { class JWTInvalidError extends JWTError {
JWTInvalidError(String message) : super(message); JWTInvalidError(String message) : super(message);
@override
String toString() => 'JWTInvalidError: $message';
} }
/// An error thrown when jwt is expired /// An error thrown when jwt is expired
class JWTExpiredError extends JWTError { class JWTExpiredError extends JWTError {
JWTExpiredError() : super('jwt expired'); JWTExpiredError() : super('jwt expired');
@override
String toString() => 'JWTExpiredError: $message';
} }
/// An error thrown when jwt is not active /// An error thrown when jwt is not active
class JWTNotActiveError extends JWTError { class JWTNotActiveError extends JWTError {
JWTNotActiveError() : super('jwt not active'); JWTNotActiveError() : super('jwt not active');
@override
String toString() => 'JWTNotActiveError: $message';
} }
/// An error thrown when parsing failed /// An error thrown when parsing failed
class JWTParseError extends JWTError { class JWTParseError extends JWTError {
JWTParseError(String message) : super(message); JWTParseError(String message) : super(message);
@override
String toString() => 'JWTParseError: $message';
}
/// An error thrown by default
class JWTUndefinedError extends JWTError {
JWTUndefinedError(this.error) : super(error.toString());
final Error error;
@override
String toString() => 'JWTUndefinedError: $message';
} }

View File

@ -20,7 +20,6 @@ class JWT {
bool checkHeaderType = true, bool checkHeaderType = true,
bool checkExpiresIn = true, bool checkExpiresIn = true,
bool checkNotBefore = true, bool checkNotBefore = true,
bool throwUndefinedErrors = false,
Duration? issueAt, Duration? issueAt,
String? audience, String? audience,
String? subject, String? subject,
@ -129,10 +128,10 @@ class JWT {
return JWT(payload); return JWT(payload);
} }
} catch (ex) { } catch (ex) {
if (throwUndefinedErrors) { if (ex is Error) {
rethrow; throw JWTUndefinedError(ex);
} else { } else {
throw JWTInvalidError('invalid token'); rethrow;
} }
} }
} }
@ -175,56 +174,64 @@ class JWT {
Duration? notBefore, Duration? notBefore,
bool noIssueAt = false, bool noIssueAt = false,
}) { }) {
final header = {'alg': algorithm.name, 'typ': 'JWT'}; try {
final header = {'alg': algorithm.name, 'typ': 'JWT'};
if (payload is Map<String, dynamic> || payload is Map<dynamic, dynamic>) { if (payload is Map<String, dynamic> || payload is Map<dynamic, dynamic>) {
try {
payload = Map<String, dynamic>.from(payload);
if (!noIssueAt) payload['iat'] = secondsSinceEpoch(DateTime.now());
if (expiresIn != null) {
payload['exp'] = secondsSinceEpoch(DateTime.now().add(expiresIn));
}
if (notBefore != null) {
payload['nbf'] = secondsSinceEpoch(DateTime.now().add(notBefore));
}
if (audience != null) payload['aud'] = audience;
if (subject != null) payload['sub'] = subject;
if (issuer != null) payload['iss'] = issuer;
if (jwtId != null) payload['jti'] = jwtId;
} catch (ex) {
assert(
payload is Map<String, dynamic>,
'If payload is a Map its must be a Map<String, dynamic>',
);
}
}
final b64Header = base64Unpadded(jsonBase64.encode(header));
String b64Payload;
try { try {
payload = Map<String, dynamic>.from(payload); b64Payload = base64Unpadded(
payload is String
if (!noIssueAt) payload['iat'] = secondsSinceEpoch(DateTime.now()); ? base64.encode(utf8.encode(payload))
if (expiresIn != null) { : jsonBase64.encode(payload),
payload['exp'] = secondsSinceEpoch(DateTime.now().add(expiresIn)); );
}
if (notBefore != null) {
payload['nbf'] = secondsSinceEpoch(DateTime.now().add(notBefore));
}
if (audience != null) payload['aud'] = audience;
if (subject != null) payload['sub'] = subject;
if (issuer != null) payload['iss'] = issuer;
if (jwtId != null) payload['jti'] = jwtId;
} catch (ex) { } catch (ex) {
assert( throw JWTError(
payload is Map<String, dynamic>, 'invalid payload json format (Map keys must be String type)',
'If payload is a Map its must be a Map<String, dynamic>',
); );
} }
}
final b64Header = base64Unpadded(jsonBase64.encode(header)); final body = '$b64Header.$b64Payload';
final signature = base64Unpadded(
String b64Payload; base64Url.encode(
try { algorithm.sign(
b64Payload = base64Unpadded( key,
payload is String Uint8List.fromList(utf8.encode(body)),
? base64.encode(utf8.encode(payload)) ),
: jsonBase64.encode(payload),
);
} catch (ex) {
throw JWTError(
'invalid payload json format (Map keys must be String type)',
);
}
final body = '$b64Header.$b64Payload';
final signature = base64Unpadded(
base64Url.encode(
algorithm.sign(
key,
Uint8List.fromList(utf8.encode(body)),
), ),
), );
);
return body + '.' + signature; return body + '.' + signature;
} catch (ex) {
if (ex is Error) {
throw JWTUndefinedError(ex);
} else {
rethrow;
}
}
} }
} }

View File

@ -1,6 +1,6 @@
name: dart_jsonwebtoken name: dart_jsonwebtoken
description: A dart implementation of the famous javascript library 'jsonwebtoken' (JWT). description: A dart implementation of the famous javascript library 'jsonwebtoken' (JWT).
version: 2.0.1 version: 2.1.0
repository: https://github.com/jonasroussel/jsonwebtoken repository: https://github.com/jonasroussel/jsonwebtoken
homepage: https://github.com/jonasroussel/jsonwebtoken#readme homepage: https://github.com/jonasroussel/jsonwebtoken#readme
@ -8,9 +8,9 @@ environment:
sdk: '>=2.12.0 <3.0.0' sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
crypto: ^3.0.0 crypto: ^3.0.1
pointycastle: ^3.0.1 pointycastle: ^3.0.1
ed25519_edwards: ^0.1.0 ed25519_edwards: ^0.1.0
dev_dependencies: dev_dependencies:
pedantic: ^1.9.2 pedantic: ^1.11.0