jwtExtractor static method

Middleware jwtExtractor({
  1. required String jwtSecret,
  2. List<String> excludePaths = const [],
})

Middleware que extrae y valida JWT desde Authorization header

jwtSecret - Clave secreta para validar la firma JWT excludePaths - Paths que no requieren extracción JWT

Extrae el JWT del header "Authorization: Bearer ", lo valida y agrega el payload al contexto del request

Implementation

static Middleware jwtExtractor({
  required String jwtSecret,
  List<String> excludePaths = const [],
}) {
  return (Handler innerHandler) {
    return (Request request) async {
      final path = request.requestedUri.path;
      final requestId = request.context['request_id'] as String? ?? 'unknown';

      // Saltar extracción para paths excluidos
      if (excludePaths.any((excludePath) => path.startsWith(excludePath))) {
        Log.d('[$requestId] JWT extraction skipped for excluded path: $path');
        return await innerHandler(request);
      }

      try {
        Log.d('[$requestId] Extracting JWT from Authorization header');

        // Obtener Authorization header
        final authHeader = request.headers['authorization'];

        if (authHeader == null || !authHeader.startsWith('Bearer ')) {
          Log.d('[$requestId] No Bearer token found in Authorization header');

          // Agregar contexto vacío para endpoints que no requieren JWT
          final updatedRequest = request.change(
            context: {...request.context, 'jwt_payload': null}
          );

          return await innerHandler(updatedRequest);
        }

        // Extraer token
        final token = authHeader.substring(7);

        if (token.isEmpty) {
          Log.w('[$requestId] Empty JWT token provided');
          return _unauthorizedResponse('Invalid JWT token format', requestId);
        }

        // Validar y decodificar JWT
        final jwtPayload = await _validateAndDecodeJWT(token, jwtSecret);

        if (jwtPayload == null) {
          Log.w('[$requestId] JWT validation failed');
          return _unauthorizedResponse('Invalid or expired JWT token', requestId);
        }

        Log.i('[$requestId] JWT validated successfully for user: ${jwtPayload['user_id'] ?? 'unknown'}');

        // Agregar payload JWT al contexto del request
        final updatedRequest = request.change(
          context: {
            ...request.context,
            'jwt_payload': jwtPayload,
            'user_id': jwtPayload['user_id'],
            'user_email': jwtPayload['email'],
            'user_role': jwtPayload['role'],
          }
        );

        return await innerHandler(updatedRequest);

      } catch (e, stackTrace) {
        Log.e('[$requestId] JWT extraction error: $e');

        return Response(
          500,
          body: jsonEncode({
            'success': false,
            'error': {
              'code': 'INTERNAL_ERROR',
              'message': 'Authentication system error',
              'status_code': 500,
            },
            'timestamp': DateTime.now().toIso8601String(),
            'request_id': requestId,
          }),
          headers: {
            'Content-Type': 'application/json',
            'X-Request-ID': requestId,
          },
        );
      }
    };
  };
}