1/// Internal state data stored during OAuth authorization flow. 2/// 3/// This contains ephemeral data needed to complete the OAuth flow, 4/// such as PKCE code verifiers, state parameters, and nonces. 5class InternalStateData { 6 /// The OAuth issuer URL 7 final String iss; 8 9 /// The DPoP key used for this authorization 10 final Map<String, dynamic> dpopKey; 11 12 /// Client authentication method (serialized as Map or String) 13 /// 14 /// Can be: 15 /// - A Map containing {method: 'private_key_jwt', kid: '...'} for private key JWT 16 /// - A Map containing {method: 'none'} for no authentication 17 /// - A String 'legacy' for backwards compatibility 18 /// - null (defaults to 'legacy' when loading) 19 final dynamic authMethod; 20 21 /// PKCE code verifier for authorization code flow 22 final String? verifier; 23 24 /// The redirect URI used during authorization 25 /// MUST match exactly during token exchange 26 final String? redirectUri; 27 28 /// Application state to preserve across the OAuth flow 29 final String? appState; 30 31 const InternalStateData({ 32 required this.iss, 33 required this.dpopKey, 34 this.authMethod, 35 this.verifier, 36 this.redirectUri, 37 this.appState, 38 }); 39 40 /// Creates an instance from a JSON map. 41 factory InternalStateData.fromJson(Map<String, dynamic> json) { 42 return InternalStateData( 43 iss: json['iss'] as String, 44 dpopKey: json['dpopKey'] as Map<String, dynamic>, 45 authMethod: json['authMethod'], // Can be Map or String 46 verifier: json['verifier'] as String?, 47 redirectUri: json['redirectUri'] as String?, 48 appState: json['appState'] as String?, 49 ); 50 } 51 52 /// Converts this instance to a JSON map. 53 Map<String, dynamic> toJson() { 54 final json = <String, dynamic>{'iss': iss, 'dpopKey': dpopKey}; 55 56 if (authMethod != null) json['authMethod'] = authMethod; 57 if (verifier != null) json['verifier'] = verifier; 58 if (redirectUri != null) json['redirectUri'] = redirectUri; 59 if (appState != null) json['appState'] = appState; 60 61 return json; 62 } 63} 64 65/// Abstract storage interface for OAuth state data. 66/// 67/// Implementations should store state data temporarily during the OAuth flow. 68/// This data is typically short-lived and can be cleared after successful 69/// authorization or timeout. 70/// 71/// Example implementation using in-memory storage: 72/// ```dart 73/// class MemoryStateStore implements StateStore { 74/// final Map<String, InternalStateData> _store = {}; 75/// 76/// @override 77/// Future<InternalStateData?> get(String key) async => _store[key]; 78/// 79/// @override 80/// Future<void> set(String key, InternalStateData data) async { 81/// _store[key] = data; 82/// } 83/// 84/// @override 85/// Future<void> del(String key) async { 86/// _store.remove(key); 87/// } 88/// } 89/// ``` 90abstract class StateStore { 91 /// Retrieves state data for the given key. 92 /// 93 /// Returns `null` if no data exists for the key. 94 Future<InternalStateData?> get(String key); 95 96 /// Stores state data for the given key. 97 /// 98 /// Overwrites any existing data for the key. 99 Future<void> set(String key, InternalStateData data); 100 101 /// Deletes state data for the given key. 102 /// 103 /// Does nothing if no data exists for the key. 104 Future<void> del(String key); 105 106 /// Optionally clears all state data. 107 /// 108 /// Implementations may choose not to implement this method. 109 Future<void> clear() async { 110 // Default implementation does nothing 111 } 112}