From 24b584d8354a3dd2a673fa6eba14d10ab07a9979 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 12 Dec 2025 00:54:21 +0300 Subject: [PATCH] Add detailed JWT debug logging for admin endpoints - Changed stderr to print for visibility in logs - Added signature mismatch debugging - Added expiry time debugging --- .../lib/api/v2/authorize_v2.dart | 53 ++++++++++++------- .../lib/api/v2/jwt_service.dart | 18 +++++-- 2 files changed, 48 insertions(+), 23 deletions(-) diff --git a/mnemo_cards_backend/lib/api/v2/authorize_v2.dart b/mnemo_cards_backend/lib/api/v2/authorize_v2.dart index 50b8c09..0b4371e 100644 --- a/mnemo_cards_backend/lib/api/v2/authorize_v2.dart +++ b/mnemo_cards_backend/lib/api/v2/authorize_v2.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:mnemo_cards_backend/api/v2/jwt_service.dart'; import 'package:mnemo_cards_backend/user/user_manager.dart'; import 'package:shelf/shelf.dart'; @@ -91,13 +89,17 @@ Middleware authorizeV2(UserManager userManager, JwtService jwtService) { // Shelf normalizes headers to lowercase, so we check 'authorization' final authHeader = request.headers['authorization']; - // Debug logging for admin endpoints - use stderr to ensure it's visible - if (normalizedPath.startsWith('/admin/')) { - stderr.writeln('[AUTH DEBUG] Path: $normalizedPath'); - stderr.writeln('[AUTH DEBUG] Authorization header present: ${authHeader != null}'); + final isAdminEndpoint = normalizedPath.startsWith('/admin/'); + + // Debug logging for admin endpoints + if (isAdminEndpoint) { + print('[AUTH DEBUG] Path: $normalizedPath'); + print('[AUTH DEBUG] Authorization header present: ${authHeader != null}'); if (authHeader != null) { - stderr.writeln('[AUTH DEBUG] Authorization header length: ${authHeader.length}'); - stderr.writeln('[AUTH DEBUG] Authorization header starts with Bearer: ${authHeader.startsWith('Bearer ')}'); + print('[AUTH DEBUG] Authorization header length: ${authHeader.length}'); + print('[AUTH DEBUG] Authorization header starts with Bearer: ${authHeader.startsWith('Bearer ')}'); + } else { + print('[AUTH DEBUG] All headers: ${request.headers}'); } } @@ -117,25 +119,36 @@ Middleware authorizeV2(UserManager userManager, JwtService jwtService) { final token = authHeader.substring(7); // Remove "Bearer " prefix - // Debug logging for admin endpoints - use stderr - if (normalizedPath.startsWith('/admin/')) { - stderr.writeln('[AUTH DEBUG] Verifying token for path: $normalizedPath'); - stderr.writeln('[AUTH DEBUG] Token length: ${token.length}'); - stderr.writeln('[AUTH DEBUG] Token preview: ${token.length > 50 ? token.substring(0, 50) + "..." : token}'); + // Debug logging for admin endpoints + if (isAdminEndpoint) { + print('[AUTH DEBUG] Verifying token for path: $normalizedPath'); + print('[AUTH DEBUG] Token length: ${token.length}'); + print('[AUTH DEBUG] Token preview: ${token.length > 50 ? token.substring(0, 50) + "..." : token}'); } - final userId = jwtService.verifyAccessToken(token); + final isAdminPath = isAdminEndpoint; + final userId = jwtService.verifyAccessToken(token, debug: isAdminPath); if (userId == null) { // Additional debug info for admin endpoints - if (normalizedPath.startsWith('/admin/')) { - stderr.writeln('[AUTH DEBUG] Token verification failed for path: $normalizedPath'); + if (isAdminPath) { + print('[AUTH DEBUG] Token verification failed for path: $normalizedPath'); // Try to extract payload to see what's wrong try { final payload = jwtService.extractPayload(token); - stderr.writeln('[AUTH DEBUG] Extracted payload: $payload'); + print('[AUTH DEBUG] Extracted payload: $payload'); + if (payload != null) { + final exp = payload['exp'] as int?; + if (exp != null) { + final expiryTime = DateTime.fromMillisecondsSinceEpoch(exp * 1000); + print('[AUTH DEBUG] Token expires at: $expiryTime'); + print('[AUTH DEBUG] Current time: ${DateTime.now()}'); + print('[AUTH DEBUG] Is expired: ${DateTime.now().isAfter(expiryTime)}'); + } + print('[AUTH DEBUG] Token type: ${payload['type']}'); + } } catch (e) { - stderr.writeln('[AUTH DEBUG] Could not extract payload: $e'); + print('[AUTH DEBUG] Could not extract payload: $e'); } } return Response( @@ -145,8 +158,8 @@ Middleware authorizeV2(UserManager userManager, JwtService jwtService) { ); } - if (normalizedPath.startsWith('/admin/')) { - stderr.writeln('[AUTH DEBUG] Token verified successfully, userId: $userId'); + if (isAdminPath) { + print('[AUTH DEBUG] Token verified successfully, userId: $userId'); } // Get user from database diff --git a/mnemo_cards_backend/lib/api/v2/jwt_service.dart b/mnemo_cards_backend/lib/api/v2/jwt_service.dart index 74fe121..353c4f7 100644 --- a/mnemo_cards_backend/lib/api/v2/jwt_service.dart +++ b/mnemo_cards_backend/lib/api/v2/jwt_service.dart @@ -63,10 +63,11 @@ class JwtService { } /// Verify access token and return user ID - String? verifyAccessToken(String token) { + String? verifyAccessToken(String token, {bool debug = false}) { try { - final payload = _verifySimpleJwt(token); + final payload = _verifySimpleJwt(token, debug: debug); if (payload == null) { + if (debug) print('[JWT DEBUG] _verifySimpleJwt returned null'); return null; } @@ -75,17 +76,20 @@ class JwtService { if (exp != null) { final expiryTime = DateTime.fromMillisecondsSinceEpoch(exp * 1000); if (DateTime.now().isAfter(expiryTime)) { + if (debug) print('[JWT DEBUG] Token expired at $expiryTime, now: ${DateTime.now()}'); return null; } } // Check token type if (payload['type'] != 'access') { + if (debug) print('[JWT DEBUG] Wrong token type: ${payload['type']}'); return null; } return payload['userId']?.toString(); } catch (e) { + if (debug) print('[JWT DEBUG] Exception in verifyAccessToken: $e'); return null; } } @@ -142,10 +146,11 @@ class JwtService { } /// Verify a simple JWT - Map? _verifySimpleJwt(String token) { + Map? _verifySimpleJwt(String token, {bool debug = false}) { try { final parts = token.split('.'); if (parts.length != 3) { + if (debug) print('[JWT DEBUG] Invalid token format: ${parts.length} parts'); return null; } @@ -160,6 +165,12 @@ class JwtService { final expectedSignatureB64 = base64UrlEncode(expectedSignature); if (signatureB64 != expectedSignatureB64) { + if (debug) { + print('[JWT DEBUG] Signature mismatch!'); + print('[JWT DEBUG] Got signature: $signatureB64'); + print('[JWT DEBUG] Expected signature: $expectedSignatureB64'); + print('[JWT DEBUG] Secret key length: ${_secretKey.length}'); + } return null; // Invalid signature } @@ -167,6 +178,7 @@ class JwtService { final payloadJson = utf8.decode(base64Url.decode(payloadB64)); return jsonDecode(payloadJson) as Map; } catch (e) { + if (debug) print('[JWT DEBUG] Exception during verification: $e'); return null; } }