JWT Decode Dart
Library
JWT Decode Dart
Overview
JWT Decode Dart is a lightweight library that provides JSON Web Token (JWT) decoding functionality for Dart applications.
Details
jwt_decode is a lightweight library designed to simplify authentication token processing in Flutter/Dart applications. It provides key features including JWT token decoding, expiration checking, and claim information extraction. This library focuses solely on decoding without token verification, requiring additional validation steps for security-critical applications. Published on pub.dev with Dart 3 compatibility, it works across all platforms including iOS, Android, Web, and desktop. An alternative library called jwt_decoder also exists with more download history, but both provide similar functionality.
Pros and Cons
Pros
- Lightweight Design: Fast JWT decoding with minimal dependencies
- Cross-Platform: Full support for iOS, Android, Web, and desktop
- Simple API: Intuitive methods for easy JWT data access
- Expiration Check: Automatic token expiration detection
- Claim Extraction: Access to any claim information in the payload
- Flutter Integration: High compatibility with Flutter applications
Cons
- No Verification: Token signature verification requires separate implementation
- Limited Security: Decoding only with minimal security features
- Error Handling: Exception handling required for invalid tokens
- Documentation: Limited functionality with minimal documentation
- Dependencies: Requires additional JWT verification libraries
Main Links
- pub.dev (jwt_decode)
- GitHub (jwt_decode)
- pub.dev (jwt_decoder)
- Flutter JWT Authentication Guide
- JWT.io
- Dart Official Site
Code Examples
Basic JWT Decoding
import 'package:jwt_decode/jwt_decode.dart';
void main() {
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
// Decode JWT
Map<String, dynamic> payload = Jwt.parseJwt(token);
print('User ID: ${payload['sub']}');
print('Email: ${payload['email']}');
print('Role: ${payload['role']}');
}
Checking Token Expiration
import 'package:jwt_decode/jwt_decode.dart';
bool isTokenValid(String token) {
try {
// Check expiration
bool isExpired = Jwt.isExpired(token);
return !isExpired;
} catch (e) {
print('Invalid token: $e');
return false;
}
}
void checkToken() {
String token = "your-jwt-token-here";
if (isTokenValid(token)) {
print('Token is valid');
Map<String, dynamic> payload = Jwt.parseJwt(token);
print('User: ${payload['name']}');
} else {
print('Token is expired or invalid');
}
}
Authentication Service Class in Flutter
import 'package:jwt_decode/jwt_decode.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AuthService {
static const String _tokenKey = 'jwt_token';
// Save token
Future<void> saveToken(String token) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_tokenKey, token);
}
// Get and validate stored token
Future<Map<String, dynamic>?> getCurrentUser() async {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString(_tokenKey);
if (token == null) return null;
try {
// Check expiration
if (Jwt.isExpired(token)) {
await logout(); // Logout if expired
return null;
}
// Get user information
final payload = Jwt.parseJwt(token);
return {
'id': payload['sub'],
'email': payload['email'],
'name': payload['name'],
'role': payload['role'],
};
} catch (e) {
print('Token parsing error: $e');
await logout();
return null;
}
}
// Logout
Future<void> logout() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_tokenKey);
}
// Admin permission check
Future<bool> isAdmin() async {
final user = await getCurrentUser();
return user?['role'] == 'admin';
}
}
Error Handling and Validation
import 'package:jwt_decode/jwt_decode.dart';
class JwtUtil {
static Map<String, dynamic>? safeParseJwt(String token) {
try {
return Jwt.parseJwt(token);
} catch (e) {
print('JWT parsing failed: $e');
return null;
}
}
static bool isValidJwt(String token) {
if (token.isEmpty) return false;
// Check basic JWT format (3 parts separated by dots)
final parts = token.split('.');
if (parts.length != 3) return false;
try {
// Check if decodable
Jwt.parseJwt(token);
return true;
} catch (e) {
return false;
}
}
static DateTime? getExpirationDate(String token) {
try {
final payload = Jwt.parseJwt(token);
final exp = payload['exp'] as int?;
if (exp != null) {
return DateTime.fromMillisecondsSinceEpoch(exp * 1000);
}
} catch (e) {
print('Error getting expiration date: $e');
}
return null;
}
static Duration? getTimeUntilExpiration(String token) {
final expDate = getExpirationDate(token);
if (expDate != null) {
return expDate.difference(DateTime.now());
}
return null;
}
}
Role-Based Routing (Flutter)
import 'package:flutter/material.dart';
import 'package:jwt_decode/jwt_decode.dart';
class AuthGuard extends StatelessWidget {
final Widget child;
final String? requiredRole;
final String? token;
final Widget fallback;
const AuthGuard({
Key? key,
required this.child,
this.requiredRole,
this.token,
this.fallback = const Text('Access Denied'),
}) : super(key: key);
@override
Widget build(BuildContext context) {
if (token == null || token!.isEmpty) {
return fallback;
}
try {
// Check token expiration
if (Jwt.isExpired(token!)) {
return const Text('Token Expired');
}
// Check required permissions
if (requiredRole != null) {
final payload = Jwt.parseJwt(token!);
final userRole = payload['role'] as String?;
if (userRole != requiredRole) {
return fallback;
}
}
return child;
} catch (e) {
return Text('Authentication Error: $e');
}
}
}
// Usage example
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: AuthGuard(
token: "your-jwt-token",
requiredRole: "admin",
child: AdminDashboard(),
fallback: LoginScreen(),
),
);
}
}
Real-time Token Monitoring
import 'dart:async';
import 'package:jwt_decode/jwt_decode.dart';
class TokenMonitor {
Timer? _expirationTimer;
final Function() onTokenExpired;
TokenMonitor({required this.onTokenExpired});
void startMonitoring(String token) {
stopMonitoring();
try {
final payload = Jwt.parseJwt(token);
final exp = payload['exp'] as int?;
if (exp != null) {
final expirationDate = DateTime.fromMillisecondsSinceEpoch(exp * 1000);
final timeUntilExpiration = expirationDate.difference(DateTime.now());
if (timeUntilExpiration.isNegative) {
// Already expired
onTokenExpired();
} else {
// Execute callback just before expiration
_expirationTimer = Timer(timeUntilExpiration, onTokenExpired);
}
}
} catch (e) {
print('Error monitoring token: $e');
onTokenExpired();
}
}
void stopMonitoring() {
_expirationTimer?.cancel();
_expirationTimer = null;
}
void dispose() {
stopMonitoring();
}
}