Main coves client
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}