JWT Decode Dart

AuthenticationJWTDartSecurityTokenDecode

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

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();
  }
}