@rakomi/react-native 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,299 @@
1
+ import { CryptoProvider, KeyValueStore } from '@rakomi/sdk-core';
2
+
3
+ /**
4
+ * `NativeAuthAdapter` — single typed surface that swaps every
5
+ * browser-coupled primitive in `@rakomi/react` for an RN/Expo equivalent.
6
+ *
7
+ * It is the source of truth for which native module backs each capability.
8
+ * The default impl in `./expo-adapter.ts` wires Expo modules; bare-RN consumers
9
+ * wire their own via `<RakomiProvider nativeAdapter={...}>`.
10
+ *
11
+ * Forward-compat slots (typed, optional, no default impl):
12
+ * - `verifiers` for EUDI Wallet attestation (eIDAS 2 / Reg. 2024/1183, end-2026 mandate).
13
+ * - `dpopProver` for RFC 9449 DPoP.
14
+ * - `pushAuthorizationRequest` for RFC 9126 PAR.
15
+ *
16
+ * Hardening rules:
17
+ * - The provider MUST `Object.freeze` the adapter on first use.
18
+ * - Adapter methods MUST NOT log token values.
19
+ * - Adapter is the ONLY layer touching expo-* modules — keeps SDK core platform-neutral.
20
+ */
21
+
22
+ interface BrowserAuthSessionOptions {
23
+ /**
24
+ * iOS-only. Defaults to `true` for stricter privacy
25
+ * (no shared cookies). Consumer can opt out via `<RakomiProvider browserPreferEphemeralSession={false}>`.
26
+ */
27
+ preferEphemeralSession?: boolean;
28
+ }
29
+ type BrowserAuthSessionResult = {
30
+ type: 'success';
31
+ url: string;
32
+ } | {
33
+ type: 'cancel';
34
+ } | {
35
+ type: 'dismiss';
36
+ } | {
37
+ type: 'locked';
38
+ };
39
+ interface SystemBrowser {
40
+ /**
41
+ * Open `authUrl` in the OS system browser (ASWebAuthenticationSession on iOS,
42
+ * Custom Tabs on Android). The OS returns a `redirectUri`-prefixed URL when
43
+ * the OAuth provider redirects.
44
+ *
45
+ * Implementations MUST NOT use any kind of WebView (RFC 8252).
46
+ */
47
+ openAuthSession(authUrl: string, redirectUri: string, options?: BrowserAuthSessionOptions): Promise<BrowserAuthSessionResult>;
48
+ }
49
+ interface DeepLinkSubscription {
50
+ /** Stop receiving callbacks. Idempotent. */
51
+ remove(): void;
52
+ }
53
+ interface DeepLinkProvider {
54
+ /**
55
+ * Return the URL the app was launched with, if any. Cold start case.
56
+ */
57
+ getInitialUrl(): Promise<string | null>;
58
+ /**
59
+ * Subscribe to incoming deep links. Listener is called with each URL.
60
+ * Implementations MUST debounce duplicate URLs within ~60s (single-use guard)
61
+ * — but typically the SDK layer enforces this, not the adapter.
62
+ */
63
+ addListener(listener: (url: string) => void): DeepLinkSubscription;
64
+ }
65
+ type BiometricResult = {
66
+ success: true;
67
+ } | {
68
+ success: false;
69
+ reason: 'cancelled' | 'lockout' | 'not_enrolled' | 'unavailable' | 'unknown';
70
+ };
71
+ interface BiometricGate {
72
+ /**
73
+ * Return `true` if the device has hardware AND the user has enrolled biometrics
74
+ * (or a passcode, when `disableDeviceFallback === false`).
75
+ */
76
+ isAvailable(): Promise<boolean>;
77
+ /**
78
+ * Prompt the user for biometric authentication.
79
+ *
80
+ * `strict: true` disables passcode fallback (`disableDeviceFallback: true`).
81
+ * Default is `false` (allow device passcode).
82
+ */
83
+ authenticate(options: {
84
+ promptMessage: string;
85
+ strict?: boolean;
86
+ }): Promise<BiometricResult>;
87
+ }
88
+ type AppStateValue = 'active' | 'background' | 'inactive' | 'unknown';
89
+ interface AppStateSubscription {
90
+ remove(): void;
91
+ }
92
+ interface AppLifecycle {
93
+ /**
94
+ * Subscribe to AppState transitions.
95
+ *
96
+ * Android Q+ fires `AppState` twice on keyboard
97
+ * dismiss — the SDK layer (not the adapter) MUST debounce ~300ms.
98
+ */
99
+ addStateChangeListener(listener: (next: AppStateValue) => void): AppStateSubscription;
100
+ /** Read current state synchronously. */
101
+ getCurrent(): AppStateValue;
102
+ }
103
+ interface NetInfoSubscription {
104
+ remove(): void;
105
+ }
106
+ interface ConnectivityProvider {
107
+ /** Resolve once with the current connectivity state. */
108
+ isConnected(): Promise<boolean>;
109
+ /** Subscribe to connectivity transitions. */
110
+ addListener(listener: (isConnected: boolean) => void): NetInfoSubscription;
111
+ }
112
+ /** Reserved for EUDI Wallet attestation verifiers (eIDAS 2). */
113
+ interface AttestationVerifier {
114
+ readonly id: string;
115
+ verify(presentation: unknown): Promise<{
116
+ ok: boolean;
117
+ reason?: string;
118
+ }>;
119
+ }
120
+ /**
121
+ * RFC 9449 DPoP proof generator — the canonical cross-port prover contract.
122
+ * `@rakomi/node`'s `createDpopProver` is the conformance oracle the proof
123
+ * *shape* matches byte-for-byte in structure.
124
+ *
125
+ * SECURITY — the production RN prover MUST bridge signing to the native
126
+ * keystore (iOS Keychain / Secure Enclave, Android Keystore/StrongBox) and MUST
127
+ * NEVER sign in the JS bundle: only `{jti, iat, nonce?}` + the signature cross
128
+ * the bridge; the private key never leaves the secure element. The prover OWNS
129
+ * every security-relevant proof field — `jti` (CSPRNG UUID, fresh per call),
130
+ * `iat` (seconds NumericDate), `typ: "dpop+jwt"`, the public-only `jwk`, and the
131
+ * pinned `alg` (`ES256` — the cross-port baseline, Secure-Enclave-eligible;
132
+ * NEVER derived from a key or any input). The caller supplies ONLY the canonical
133
+ * request binding (`htm`/`htu`) and the optional server `nonce`; it can never
134
+ * influence the algorithm or inject claims, and the proof carries NO PII
135
+ * (exactly `{htm, htu, jti, iat}` body + optional `nonce`).
136
+ */
137
+ interface DpopProofInput {
138
+ /** HTTP method of the bound request — `"POST"` for the refresh call. */
139
+ htm: string;
140
+ /**
141
+ * The ALREADY-canonical `htu` (RFC 9449 §4.3 — scheme+host+path, query/fragment
142
+ * stripped, default port stripped). The SDK decision layer canonicalizes; the
143
+ * prover signs it verbatim and MUST NOT re-derive or mutate it.
144
+ */
145
+ htu: string;
146
+ /** Optional RFC 9449 §8 server nonce, echoed into the proof on a retry. */
147
+ nonce?: string;
148
+ }
149
+ interface DpopProver {
150
+ /**
151
+ * Build a compact-serialized DPoP-proof JWT for the bound request. Resolves to
152
+ * the `DPoP` header value. The prover signs with the session's single native
153
+ * keypair (one keypair per session — never re-generated per call). MUST reject
154
+ * (never return an empty/falsy string) if the native signer is unavailable so
155
+ * the SDK surfaces `dpop_prover_unavailable` instead of a silent Bearer call.
156
+ */
157
+ createProof(input: DpopProofInput): Promise<string>;
158
+ /**
159
+ * RFC 7638 SHA-256 thumbprint of the prover's public JWK — lets the SDK's
160
+ * bound-state cross-check pair `token_type === "DPoP"` with a `jkt` sanity
161
+ * check (`jkt`-continuity). Resolves the SAME value for the life
162
+ * of the session's keypair.
163
+ */
164
+ jktHint(): Promise<string>;
165
+ }
166
+ /** Reserved for RFC 9126 PAR (Pushed Authorization Requests). */
167
+ interface ParClient {
168
+ push(authorizationRequest: Record<string, string>): Promise<{
169
+ requestUri: string;
170
+ expiresIn: number;
171
+ }>;
172
+ }
173
+ /**
174
+ * Background-fetch contract — best-effort token refresh while the app is suspended
175
+ * (Expo Task Manager / expo-background-fetch on Expo, BGTaskScheduler on iOS / WorkManager on Android
176
+ * for bare-RN consumers).
177
+ *
178
+ * The OS controls scheduling; the SDK only registers the work and provides the handler.
179
+ * best-effort, NOT guaranteed delivery.
180
+ */
181
+ interface BackgroundTask {
182
+ /**
183
+ * Register a periodic refresh task. The handler is called by the OS at OS-decided intervals
184
+ * (≥15 min on Android; OS-throttled on iOS). Returns an unregister function.
185
+ */
186
+ register(handler: () => Promise<void>, options?: {
187
+ minimumIntervalSeconds?: number;
188
+ }): Promise<() => Promise<void>>;
189
+ /** Resolve `true` if the OS reports background-fetch as available + not user-disabled. */
190
+ isAvailable(): Promise<boolean>;
191
+ }
192
+ interface NativeAuthAdapter {
193
+ readonly crypto: CryptoProvider;
194
+ readonly storage: KeyValueStore;
195
+ readonly browser: SystemBrowser;
196
+ readonly deepLink: DeepLinkProvider;
197
+ readonly biometric: BiometricGate;
198
+ readonly appLifecycle: AppLifecycle;
199
+ readonly connectivity: ConnectivityProvider;
200
+ readonly verifiers?: readonly AttestationVerifier[];
201
+ readonly dpopProver?: DpopProver;
202
+ readonly par?: ParClient;
203
+ /** Optional best-effort background refresh hook. */
204
+ readonly backgroundTask?: BackgroundTask;
205
+ }
206
+ /**
207
+ * `tokenCache` injection sugar (shorthand).
208
+ *
209
+ * If a consumer just wants to override storage without re-implementing the full adapter,
210
+ * they pass `tokenCache` and the SDK constructs an adapter that delegates only
211
+ * `storage` to `tokenCache` while inheriting the rest from the default Expo adapter.
212
+ */
213
+ interface TokenCache {
214
+ getToken(key: string): Promise<string | null>;
215
+ saveToken(key: string, value: string): Promise<void>;
216
+ clearToken(key: string): Promise<void>;
217
+ }
218
+
219
+ /**
220
+ * The raw native-module surface a RN host exposes for DPoP signing — a
221
+ * TurboModule / legacy NativeModule whose methods take POSITIONAL arguments and
222
+ * return Promises (RN bridge convention: no `undefined` across the bridge — an
223
+ * absent nonce is passed as `null`).
224
+ *
225
+ * contract the native implementation MUST honour:
226
+ * - Generate ONE session keypair in the secure element on first use and hold it
227
+ * for the module instance's lifetime (one keypair per session); NEVER
228
+ * regenerate per call (a fresh key ⇒ `jkt` mismatch ⇒ 401 on every refresh).
229
+ * - `createDpopProof` builds + signs the FULL compact proof JWT natively and
230
+ * resolves the `DPoP` header value; it owns `jti`/`iat`/`typ`/`jwk`/`alg`.
231
+ * - Reject (Promise rejection) when the secure element is unavailable — NEVER
232
+ * resolve an empty string or a Bearer-shaped placeholder.
233
+ */
234
+ interface NativeDpopModuleSpec {
235
+ /**
236
+ * Build + sign the compact DPoP-proof JWT in the secure element for the bound
237
+ * request. `nonce` is `null` unless this is the RFC 9449 §8 nonce retry.
238
+ * Resolves the `DPoP` header value.
239
+ */
240
+ createDpopProof(htm: string, htu: string, nonce: string | null): Promise<string>;
241
+ /** RFC 7638 SHA-256 thumbprint of the session public key (stable for the key's life). */
242
+ dpopJktThumbprint(): Promise<string>;
243
+ }
244
+ interface CreateNativeDpopProverOptions {
245
+ /**
246
+ * The host's native DPoP module. On bare RN this is the linked native module
247
+ * (`NativeModules.RakomiDpop`); on Expo it is the Expo module's JS surface.
248
+ * The module MUST be backed by the platform secure element.
249
+ */
250
+ module: NativeDpopModuleSpec;
251
+ }
252
+ /**
253
+ * Adapt a host's native DPoP signing module to the canonical {@link DpopProver}
254
+ * contract. Wire the result into the consumer's `NativeAuthAdapter.dpopProver`
255
+ * slot; `<RakomiProvider>` then auto-constructs the session-scoped `DpopSession`.
256
+ *
257
+ * The adapter enforces the no-silent-downgrade invariant: if the native
258
+ * signer rejects OR returns a falsy proof, `createProof` throws so the refresh
259
+ * path surfaces `auth/dpop_prover_unavailable` and makes NO proof-less network
260
+ * call — it never falls back to an empty/Bearer request.
261
+ *
262
+ * @public — additive-only.
263
+ */
264
+ declare function createNativeDpopProver(options: CreateNativeDpopProverOptions): DpopProver;
265
+
266
+ /**
267
+ * Default Expo adapter — wires the `NativeAuthAdapter` interface to Expo modules.
268
+ *
269
+ * Modules are imported dynamically inside the factory so:
270
+ * - Bare-RN consumers who pass a custom adapter don't pay the Expo bundle cost.
271
+ * - Consumers who never use biometric don't pay the `expo-local-authentication`
272
+ * cost (lazy-load).
273
+ *
274
+ * Hardening:
275
+ * - The returned adapter is `Object.freeze`-able by callers; this module returns
276
+ * a plain object — `<RakomiProvider>` freezes it on mount.
277
+ * - No `console.log` of token values anywhere in this file.
278
+ *
279
+ * Native-passkey integration (`react-native-passkey`, ASAuthorization) is
280
+ * out-of-scope here.
281
+ */
282
+
283
+ /**
284
+ * Build the default Expo-backed `NativeAuthAdapter`.
285
+ *
286
+ * Each capability is lazily resolved on first use to keep the cold-import bundle
287
+ * small (perf budget). The resolution is memoised internally.
288
+ */
289
+ declare function createDefaultExpoAdapter(options?: CreateDefaultExpoAdapterOptions): NativeAuthAdapter;
290
+ interface CreateDefaultExpoAdapterOptions {
291
+ /**
292
+ * Override storage with the consumer's own token cache (sugar).
293
+ * When provided, `crypto`/`browser`/`deepLink`/`biometric`/`appLifecycle`/`connectivity`
294
+ * still come from Expo defaults; only storage is replaced.
295
+ */
296
+ tokenCache?: TokenCache;
297
+ }
298
+
299
+ export { type AppLifecycle as A, type BackgroundTask as B, type ConnectivityProvider as C, type DpopProver as D, type NativeAuthAdapter as N, type ParClient as P, type SystemBrowser as S, type TokenCache as T, type AppStateValue as a, type AttestationVerifier as b, type BiometricGate as c, type BiometricResult as d, type BrowserAuthSessionOptions as e, type BrowserAuthSessionResult as f, type CreateDefaultExpoAdapterOptions as g, type CreateNativeDpopProverOptions as h, type DeepLinkProvider as i, type DpopProofInput as j, type NativeDpopModuleSpec as k, createDefaultExpoAdapter as l, createNativeDpopProver as m, type AppStateSubscription as n, type DeepLinkSubscription as o, type NetInfoSubscription as p };
@@ -0,0 +1,299 @@
1
+ import { CryptoProvider, KeyValueStore } from '@rakomi/sdk-core';
2
+
3
+ /**
4
+ * `NativeAuthAdapter` — single typed surface that swaps every
5
+ * browser-coupled primitive in `@rakomi/react` for an RN/Expo equivalent.
6
+ *
7
+ * It is the source of truth for which native module backs each capability.
8
+ * The default impl in `./expo-adapter.ts` wires Expo modules; bare-RN consumers
9
+ * wire their own via `<RakomiProvider nativeAdapter={...}>`.
10
+ *
11
+ * Forward-compat slots (typed, optional, no default impl):
12
+ * - `verifiers` for EUDI Wallet attestation (eIDAS 2 / Reg. 2024/1183, end-2026 mandate).
13
+ * - `dpopProver` for RFC 9449 DPoP.
14
+ * - `pushAuthorizationRequest` for RFC 9126 PAR.
15
+ *
16
+ * Hardening rules:
17
+ * - The provider MUST `Object.freeze` the adapter on first use.
18
+ * - Adapter methods MUST NOT log token values.
19
+ * - Adapter is the ONLY layer touching expo-* modules — keeps SDK core platform-neutral.
20
+ */
21
+
22
+ interface BrowserAuthSessionOptions {
23
+ /**
24
+ * iOS-only. Defaults to `true` for stricter privacy
25
+ * (no shared cookies). Consumer can opt out via `<RakomiProvider browserPreferEphemeralSession={false}>`.
26
+ */
27
+ preferEphemeralSession?: boolean;
28
+ }
29
+ type BrowserAuthSessionResult = {
30
+ type: 'success';
31
+ url: string;
32
+ } | {
33
+ type: 'cancel';
34
+ } | {
35
+ type: 'dismiss';
36
+ } | {
37
+ type: 'locked';
38
+ };
39
+ interface SystemBrowser {
40
+ /**
41
+ * Open `authUrl` in the OS system browser (ASWebAuthenticationSession on iOS,
42
+ * Custom Tabs on Android). The OS returns a `redirectUri`-prefixed URL when
43
+ * the OAuth provider redirects.
44
+ *
45
+ * Implementations MUST NOT use any kind of WebView (RFC 8252).
46
+ */
47
+ openAuthSession(authUrl: string, redirectUri: string, options?: BrowserAuthSessionOptions): Promise<BrowserAuthSessionResult>;
48
+ }
49
+ interface DeepLinkSubscription {
50
+ /** Stop receiving callbacks. Idempotent. */
51
+ remove(): void;
52
+ }
53
+ interface DeepLinkProvider {
54
+ /**
55
+ * Return the URL the app was launched with, if any. Cold start case.
56
+ */
57
+ getInitialUrl(): Promise<string | null>;
58
+ /**
59
+ * Subscribe to incoming deep links. Listener is called with each URL.
60
+ * Implementations MUST debounce duplicate URLs within ~60s (single-use guard)
61
+ * — but typically the SDK layer enforces this, not the adapter.
62
+ */
63
+ addListener(listener: (url: string) => void): DeepLinkSubscription;
64
+ }
65
+ type BiometricResult = {
66
+ success: true;
67
+ } | {
68
+ success: false;
69
+ reason: 'cancelled' | 'lockout' | 'not_enrolled' | 'unavailable' | 'unknown';
70
+ };
71
+ interface BiometricGate {
72
+ /**
73
+ * Return `true` if the device has hardware AND the user has enrolled biometrics
74
+ * (or a passcode, when `disableDeviceFallback === false`).
75
+ */
76
+ isAvailable(): Promise<boolean>;
77
+ /**
78
+ * Prompt the user for biometric authentication.
79
+ *
80
+ * `strict: true` disables passcode fallback (`disableDeviceFallback: true`).
81
+ * Default is `false` (allow device passcode).
82
+ */
83
+ authenticate(options: {
84
+ promptMessage: string;
85
+ strict?: boolean;
86
+ }): Promise<BiometricResult>;
87
+ }
88
+ type AppStateValue = 'active' | 'background' | 'inactive' | 'unknown';
89
+ interface AppStateSubscription {
90
+ remove(): void;
91
+ }
92
+ interface AppLifecycle {
93
+ /**
94
+ * Subscribe to AppState transitions.
95
+ *
96
+ * Android Q+ fires `AppState` twice on keyboard
97
+ * dismiss — the SDK layer (not the adapter) MUST debounce ~300ms.
98
+ */
99
+ addStateChangeListener(listener: (next: AppStateValue) => void): AppStateSubscription;
100
+ /** Read current state synchronously. */
101
+ getCurrent(): AppStateValue;
102
+ }
103
+ interface NetInfoSubscription {
104
+ remove(): void;
105
+ }
106
+ interface ConnectivityProvider {
107
+ /** Resolve once with the current connectivity state. */
108
+ isConnected(): Promise<boolean>;
109
+ /** Subscribe to connectivity transitions. */
110
+ addListener(listener: (isConnected: boolean) => void): NetInfoSubscription;
111
+ }
112
+ /** Reserved for EUDI Wallet attestation verifiers (eIDAS 2). */
113
+ interface AttestationVerifier {
114
+ readonly id: string;
115
+ verify(presentation: unknown): Promise<{
116
+ ok: boolean;
117
+ reason?: string;
118
+ }>;
119
+ }
120
+ /**
121
+ * RFC 9449 DPoP proof generator — the canonical cross-port prover contract.
122
+ * `@rakomi/node`'s `createDpopProver` is the conformance oracle the proof
123
+ * *shape* matches byte-for-byte in structure.
124
+ *
125
+ * SECURITY — the production RN prover MUST bridge signing to the native
126
+ * keystore (iOS Keychain / Secure Enclave, Android Keystore/StrongBox) and MUST
127
+ * NEVER sign in the JS bundle: only `{jti, iat, nonce?}` + the signature cross
128
+ * the bridge; the private key never leaves the secure element. The prover OWNS
129
+ * every security-relevant proof field — `jti` (CSPRNG UUID, fresh per call),
130
+ * `iat` (seconds NumericDate), `typ: "dpop+jwt"`, the public-only `jwk`, and the
131
+ * pinned `alg` (`ES256` — the cross-port baseline, Secure-Enclave-eligible;
132
+ * NEVER derived from a key or any input). The caller supplies ONLY the canonical
133
+ * request binding (`htm`/`htu`) and the optional server `nonce`; it can never
134
+ * influence the algorithm or inject claims, and the proof carries NO PII
135
+ * (exactly `{htm, htu, jti, iat}` body + optional `nonce`).
136
+ */
137
+ interface DpopProofInput {
138
+ /** HTTP method of the bound request — `"POST"` for the refresh call. */
139
+ htm: string;
140
+ /**
141
+ * The ALREADY-canonical `htu` (RFC 9449 §4.3 — scheme+host+path, query/fragment
142
+ * stripped, default port stripped). The SDK decision layer canonicalizes; the
143
+ * prover signs it verbatim and MUST NOT re-derive or mutate it.
144
+ */
145
+ htu: string;
146
+ /** Optional RFC 9449 §8 server nonce, echoed into the proof on a retry. */
147
+ nonce?: string;
148
+ }
149
+ interface DpopProver {
150
+ /**
151
+ * Build a compact-serialized DPoP-proof JWT for the bound request. Resolves to
152
+ * the `DPoP` header value. The prover signs with the session's single native
153
+ * keypair (one keypair per session — never re-generated per call). MUST reject
154
+ * (never return an empty/falsy string) if the native signer is unavailable so
155
+ * the SDK surfaces `dpop_prover_unavailable` instead of a silent Bearer call.
156
+ */
157
+ createProof(input: DpopProofInput): Promise<string>;
158
+ /**
159
+ * RFC 7638 SHA-256 thumbprint of the prover's public JWK — lets the SDK's
160
+ * bound-state cross-check pair `token_type === "DPoP"` with a `jkt` sanity
161
+ * check (`jkt`-continuity). Resolves the SAME value for the life
162
+ * of the session's keypair.
163
+ */
164
+ jktHint(): Promise<string>;
165
+ }
166
+ /** Reserved for RFC 9126 PAR (Pushed Authorization Requests). */
167
+ interface ParClient {
168
+ push(authorizationRequest: Record<string, string>): Promise<{
169
+ requestUri: string;
170
+ expiresIn: number;
171
+ }>;
172
+ }
173
+ /**
174
+ * Background-fetch contract — best-effort token refresh while the app is suspended
175
+ * (Expo Task Manager / expo-background-fetch on Expo, BGTaskScheduler on iOS / WorkManager on Android
176
+ * for bare-RN consumers).
177
+ *
178
+ * The OS controls scheduling; the SDK only registers the work and provides the handler.
179
+ * best-effort, NOT guaranteed delivery.
180
+ */
181
+ interface BackgroundTask {
182
+ /**
183
+ * Register a periodic refresh task. The handler is called by the OS at OS-decided intervals
184
+ * (≥15 min on Android; OS-throttled on iOS). Returns an unregister function.
185
+ */
186
+ register(handler: () => Promise<void>, options?: {
187
+ minimumIntervalSeconds?: number;
188
+ }): Promise<() => Promise<void>>;
189
+ /** Resolve `true` if the OS reports background-fetch as available + not user-disabled. */
190
+ isAvailable(): Promise<boolean>;
191
+ }
192
+ interface NativeAuthAdapter {
193
+ readonly crypto: CryptoProvider;
194
+ readonly storage: KeyValueStore;
195
+ readonly browser: SystemBrowser;
196
+ readonly deepLink: DeepLinkProvider;
197
+ readonly biometric: BiometricGate;
198
+ readonly appLifecycle: AppLifecycle;
199
+ readonly connectivity: ConnectivityProvider;
200
+ readonly verifiers?: readonly AttestationVerifier[];
201
+ readonly dpopProver?: DpopProver;
202
+ readonly par?: ParClient;
203
+ /** Optional best-effort background refresh hook. */
204
+ readonly backgroundTask?: BackgroundTask;
205
+ }
206
+ /**
207
+ * `tokenCache` injection sugar (shorthand).
208
+ *
209
+ * If a consumer just wants to override storage without re-implementing the full adapter,
210
+ * they pass `tokenCache` and the SDK constructs an adapter that delegates only
211
+ * `storage` to `tokenCache` while inheriting the rest from the default Expo adapter.
212
+ */
213
+ interface TokenCache {
214
+ getToken(key: string): Promise<string | null>;
215
+ saveToken(key: string, value: string): Promise<void>;
216
+ clearToken(key: string): Promise<void>;
217
+ }
218
+
219
+ /**
220
+ * The raw native-module surface a RN host exposes for DPoP signing — a
221
+ * TurboModule / legacy NativeModule whose methods take POSITIONAL arguments and
222
+ * return Promises (RN bridge convention: no `undefined` across the bridge — an
223
+ * absent nonce is passed as `null`).
224
+ *
225
+ * contract the native implementation MUST honour:
226
+ * - Generate ONE session keypair in the secure element on first use and hold it
227
+ * for the module instance's lifetime (one keypair per session); NEVER
228
+ * regenerate per call (a fresh key ⇒ `jkt` mismatch ⇒ 401 on every refresh).
229
+ * - `createDpopProof` builds + signs the FULL compact proof JWT natively and
230
+ * resolves the `DPoP` header value; it owns `jti`/`iat`/`typ`/`jwk`/`alg`.
231
+ * - Reject (Promise rejection) when the secure element is unavailable — NEVER
232
+ * resolve an empty string or a Bearer-shaped placeholder.
233
+ */
234
+ interface NativeDpopModuleSpec {
235
+ /**
236
+ * Build + sign the compact DPoP-proof JWT in the secure element for the bound
237
+ * request. `nonce` is `null` unless this is the RFC 9449 §8 nonce retry.
238
+ * Resolves the `DPoP` header value.
239
+ */
240
+ createDpopProof(htm: string, htu: string, nonce: string | null): Promise<string>;
241
+ /** RFC 7638 SHA-256 thumbprint of the session public key (stable for the key's life). */
242
+ dpopJktThumbprint(): Promise<string>;
243
+ }
244
+ interface CreateNativeDpopProverOptions {
245
+ /**
246
+ * The host's native DPoP module. On bare RN this is the linked native module
247
+ * (`NativeModules.RakomiDpop`); on Expo it is the Expo module's JS surface.
248
+ * The module MUST be backed by the platform secure element.
249
+ */
250
+ module: NativeDpopModuleSpec;
251
+ }
252
+ /**
253
+ * Adapt a host's native DPoP signing module to the canonical {@link DpopProver}
254
+ * contract. Wire the result into the consumer's `NativeAuthAdapter.dpopProver`
255
+ * slot; `<RakomiProvider>` then auto-constructs the session-scoped `DpopSession`.
256
+ *
257
+ * The adapter enforces the no-silent-downgrade invariant: if the native
258
+ * signer rejects OR returns a falsy proof, `createProof` throws so the refresh
259
+ * path surfaces `auth/dpop_prover_unavailable` and makes NO proof-less network
260
+ * call — it never falls back to an empty/Bearer request.
261
+ *
262
+ * @public — additive-only.
263
+ */
264
+ declare function createNativeDpopProver(options: CreateNativeDpopProverOptions): DpopProver;
265
+
266
+ /**
267
+ * Default Expo adapter — wires the `NativeAuthAdapter` interface to Expo modules.
268
+ *
269
+ * Modules are imported dynamically inside the factory so:
270
+ * - Bare-RN consumers who pass a custom adapter don't pay the Expo bundle cost.
271
+ * - Consumers who never use biometric don't pay the `expo-local-authentication`
272
+ * cost (lazy-load).
273
+ *
274
+ * Hardening:
275
+ * - The returned adapter is `Object.freeze`-able by callers; this module returns
276
+ * a plain object — `<RakomiProvider>` freezes it on mount.
277
+ * - No `console.log` of token values anywhere in this file.
278
+ *
279
+ * Native-passkey integration (`react-native-passkey`, ASAuthorization) is
280
+ * out-of-scope here.
281
+ */
282
+
283
+ /**
284
+ * Build the default Expo-backed `NativeAuthAdapter`.
285
+ *
286
+ * Each capability is lazily resolved on first use to keep the cold-import bundle
287
+ * small (perf budget). The resolution is memoised internally.
288
+ */
289
+ declare function createDefaultExpoAdapter(options?: CreateDefaultExpoAdapterOptions): NativeAuthAdapter;
290
+ interface CreateDefaultExpoAdapterOptions {
291
+ /**
292
+ * Override storage with the consumer's own token cache (sugar).
293
+ * When provided, `crypto`/`browser`/`deepLink`/`biometric`/`appLifecycle`/`connectivity`
294
+ * still come from Expo defaults; only storage is replaced.
295
+ */
296
+ tokenCache?: TokenCache;
297
+ }
298
+
299
+ export { type AppLifecycle as A, type BackgroundTask as B, type ConnectivityProvider as C, type DpopProver as D, type NativeAuthAdapter as N, type ParClient as P, type SystemBrowser as S, type TokenCache as T, type AppStateValue as a, type AttestationVerifier as b, type BiometricGate as c, type BiometricResult as d, type BrowserAuthSessionOptions as e, type BrowserAuthSessionResult as f, type CreateDefaultExpoAdapterOptions as g, type CreateNativeDpopProverOptions as h, type DeepLinkProvider as i, type DpopProofInput as j, type NativeDpopModuleSpec as k, createDefaultExpoAdapter as l, createNativeDpopProver as m, type AppStateSubscription as n, type DeepLinkSubscription as o, type NetInfoSubscription as p };