verify function

Future<Map<String, dynamic>> verify(
  1. String token,
  2. String secret, [
  3. String alg = 'HS256'
])

Verifies token against secret using alg, validates standard time-based claims (exp, nbf, iat), and returns the decoded payload.

Throws JwtException when the signature is invalid, the token is expired, or any other claim check fails.

try {
  final payload = await verify(token, secret);
  print(payload['sub']);
} on JwtException catch (e) {
  print(e.message); // 'Token expired', 'Invalid signature', etc.
}

Implementation

Future<Map<String, dynamic>> verify(
  String token,
  String secret, [
  String alg = 'HS256',
]) async {
  final parts = token.split('.');
  if (parts.length != 3) throw const JwtException('Malformed token');

  final hmac = _hmacFor(alg, utf8.encode(secret));
  if (hmac == null) throw JwtException('Unsupported algorithm: $alg');

  final expected =
      _b64url(hmac.convert(utf8.encode('${parts[0]}.${parts[1]}')).bytes);
  if (parts[2] != expected) throw const JwtException('Invalid signature');

  final Map<String, dynamic> claims;
  try {
    claims = Map<String, dynamic>.from(
      jsonDecode(utf8.decode(_b64urlDecode(parts[1]))) as Map,
    );
  } catch (_) {
    throw const JwtException('Malformed payload');
  }

  final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;

  if (claims['exp'] case final int exp) {
    if (now > exp) throw const JwtException('Token expired');
  }

  if (claims['nbf'] case final int nbf) {
    if (now < nbf) throw const JwtException('Token not yet valid');
  }

  if (claims['iat'] case final int iat) {
    if (iat > now) throw const JwtException('Token issued in the future');
  }

  return claims;
}