verify static method
Verify a token.
key
must be
- SecretKey with HMAC algorithm
- RSAPublicKey with RSA algorithm
- ECPublicKey with ECDSA algorithm
- EdDSAPublicKey with EdDSA algorithm
issueAt
allows to verify that the token wasn't issued too long ago. The
value is a timestamp (number of seconds since epoch) in UTC if
issueAtUtc
is true, it is compared to the value of the 'iat' claim.
Verification fails if the 'iat' claim is before issueAt
.
Implementation
static JWT verify(
String token,
JWTKey key, {
bool checkHeaderType = true,
bool checkExpiresIn = true,
bool checkNotBefore = true,
Duration? issueAt,
bool issueAtUtc = true,
Audience? audience,
String? subject,
String? issuer,
String? jwtId,
}) {
try {
final parts = token.split('.');
final header = jsonBase64.decode(base64Padded(parts[0]));
if (header == null || header is! Map<String, dynamic>) {
throw JWTInvalidException('invalid header');
}
if (checkHeaderType && header['typ'] != 'JWT') {
throw JWTInvalidException('not a jwt');
}
final algorithm = JWTAlgorithm.fromName(header['alg']);
final body = utf8.encode(parts[0] + '.' + parts[1]);
final signature = base64Url.decode(base64Padded(parts[2]));
if (!algorithm.verify(key, Uint8List.fromList(body), signature)) {
throw JWTInvalidException('invalid signature');
}
dynamic payload;
try {
payload = jsonBase64.decode(base64Padded(parts[1]));
} catch (ex) {
payload = utf8.decode(base64Url.decode(base64Padded(parts[1])));
}
if (payload is Map) {
// exp
if (checkExpiresIn && payload.containsKey('exp')) {
final exp = DateTime.fromMillisecondsSinceEpoch(
(payload['exp'] * 1000).toInt(),
isUtc: true,
);
if (exp.isBefore(timeNowUTC())) {
throw JWTExpiredException();
}
}
// nbf
if (checkNotBefore && payload.containsKey('nbf')) {
final nbf = DateTime.fromMillisecondsSinceEpoch(
(payload['nbf'] * 1000).toInt(),
isUtc: true,
);
if (nbf.isAfter(timeNowUTC())) {
throw JWTNotActiveException();
}
}
// iat
if (issueAt != null) {
if (!payload.containsKey('iat')) {
throw JWTInvalidException('invalid issue at');
}
final iat = DateTime.fromMillisecondsSinceEpoch(
(payload['iat'] * 1000).toInt(),
isUtc: true,
);
final issueAtTime = DateTime.fromMillisecondsSinceEpoch(
issueAt.inMilliseconds,
isUtc: issueAtUtc,
);
// Verify that the token isn't expired
if (iat.isBefore(issueAtTime)) {
throw JWTInvalidException('expired issue at');
}
}
// aud
if (audience != null) {
if (payload.containsKey('aud')) {
if (payload['aud'] is String && payload['aud'] != audience.first) {
throw JWTInvalidException('invalid audience');
} else if (payload['aud'] is List &&
!ListEquality().equals(payload['aud'], audience)) {
throw JWTInvalidException('invalid audience');
}
} else {
throw JWTInvalidException('invalid audience');
}
}
// sub
if (subject != null) {
if (!payload.containsKey('sub') || payload['sub'] != subject) {
throw JWTInvalidException('invalid subject');
}
}
// iss
if (issuer != null) {
if (!payload.containsKey('iss') || payload['iss'] != issuer) {
throw JWTInvalidException('invalid issuer');
}
}
// jti
if (jwtId != null) {
if (!payload.containsKey('jti') || payload['jti'] != jwtId) {
throw JWTInvalidException('invalid jwt id');
}
}
return JWT(
payload,
header: header,
audience: _parseAud(payload['aud']),
issuer: payload['iss']?.toString(),
subject: payload['sub']?.toString(),
jwtId: payload['jti']?.toString(),
);
} else {
return JWT(payload);
}
} catch (ex, stackTrace) {
if (ex is Exception && ex is! JWTException) {
throw JWTUndefinedException(ex, stackTrace);
} else {
rethrow;
}
}
}