···
final dpopKeyJwk = (dpopKey as dynamic).privateJwk ?? dpopKey.bareJwk ?? {};
+
print('๐ Storing DPoP key for authorization flow');
···
// Create OAuth server agent
stateData.authMethod != null
? ClientAuthMethod.fromJson(
···
: const ClientAuthMethod.none(); // Legacy fallback
// Restore dpopKey from stored private JWK
+
// Restore DPoP key with error handling for corrupted JWK data
+
final FlutterKey dpopKey;
+
dpopKey = FlutterKey.fromJwk(
+
stateData.dpopKey as Map<String, dynamic>,
+
print('๐ DPoP key restored successfully for token exchange');
+
'Failed to restore DPoP key from stored state: $e. '
+
'The stored key may be corrupted. Please try authenticating again.',
final server = await serverFactory.fromIssuer(
···
: const ClientAuthMethod.none(); // Legacy
+
// Restore dpopKey from stored private JWK with error handling
+
// CRITICAL FIX: Use the stored key instead of generating a new one
+
// This ensures DPoP proofs match the token binding
+
final FlutterKey dpopKey;
+
dpopKey = FlutterKey.fromJwk(
+
session.dpopKey as Map<String, dynamic>,
+
// If key is corrupted, delete the session and force re-authentication
+
await _sessionGetter.delStored(
+
Exception('Corrupted DPoP key in stored session: $e'),
+
'Failed to restore DPoP key for session. The stored key is corrupted. '
+
'Please authenticate again.',
final server = await serverFactory.fromIssuer(
···
: const ClientAuthMethod.none(); // Legacy
+
// Restore dpopKey from stored private JWK with error handling
+
// CRITICAL FIX: Use the stored key instead of generating a new one
+
// This ensures DPoP proofs match the token binding
+
final FlutterKey dpopKey;
+
dpopKey = FlutterKey.fromJwk(
+
session.dpopKey as Map<String, dynamic>,
+
// If key is corrupted, skip server-side revocation
+
// The finally block will still delete the local session
+
print('โ ๏ธ Cannot revoke on server: corrupted DPoP key ($e)');
+
print(' Local session will still be deleted');
final server = await serverFactory.fromIssuer(