Lightweight JWT token decoder with typed claim access and expiration checking
dart pub add philiprehberger_jwt_decoderLightweight JWT token decoder with typed claim access and expiration checking
Add to your pubspec.yaml:
dependencies:
philiprehberger_jwt_decoder: ^0.6.0
Then run:
dart pub get
import 'package:philiprehberger_jwt_decoder/jwt_decoder.dart';
final payload = JwtDecoder.decode(token);
print(payload.subject); // user-123
print(payload.expiration); // 2026-04-04 12:00:00.000
if (JwtDecoder.isExpired(token)) {
print('Token has expired');
}
final remaining = JwtDecoder.timeToExpiry(token);
print('Expires in ${remaining?.inMinutes} minutes');
if (JwtDecoder.isValid(token)) {
print('Token has valid JWT structure');
}
JwtDecoder.isExpired(token, clockSkew: Duration(seconds: 30));
import 'package:philiprehberger_jwt_decoder/philiprehberger_jwt_decoder.dart';
final header = JwtDecoder.decodeHeader(token);
print(header['alg']); // "HS256"
print(header['typ']); // "JWT"
final alg = JwtDecoder.algorithm(token); // "HS256"
// Returns null instead of throwing on malformed tokens
final payload = JwtDecoder.tryDecode(token);
if (payload != null) {
print(payload.subject);
}
final header = JwtDecoder.tryDecodeHeader(token);
final payload = JwtDecoder.decode(token);
final role = payload.claim<String>('role'); // 'admin'
final level = payload.claim<int>('level'); // 5
final payload = JwtDecoder.decode(token);
// Returns the claim value if present and typed, otherwise the default.
final role = payload.claimOr<String>('role', 'guest');
final level = payload.claimOr<int>('level', 0);
final payload = JwtDecoder.decode(token);
// Pick a subset of claims; missing keys are skipped (not inserted as null).
final subset = payload.pickClaims(['sub', 'role', 'tenant']);
// {'sub': 'user-123', 'role': 'admin'}
final payload = JwtDecoder.decode(token);
// Always returns a list, whether aud is a string or array
final audiences = payload.audienceList; // ['app-1', 'app-2']
if (JwtDecoder.isNotYetValid(token)) {
print('Token is not yet valid');
}
// With clock skew tolerance
JwtDecoder.isNotYetValid(token, clockSkew: Duration(seconds: 30));
| Method | Description |
|---|---|
JwtDecoder.decode(token) | Decode a JWT and return its payload |
JwtDecoder.tryDecode(token) | Decode a JWT, or return null if malformed |
JwtDecoder.decodeHeader(token) | Decode the JWT header and return the claims map |
JwtDecoder.tryDecodeHeader(token) | Decode the JWT header, or return null if malformed |
JwtDecoder.algorithm(token) | Get the alg claim from the header |
JwtDecoder.isValid(token) | Check if a token has valid JWT structure |
JwtDecoder.isExpired(token, {clockSkew}) | Check if a token has expired |
JwtDecoder.timeToExpiry(token) | Get remaining time until expiration |
JwtPayload.subject | The sub claim |
JwtPayload.issuer | The iss claim |
JwtPayload.audience | The aud claim |
JwtPayload.jwtId | The jti claim |
JwtPayload.issuedAt | The iat claim as DateTime |
JwtPayload.expiration | The exp claim as DateTime |
JwtPayload.notBefore | The nbf claim as DateTime |
JwtPayload.audienceList | The aud claim as a list (handles both string and array) |
JwtDecoder.isNotYetValid(token, {clockSkew}) | Check if a token's nbf claim is in the future |
JwtPayload.claim<T>(key) | Get any custom claim by key |
JwtPayload.claimOr<T>(key, defaultValue) | Get a typed custom claim, or defaultValue if absent or mistyped |
JwtPayload.pickClaims(keys) | Get a subset map containing only the requested keys that are present |
dart pub get
dart analyze --fatal-infos
dart test
If you find this project useful: