Authentication

Khadem provides a comprehensive authentication system with support for JWT and token-based authentication. The system is built with security, flexibility, and ease of use in mind.

Multi-Driver Support

JWT, Token, and extensible custom drivers

Secure by Default

Built-in security features and best practices

Flexible Configuration

Guard-based authentication with multiple providers

AuthManager - Main Authentication Interface

The AuthManager provides a unified interface for all authentication operations. It supports multiple guards and drivers for flexible authentication scenarios.
dart

import 'package:khadem/khadem_dart.dart';

class AuthController {
  final AuthManager auth;

  AuthController.singleton({String? guard})
      : auth = AuthManager(guard: guard);

  static AuthController instance = AuthController.singleton();
  factory AuthController() => instance;

  Future<Map<String, dynamic>> login(Request req, Response res) async {
    final data = await req.validate({
      'email': 'required|email',
      'password': 'required|min:6',
    });

    final payload = await auth.login(data);

    return {
      'message': 'Login successful',
      'data': {
        'token': payload['token'],
        'user': payload['user'],
      }
    };
  }

  Future<Map<String, dynamic>> register(Request req, Response res) async {
    final data = await req.validate({
      'name': 'required',
      'email': 'required|email',
      'password': 'required|min:6|confirmed',
    });

    data['password'] = HashHelper.hash(data['password']);

    final user = User()..fromJson(data);
    await user.save();

    final payload = await auth.login({
      'email': data['email'],
      'password': req.input('password'),
    });

    return {
      'message': 'User registered successfully',
      'data': {
        'token': payload['token'],
        'user': user,
      }
    };
  }
}
dart

import 'package:khadem/khadem_dart.dart';

class AuthController {
  static Future<Map<String, dynamic>> login(Request req, Response res) async {
    final data = await req.validate({
      'email': 'required|email',
      'password': 'required|min:6',
    });

    final auth = AuthManager();
    final payload = await auth.login(data);

    res.sendJson({
      'message': 'Login successful',
      'data': {
        'token': payload['token'],
        'user': User()..fromJson(payload['user']),
      }
    });
  }
}

Web Authentication

For web applications, Khadem provides session-based authentication with WebAuthService and WebAuthMiddleware for protecting routes and managing user sessions.
dart

import 'package:khadem/khadem_dart.dart';

class WebAuthController {
  WebAuthController.singleton();
  static WebAuthController instance = WebAuthController.singleton();
  factory WebAuthController() => instance;

  Future<void> showLogin(Request req, Response res) async {
    await res.view('login');
  }

  Future<void> showRegister(Request req, Response res) async {
    await res.view('register');
  }

  Future<void> login(Request req, Response res) async {
    final data = await req.validate({
      'email': 'required|email',
      'password': 'required|min:6',
      'remember': 'nullable|boolean',
    });

    final webAuth = WebAuthService();
    await webAuth.attemptLogin({
      'email': data['email'],
      'password': data['password'],
    }, remember: data['remember'] == true);

    final intended = req.input('intended') ?? '/dashboard';
    await res.redirect(intended);
  }

  Future<void> register(Request req, Response res) async {
    final data = await req.validate({
      'name': 'required',
      'email': 'required|email',
      'password': 'required|min:6|confirmed',
    });

    data['password'] = HashHelper.hash(data['password']);
    final user = User()..fromJson(data);
    await user.save();

    final webAuth = WebAuthService();
    await webAuth.attemptLogin({
      'email': data['email'],
      'password': req.input('password'),
    }, remember: false);

    await res.redirect('/dashboard');
  }

  Future<void> dashboard(Request req, Response res) async {
    final webAuth = WebAuthService();
    final isAuthenticated = await webAuth.isAuthenticated(req);

    if (!isAuthenticated) {
      return res.redirect('/login');
    }

    final user = User()..fromJson((await webAuth.getCurrentUser(req)) ?? {});
    await res.view('dashboard', data: {'user': user.toJson()});
  }

  Future<void> logout(Request req, Response res) async {
    final webAuth = WebAuthService();
    await webAuth.logout(req, res);
    await res.redirect('/login');
  }
}
dart

import 'package:khadem/khadem_dart.dart';

class WebAuthMiddleware {
  static Middleware auth({
    String redirectTo = '/login',
    List<String> except = const [],
  }) {
    final authService = WebAuthService();

    return Middleware((Request req, Response res, NextFunction next) async {
      if (_isExcluded(req.path, except)) {
        return next();
      }

      if (!authService.isAuthenticated(req)) {
        req.session.set('url.intended', req.uri.toString());
        req.session.flash('message', 'Please log in to continue');
        res.redirect(redirectTo);
        return;
      }

      await _ensureUserContext(req, authService, redirectTo);
      return next();
    });
  }

  static Middleware guest({
    String redirectTo = '/dashboard',
    List<String> except = const [],
  }) {
    final authService = WebAuthService();

    return Middleware((Request req, Response res, NextFunction next) async {
      if (_isExcluded(req.path, except)) {
        return next();
      }

      if (authService.isAuthenticated(req)) {
        res.redirect(redirectTo);
        return;
      }

      return next();
    });
  }

  static bool _isExcluded(String path, List<String> except) {
    return except.any((route) => path.startsWith(route));
  }

  static Future<void> _ensureUserContext(
    Request req,
    WebAuthService authService,
    String redirectTo,
  ) async {
    if (req.user == null) {
      final user = await authService.getCurrentUser(req);
      if (user != null) {
        req.setUser(user);
      }
    }
  }
}

Authentication Middleware

Protect your routes with authentication middleware. The AuthMiddleware automatically validates Bearer tokens and attaches user data to requests.
dart

import 'package:khadem/khadem_dart.dart';

class AuthMiddleware extends Middleware {
  AuthMiddleware() : super(_handleAuth);

  static Future<void> _handleAuth(Request req, Response res, NextFunction next) async {
    try {
      final authHeader = _extractAuthHeader(req);
      final token = _extractBearerToken(authHeader);
      final user = await _verifyToken(token);
      _attachUserToRequest(req, user);
      await next();
    } catch (error) {
      throw AuthException('Authentication failed: ${error.toString()}');
    }
  }

  static String _extractAuthHeader(Request request) {
    final authHeader = request.header('authorization');
    if (authHeader == null || authHeader.isEmpty) {
      throw AuthException('Missing authorization header');
    }
    return authHeader;
  }

  static String _extractBearerToken(String authHeader) {
    if (!authHeader.startsWith('Bearer ')) {
      throw AuthException('Invalid authorization header format');
    }
    final token = authHeader.replaceFirst('Bearer ', '').trim();
    if (token.isEmpty) {
      throw AuthException('Empty token provided');
    }
    return token;
  }

  static Future<Map<String, dynamic>> _verifyToken(String token) async {
    final authManager = Khadem.container.resolve<AuthManager>();
    return authManager.verify(token);
  }

  static void _attachUserToRequest(Request request, Map<String, dynamic> user) {
    request.setAttribute('user', user);
    request.setAttribute('userId', user['id']);
    request.setAttribute('isAuthenticated', true);
  }
}
dart

// routes/web.dart
import 'package:khadem/khadem_dart.dart';

void defineRoutes() {
  // Public routes
  Route.get('/login', AuthController.showLoginForm);
  Route.post('/login', AuthController.login);
  Route.get('/register', AuthController.showRegister);
  Route.post('/register', AuthController.register);

  // API routes with AuthMiddleware
  Route.group(() {
    Route.get('/api/profile', AuthController.profile);
    Route.put('/api/profile', AuthController.updateProfile);
    Route.post('/api/logout', AuthController.logout);
  }, middleware: [AuthMiddleware()]);

  // Web routes with WebAuthMiddleware
  Route.group(() {
    Route.get('/dashboard', WebAuthController.dashboard);
    Route.get('/chat', WebAuthController.chat);
  }, middleware: [WebAuthMiddleware.auth()]);
}

Guards and Authentication Drivers

Khadem supports multiple authentication guards and drivers. Guards define authentication contexts, while drivers handle the actual authentication logic.
dart

import 'package:khadem/khadem_dart.dart';

// Using different guards
class MultiGuardController {
  static Future<Map<String, dynamic>> adminLogin(Request req, Response res) async {
    final adminAuth = AuthManager(guard: 'admins');
    final data = await req.validate({
      'email': 'required|email',
      'password': 'required|min:6',
    });

    final result = await adminAuth.login(data);

    res.sendJson({
      'message': 'Admin login successful',
      'data': {
        'token': result['token'],
        'user': result['user'],
        'guard': 'admins',
      }
    });
  }

  static Future<Map<String, dynamic>> userLogin(Request req, Response res) async {
    final userAuth = AuthManager(guard: 'users');
    final data = await req.validate({
      'email': 'required|email',
      'password': 'required|min:6',
    });

    final result = await userAuth.login(data);

    res.sendJson({
      'message': 'User login successful',
      'data': {
        'token': result['token'],
        'user': result['user'],
        'guard': 'users',
      }
    });
  }
}
dart

import 'package:khadem/khadem_dart.dart';

class JWTAuthController {
  static Future<Map<String, dynamic>> login(Request req, Response res) async {
    final data = await req.validate({
      'email': 'required|email',
      'password': 'required|min:6',
    });

    final jwtService = JWTAuthService.create('users');
    final result = await jwtService.attemptLogin(data);

    res.sendJson({
      'message': 'JWT login successful',
      'data': {
        'token': result['access_token'],
        'user': result['user'],
        'expires_in': result['expires_in'],
      }
    });
  }
}

Token Management

Khadem provides comprehensive token management including refresh tokens, token verification, and secure token generation.
dart

import 'package:khadem/khadem_dart.dart';

class TokenController {
  static Future<Map<String, dynamic>> refreshToken(Request req, Response res) async {
    final refreshToken = req.input('refresh_token');

    if (refreshToken == null || refreshToken.isEmpty) {
      return res.status(400).sendJson({
        'error': 'Refresh token is required'
      });
    }

    final auth = AuthManager();
    final result = await auth.refreshAccessToken(refreshToken);

    res.sendJson({
      'message': 'Token refreshed successfully',
      'data': {
        'access_token': result['access_token'],
        'refresh_token': result['refresh_token'],
        'token_type': result['token_type'],
        'expires_in': result['expires_in'],
      }
    });
  }
}
dart

import 'package:khadem/khadem_dart.dart';

class AuthVerificationController {
  static Future<Map<String, dynamic>> verifyToken(Request req, Response res) async {
    final token = req.header('authorization')?.replaceFirst('Bearer ', '');

    if (token == null || token.isEmpty) {
      return res.status(401).sendJson({
        'error': 'Token is required'
      });
    }

    final auth = AuthManager();
    final user = await auth.verify(token);

    res.sendJson({
      'message': 'Token is valid',
      'data': {
        'user': user,
        'valid': true,
      }
    });
  }
}

Error Handling

Proper error handling is crucial for authentication. Khadem provides specific exceptions and comprehensive error responses.
dart

import 'package:khadem/khadem_dart.dart';

class AuthErrorHandler {
  static Future<Map<String, dynamic>> handleAuthError(Request req, dynamic error) async {
    if (error is AuthException) {
      final statusCode = error.statusCode ?? 401;
      return {
        'success': false,
        'error': error.message,
        'code': error.code ?? 'AUTH_ERROR',
        'status_code': statusCode,
      };
    }

    // Handle other authentication-related errors
    if (error.toString().contains('token')) {
      return {
        'success': false,
        'error': 'Authentication token error',
        'code': 'TOKEN_ERROR',
        'status_code': 401,
      };
    }

    if (error.toString().contains('credentials')) {
      return {
        'success': false,
        'error': 'Invalid credentials',
        'code': 'INVALID_CREDENTIALS',
        'status_code': 401,
      };
    }

    return {
      'success': false,
      'error': 'Authentication failed',
      'code': 'AUTH_FAILED',
      'status_code': 500,
    };
  }
}

// Usage in controller
class AuthController {
  static Future<Map<String, dynamic>> login(Request req, Response res) async {
    try {
      final data = await req.validate({
        'email': 'required|email',
        'password': 'required|min:6',
      });

      final auth = AuthManager();
      final result = await auth.login(data);

      res.sendJson({
        'success': true,
        'data': result,
      });
    } catch (error) {
      final errorResponse = await AuthErrorHandler.handleAuthError(req, error);
      res.status(errorResponse['status_code']).sendJson(errorResponse);
    }
  }
}

On this page