@rakomi/react-native 0.1.0 → 0.2.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.
- package/COMPLIANCE.md +36 -6
- package/README.md +247 -0
- package/SECURITY.md +1 -1
- package/THIRD-PARTY-NOTICES +1 -1
- package/dist/index.cjs +692 -13
- package/dist/index.d.cts +70 -6
- package/dist/index.d.ts +70 -6
- package/dist/index.js +693 -16
- package/dist/native/index.cjs +265 -0
- package/dist/native/index.d.cts +33 -3
- package/dist/native/index.d.ts +33 -3
- package/dist/native/index.js +261 -1
- package/dist/{expo-adapter-CtPCEgue.d.cts → passkey-adapter-D_Z3Z7lV.d.cts} +172 -2
- package/dist/{expo-adapter-CtPCEgue.d.ts → passkey-adapter-D_Z3Z7lV.d.ts} +172 -2
- package/package.json +3 -3
- package/sbom.cdx.json +17 -11
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CryptoProvider, KeyValueStore } from '@rakomi/sdk-core';
|
|
1
|
+
import { CryptoProvider, KeyValueStore, PasskeyCeremonyAdapter } from '@rakomi/sdk-core';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* `NativeAuthAdapter` — single typed surface that swaps every
|
|
@@ -202,6 +202,21 @@ interface NativeAuthAdapter {
|
|
|
202
202
|
readonly par?: ParClient;
|
|
203
203
|
/** Optional best-effort background refresh hook. */
|
|
204
204
|
readonly backgroundTask?: BackgroundTask;
|
|
205
|
+
/**
|
|
206
|
+
* The passkey ceremony, delegated to the platform.
|
|
207
|
+
*
|
|
208
|
+
* The contract is `@rakomi/sdk-core`'s `PasskeyCeremonyAdapter` — imported, never re-declared
|
|
209
|
+
* here. A second interface for the same thing would let the browser binding and the native
|
|
210
|
+
* binding drift, and the conformance suite both of them run could only ever test one of them.
|
|
211
|
+
*
|
|
212
|
+
* Build one with `createNativePasskeyAdapter({ module })` from `@rakomi/react-native/native`.
|
|
213
|
+
* On Expo web a host may put a browser adapter in this same slot — the slot is platform-agnostic.
|
|
214
|
+
*
|
|
215
|
+
* A future SECOND ceremony type (a device-bound key, a hybrid/CTAP flow) gets its OWN optional
|
|
216
|
+
* slot; this one never becomes a type union. A union would destroy the totality of the shared
|
|
217
|
+
* conformance suite, which validates exactly ONE contract.
|
|
218
|
+
*/
|
|
219
|
+
readonly passkeys?: PasskeyCeremonyAdapter;
|
|
205
220
|
}
|
|
206
221
|
/**
|
|
207
222
|
* `tokenCache` injection sugar (shorthand).
|
|
@@ -296,4 +311,159 @@ interface CreateDefaultExpoAdapterOptions {
|
|
|
296
311
|
tokenCache?: TokenCache;
|
|
297
312
|
}
|
|
298
313
|
|
|
299
|
-
|
|
314
|
+
/**
|
|
315
|
+
* The raw native-module surface a host exposes for passkey ceremonies.
|
|
316
|
+
*
|
|
317
|
+
* It is a structural duck-type: no brand, no symbol, no base class. **Any module satisfying this
|
|
318
|
+
* spec is a drop-in replacement; the SDK does not know which one you used** — your own Swift/Kotlin
|
|
319
|
+
* module, a community package, or a future first-party Expo module.
|
|
320
|
+
*
|
|
321
|
+
* ## What crosses the bridge
|
|
322
|
+
*
|
|
323
|
+
* Both ceremony methods take the server's options as a **JSON string** and resolve the finished
|
|
324
|
+
* credential as a **JSON string**. See the file header for why this is not an object.
|
|
325
|
+
*
|
|
326
|
+
* ## What the module MUST return (W3C WebAuthn **Level 3** JSON serialization, §5.1.8 / §5.1.9 —
|
|
327
|
+
* Candidate Recommendation; these JSON dictionaries do not exist in Level 2)
|
|
328
|
+
*
|
|
329
|
+
* `createPasskey` (registration):
|
|
330
|
+
*
|
|
331
|
+
* | member | required |
|
|
332
|
+
* |---|---|
|
|
333
|
+
* | `id`, `rawId`, `type: "public-key"` | MUST |
|
|
334
|
+
* | `response.clientDataJSON`, `response.attestationObject` | MUST |
|
|
335
|
+
* | `response.authenticatorData`, `response.publicKeyAlgorithm` | MUST |
|
|
336
|
+
* | `response.transports` | MUST (omitting it silently degrades later autofill) |
|
|
337
|
+
* | `clientExtensionResults` | MUST |
|
|
338
|
+
* | `authenticatorAttachment`, `response.publicKey` | optional |
|
|
339
|
+
*
|
|
340
|
+
* `getPasskey` (assertion):
|
|
341
|
+
*
|
|
342
|
+
* | member | required |
|
|
343
|
+
* |---|---|
|
|
344
|
+
* | `id`, `rawId`, `type: "public-key"` | MUST |
|
|
345
|
+
* | `response.clientDataJSON`, `response.authenticatorData`, `response.signature` | MUST |
|
|
346
|
+
* | `clientExtensionResults` | MUST |
|
|
347
|
+
* | `response.userHandle` | optional |
|
|
348
|
+
*
|
|
349
|
+
* **ENCODING — the single most common integration failure.** Every binary member is
|
|
350
|
+
* **base64url WITHOUT padding** (RFC 4648 **§5**). On iOS, `Data.base64EncodedString()` emits
|
|
351
|
+
* *standard* base64 (RFC 4648 **§4**, with `+`, `/` and `=`) — that is WRONG and the bridge rejects
|
|
352
|
+
* it with a message naming the field.
|
|
353
|
+
*
|
|
354
|
+
* (The bridge ENFORCES a subset of the table above — the fields the SDK's own fixtures carry and the
|
|
355
|
+
* server actually consumes. The rest are a MUST for you, not a MUST for the validator; sending them
|
|
356
|
+
* is what keeps your integration correct as the server tightens.)
|
|
357
|
+
*
|
|
358
|
+
* ## Rejection vocabulary — CLOSED
|
|
359
|
+
*
|
|
360
|
+
* Reject with an object carrying a `code`:
|
|
361
|
+
* - `PASSKEY_CANCELLED` — the user dismissed the sheet, or the OS cancelled.
|
|
362
|
+
* - `PASSKEY_UNSUPPORTED` — no passkey support on this OS version, or no credential provider.
|
|
363
|
+
* - `PASSKEY_NO_CREDENTIAL` — the user has no passkey for this relying party.
|
|
364
|
+
*
|
|
365
|
+
* Any other rejection is a genuine failure and is passed through untouched. **Do NOT set a
|
|
366
|
+
* rejection's `name` to `AbortError` / `NotAllowedError` / `TimeoutError` unless it really is a
|
|
367
|
+
* cancellation** — the SDK matches on those names and will read your intent as "the user cancelled".
|
|
368
|
+
*
|
|
369
|
+
* ## Stability (this is a SemVer surface even though we never compile your module)
|
|
370
|
+
*
|
|
371
|
+
* Your module is Swift or Kotlin: our typecheck will never see it, so a breaking change to this spec
|
|
372
|
+
* breaks every existing module **at runtime**, not at compile time — and the SDK's 24-month support
|
|
373
|
+
* policy means a module written today must still satisfy this spec two years from now. Therefore:
|
|
374
|
+
* - a new **optional** member → MINOR;
|
|
375
|
+
* - a new **optional trailing parameter** on an existing method → MINOR (a lower-arity function
|
|
376
|
+
* stays assignable — which is exactly why we do not reserve parameters "just in case");
|
|
377
|
+
* - a **required** method, a parameter-type change, or a change to the rejection vocabulary → MAJOR;
|
|
378
|
+
* - the rejection vocabulary is CLOSED: a new code is first a change in `@rakomi/sdk-core`, and only
|
|
379
|
+
* then a lane here.
|
|
380
|
+
*
|
|
381
|
+
* ## Orphan credentials — a residual you should know about
|
|
382
|
+
*
|
|
383
|
+
* If a registration ceremony is abandoned *after* the platform provider (iCloud Keychain / Google
|
|
384
|
+
* Password Manager) already created the credential, the user holds a passkey for your app that the
|
|
385
|
+
* server never learned about; their next sign-in with it fails. Implementing
|
|
386
|
+
* {@link NativePasskeyModuleSpec.cancelPasskeyRequest} narrows that window. The SDK does not
|
|
387
|
+
* reconcile orphans.
|
|
388
|
+
*/
|
|
389
|
+
interface NativePasskeyModuleSpec {
|
|
390
|
+
/**
|
|
391
|
+
* Whether this platform can run a passkey ceremony at all.
|
|
392
|
+
*
|
|
393
|
+
* **Advisory, not a verdict.** Every module in the ecosystem answers this from the OS version, so
|
|
394
|
+
* it says `true` on an Android emulator with no credential provider and on an iOS version where
|
|
395
|
+
* the platform provider does not exist. The SDK treats a throw or a non-`true` value as `false`
|
|
396
|
+
* (fail-closed) but cannot catch an honest `true` with no provider behind it — the authoritative
|
|
397
|
+
* verdict is the ceremony's own outcome.
|
|
398
|
+
*/
|
|
399
|
+
isPasskeySupported(): Promise<boolean>;
|
|
400
|
+
/** Run the registration ceremony. `requestJson` is the server's options, verbatim. */
|
|
401
|
+
createPasskey(requestJson: string): Promise<string>;
|
|
402
|
+
/** Run the assertion ceremony. `requestJson` is the server's options, verbatim. */
|
|
403
|
+
getPasskey(requestJson: string): Promise<string>;
|
|
404
|
+
/**
|
|
405
|
+
* Optional: is a *platform* authenticator (Face ID / fingerprint / screen lock) available?
|
|
406
|
+
*
|
|
407
|
+
* A UI hint only — never a gate. A synced passkey on another device is still a passkey, so a
|
|
408
|
+
* `false` here does not mean "no passkeys". Omit it and the SDK reports `null` (unknown) rather
|
|
409
|
+
* than guessing.
|
|
410
|
+
*/
|
|
411
|
+
isPlatformAuthenticatorAvailable?(): Promise<boolean>;
|
|
412
|
+
/**
|
|
413
|
+
* Optional: dismiss the in-flight native sheet (iOS `ASAuthorizationController.cancel()`;
|
|
414
|
+
* Android's `CredentialManager` calls are cancellable). Best-effort — a throw is ignored.
|
|
415
|
+
*
|
|
416
|
+
* It takes no request id because the native side has nothing to correlate against: the passkey sheet
|
|
417
|
+
* is an **OS-global modal**, and a cancel dismisses whatever is on screen. The SDK does NOT serialise
|
|
418
|
+
* ceremonies — nothing here can, since the sheet is owned by the OS — so the bridge instead fires the
|
|
419
|
+
* cancel only while the aborting ceremony is still the one in flight, making a stale abort a no-op
|
|
420
|
+
* rather than a teardown of somebody else's sheet. Preventing a second ceremony from being STARTED is
|
|
421
|
+
* a UI concern and belongs to the provider.
|
|
422
|
+
*/
|
|
423
|
+
cancelPasskeyRequest?(): void | Promise<void>;
|
|
424
|
+
}
|
|
425
|
+
interface CreateNativePasskeyAdapterOptions {
|
|
426
|
+
/**
|
|
427
|
+
* The host's native passkey module. On bare RN this is the linked native module
|
|
428
|
+
* (`NativeModules.RakomiPasskey`); on Expo it is the Expo module's JS surface; it may also be a
|
|
429
|
+
* community package wrapped to satisfy {@link NativePasskeyModuleSpec}.
|
|
430
|
+
*/
|
|
431
|
+
module: NativePasskeyModuleSpec;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Adapt a host's native passkey module to the canonical `PasskeyCeremonyAdapter` contract. Wire the
|
|
435
|
+
* result into `NativeAuthAdapter.passkeys`.
|
|
436
|
+
*
|
|
437
|
+
* The module's SHAPE is validated here, at wiring time, and a missing method throws loudly — in the
|
|
438
|
+
* host's adapter-construction code, where the bug is, rather than degrading to "passkeys are not
|
|
439
|
+
* supported on this device", which is a lie that hides the host's bug. An ABSENT adapter and a
|
|
440
|
+
* BROKEN adapter are different diagnoses and must not be collapsed.
|
|
441
|
+
*
|
|
442
|
+
* @public — additive-only.
|
|
443
|
+
*/
|
|
444
|
+
/**
|
|
445
|
+
* The core's ceremony contract, plus the one native-only capability the hook needs and the contract
|
|
446
|
+
* has no place for: the platform-authenticator hint. It is attached HERE rather than plumbed through
|
|
447
|
+
* the host, because the probe needs the MODULE and the hook only ever sees the adapter.
|
|
448
|
+
*
|
|
449
|
+
* Structurally a `PasskeyCeremonyAdapter`, so it drops into `nativeAdapter.passkeys` unchanged.
|
|
450
|
+
*/
|
|
451
|
+
interface NativePasskeyCeremonyAdapter extends PasskeyCeremonyAdapter {
|
|
452
|
+
/** `true` / `false` / `null` — and `null` means UNKNOWN, never "no". */
|
|
453
|
+
hasPlatformAuthenticator(): Promise<boolean | null>;
|
|
454
|
+
}
|
|
455
|
+
declare function createNativePasskeyAdapter(options: CreateNativePasskeyAdapterOptions): NativePasskeyCeremonyAdapter;
|
|
456
|
+
/**
|
|
457
|
+
* Ask the host's module whether a *platform* authenticator is available.
|
|
458
|
+
*
|
|
459
|
+
* `null` means **unknown**, and it is `null` in all three shapes of "the module does not really
|
|
460
|
+
* implement this", because in React Native a missing method has three shapes, not one: it is absent
|
|
461
|
+
* (a legacy `NativeModules` object), or it exists but throws synchronously (a TurboModule whose
|
|
462
|
+
* codegen compiled the spec but whose native side did not implement it), or it exists but rejects
|
|
463
|
+
* (an Expo module proxy). None of them may answer `false` — that would be the lie "this device has
|
|
464
|
+
* no platform authenticator" — and none of them may be inferred from `isSupported()`, because a
|
|
465
|
+
* synced passkey is a passkey without a local platform authenticator.
|
|
466
|
+
*/
|
|
467
|
+
declare function probePlatformAuthenticator(mod: NativePasskeyModuleSpec): Promise<boolean | null>;
|
|
468
|
+
|
|
469
|
+
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 CreateNativePasskeyAdapterOptions as i, type DeepLinkProvider as j, type DpopProofInput as k, type NativeDpopModuleSpec as l, type NativePasskeyCeremonyAdapter as m, type NativePasskeyModuleSpec as n, createDefaultExpoAdapter as o, createNativeDpopProver as p, createNativePasskeyAdapter as q, type AppStateSubscription as r, type DeepLinkSubscription as s, type NetInfoSubscription as t, probePlatformAuthenticator as u };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CryptoProvider, KeyValueStore } from '@rakomi/sdk-core';
|
|
1
|
+
import { CryptoProvider, KeyValueStore, PasskeyCeremonyAdapter } from '@rakomi/sdk-core';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* `NativeAuthAdapter` — single typed surface that swaps every
|
|
@@ -202,6 +202,21 @@ interface NativeAuthAdapter {
|
|
|
202
202
|
readonly par?: ParClient;
|
|
203
203
|
/** Optional best-effort background refresh hook. */
|
|
204
204
|
readonly backgroundTask?: BackgroundTask;
|
|
205
|
+
/**
|
|
206
|
+
* The passkey ceremony, delegated to the platform.
|
|
207
|
+
*
|
|
208
|
+
* The contract is `@rakomi/sdk-core`'s `PasskeyCeremonyAdapter` — imported, never re-declared
|
|
209
|
+
* here. A second interface for the same thing would let the browser binding and the native
|
|
210
|
+
* binding drift, and the conformance suite both of them run could only ever test one of them.
|
|
211
|
+
*
|
|
212
|
+
* Build one with `createNativePasskeyAdapter({ module })` from `@rakomi/react-native/native`.
|
|
213
|
+
* On Expo web a host may put a browser adapter in this same slot — the slot is platform-agnostic.
|
|
214
|
+
*
|
|
215
|
+
* A future SECOND ceremony type (a device-bound key, a hybrid/CTAP flow) gets its OWN optional
|
|
216
|
+
* slot; this one never becomes a type union. A union would destroy the totality of the shared
|
|
217
|
+
* conformance suite, which validates exactly ONE contract.
|
|
218
|
+
*/
|
|
219
|
+
readonly passkeys?: PasskeyCeremonyAdapter;
|
|
205
220
|
}
|
|
206
221
|
/**
|
|
207
222
|
* `tokenCache` injection sugar (shorthand).
|
|
@@ -296,4 +311,159 @@ interface CreateDefaultExpoAdapterOptions {
|
|
|
296
311
|
tokenCache?: TokenCache;
|
|
297
312
|
}
|
|
298
313
|
|
|
299
|
-
|
|
314
|
+
/**
|
|
315
|
+
* The raw native-module surface a host exposes for passkey ceremonies.
|
|
316
|
+
*
|
|
317
|
+
* It is a structural duck-type: no brand, no symbol, no base class. **Any module satisfying this
|
|
318
|
+
* spec is a drop-in replacement; the SDK does not know which one you used** — your own Swift/Kotlin
|
|
319
|
+
* module, a community package, or a future first-party Expo module.
|
|
320
|
+
*
|
|
321
|
+
* ## What crosses the bridge
|
|
322
|
+
*
|
|
323
|
+
* Both ceremony methods take the server's options as a **JSON string** and resolve the finished
|
|
324
|
+
* credential as a **JSON string**. See the file header for why this is not an object.
|
|
325
|
+
*
|
|
326
|
+
* ## What the module MUST return (W3C WebAuthn **Level 3** JSON serialization, §5.1.8 / §5.1.9 —
|
|
327
|
+
* Candidate Recommendation; these JSON dictionaries do not exist in Level 2)
|
|
328
|
+
*
|
|
329
|
+
* `createPasskey` (registration):
|
|
330
|
+
*
|
|
331
|
+
* | member | required |
|
|
332
|
+
* |---|---|
|
|
333
|
+
* | `id`, `rawId`, `type: "public-key"` | MUST |
|
|
334
|
+
* | `response.clientDataJSON`, `response.attestationObject` | MUST |
|
|
335
|
+
* | `response.authenticatorData`, `response.publicKeyAlgorithm` | MUST |
|
|
336
|
+
* | `response.transports` | MUST (omitting it silently degrades later autofill) |
|
|
337
|
+
* | `clientExtensionResults` | MUST |
|
|
338
|
+
* | `authenticatorAttachment`, `response.publicKey` | optional |
|
|
339
|
+
*
|
|
340
|
+
* `getPasskey` (assertion):
|
|
341
|
+
*
|
|
342
|
+
* | member | required |
|
|
343
|
+
* |---|---|
|
|
344
|
+
* | `id`, `rawId`, `type: "public-key"` | MUST |
|
|
345
|
+
* | `response.clientDataJSON`, `response.authenticatorData`, `response.signature` | MUST |
|
|
346
|
+
* | `clientExtensionResults` | MUST |
|
|
347
|
+
* | `response.userHandle` | optional |
|
|
348
|
+
*
|
|
349
|
+
* **ENCODING — the single most common integration failure.** Every binary member is
|
|
350
|
+
* **base64url WITHOUT padding** (RFC 4648 **§5**). On iOS, `Data.base64EncodedString()` emits
|
|
351
|
+
* *standard* base64 (RFC 4648 **§4**, with `+`, `/` and `=`) — that is WRONG and the bridge rejects
|
|
352
|
+
* it with a message naming the field.
|
|
353
|
+
*
|
|
354
|
+
* (The bridge ENFORCES a subset of the table above — the fields the SDK's own fixtures carry and the
|
|
355
|
+
* server actually consumes. The rest are a MUST for you, not a MUST for the validator; sending them
|
|
356
|
+
* is what keeps your integration correct as the server tightens.)
|
|
357
|
+
*
|
|
358
|
+
* ## Rejection vocabulary — CLOSED
|
|
359
|
+
*
|
|
360
|
+
* Reject with an object carrying a `code`:
|
|
361
|
+
* - `PASSKEY_CANCELLED` — the user dismissed the sheet, or the OS cancelled.
|
|
362
|
+
* - `PASSKEY_UNSUPPORTED` — no passkey support on this OS version, or no credential provider.
|
|
363
|
+
* - `PASSKEY_NO_CREDENTIAL` — the user has no passkey for this relying party.
|
|
364
|
+
*
|
|
365
|
+
* Any other rejection is a genuine failure and is passed through untouched. **Do NOT set a
|
|
366
|
+
* rejection's `name` to `AbortError` / `NotAllowedError` / `TimeoutError` unless it really is a
|
|
367
|
+
* cancellation** — the SDK matches on those names and will read your intent as "the user cancelled".
|
|
368
|
+
*
|
|
369
|
+
* ## Stability (this is a SemVer surface even though we never compile your module)
|
|
370
|
+
*
|
|
371
|
+
* Your module is Swift or Kotlin: our typecheck will never see it, so a breaking change to this spec
|
|
372
|
+
* breaks every existing module **at runtime**, not at compile time — and the SDK's 24-month support
|
|
373
|
+
* policy means a module written today must still satisfy this spec two years from now. Therefore:
|
|
374
|
+
* - a new **optional** member → MINOR;
|
|
375
|
+
* - a new **optional trailing parameter** on an existing method → MINOR (a lower-arity function
|
|
376
|
+
* stays assignable — which is exactly why we do not reserve parameters "just in case");
|
|
377
|
+
* - a **required** method, a parameter-type change, or a change to the rejection vocabulary → MAJOR;
|
|
378
|
+
* - the rejection vocabulary is CLOSED: a new code is first a change in `@rakomi/sdk-core`, and only
|
|
379
|
+
* then a lane here.
|
|
380
|
+
*
|
|
381
|
+
* ## Orphan credentials — a residual you should know about
|
|
382
|
+
*
|
|
383
|
+
* If a registration ceremony is abandoned *after* the platform provider (iCloud Keychain / Google
|
|
384
|
+
* Password Manager) already created the credential, the user holds a passkey for your app that the
|
|
385
|
+
* server never learned about; their next sign-in with it fails. Implementing
|
|
386
|
+
* {@link NativePasskeyModuleSpec.cancelPasskeyRequest} narrows that window. The SDK does not
|
|
387
|
+
* reconcile orphans.
|
|
388
|
+
*/
|
|
389
|
+
interface NativePasskeyModuleSpec {
|
|
390
|
+
/**
|
|
391
|
+
* Whether this platform can run a passkey ceremony at all.
|
|
392
|
+
*
|
|
393
|
+
* **Advisory, not a verdict.** Every module in the ecosystem answers this from the OS version, so
|
|
394
|
+
* it says `true` on an Android emulator with no credential provider and on an iOS version where
|
|
395
|
+
* the platform provider does not exist. The SDK treats a throw or a non-`true` value as `false`
|
|
396
|
+
* (fail-closed) but cannot catch an honest `true` with no provider behind it — the authoritative
|
|
397
|
+
* verdict is the ceremony's own outcome.
|
|
398
|
+
*/
|
|
399
|
+
isPasskeySupported(): Promise<boolean>;
|
|
400
|
+
/** Run the registration ceremony. `requestJson` is the server's options, verbatim. */
|
|
401
|
+
createPasskey(requestJson: string): Promise<string>;
|
|
402
|
+
/** Run the assertion ceremony. `requestJson` is the server's options, verbatim. */
|
|
403
|
+
getPasskey(requestJson: string): Promise<string>;
|
|
404
|
+
/**
|
|
405
|
+
* Optional: is a *platform* authenticator (Face ID / fingerprint / screen lock) available?
|
|
406
|
+
*
|
|
407
|
+
* A UI hint only — never a gate. A synced passkey on another device is still a passkey, so a
|
|
408
|
+
* `false` here does not mean "no passkeys". Omit it and the SDK reports `null` (unknown) rather
|
|
409
|
+
* than guessing.
|
|
410
|
+
*/
|
|
411
|
+
isPlatformAuthenticatorAvailable?(): Promise<boolean>;
|
|
412
|
+
/**
|
|
413
|
+
* Optional: dismiss the in-flight native sheet (iOS `ASAuthorizationController.cancel()`;
|
|
414
|
+
* Android's `CredentialManager` calls are cancellable). Best-effort — a throw is ignored.
|
|
415
|
+
*
|
|
416
|
+
* It takes no request id because the native side has nothing to correlate against: the passkey sheet
|
|
417
|
+
* is an **OS-global modal**, and a cancel dismisses whatever is on screen. The SDK does NOT serialise
|
|
418
|
+
* ceremonies — nothing here can, since the sheet is owned by the OS — so the bridge instead fires the
|
|
419
|
+
* cancel only while the aborting ceremony is still the one in flight, making a stale abort a no-op
|
|
420
|
+
* rather than a teardown of somebody else's sheet. Preventing a second ceremony from being STARTED is
|
|
421
|
+
* a UI concern and belongs to the provider.
|
|
422
|
+
*/
|
|
423
|
+
cancelPasskeyRequest?(): void | Promise<void>;
|
|
424
|
+
}
|
|
425
|
+
interface CreateNativePasskeyAdapterOptions {
|
|
426
|
+
/**
|
|
427
|
+
* The host's native passkey module. On bare RN this is the linked native module
|
|
428
|
+
* (`NativeModules.RakomiPasskey`); on Expo it is the Expo module's JS surface; it may also be a
|
|
429
|
+
* community package wrapped to satisfy {@link NativePasskeyModuleSpec}.
|
|
430
|
+
*/
|
|
431
|
+
module: NativePasskeyModuleSpec;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Adapt a host's native passkey module to the canonical `PasskeyCeremonyAdapter` contract. Wire the
|
|
435
|
+
* result into `NativeAuthAdapter.passkeys`.
|
|
436
|
+
*
|
|
437
|
+
* The module's SHAPE is validated here, at wiring time, and a missing method throws loudly — in the
|
|
438
|
+
* host's adapter-construction code, where the bug is, rather than degrading to "passkeys are not
|
|
439
|
+
* supported on this device", which is a lie that hides the host's bug. An ABSENT adapter and a
|
|
440
|
+
* BROKEN adapter are different diagnoses and must not be collapsed.
|
|
441
|
+
*
|
|
442
|
+
* @public — additive-only.
|
|
443
|
+
*/
|
|
444
|
+
/**
|
|
445
|
+
* The core's ceremony contract, plus the one native-only capability the hook needs and the contract
|
|
446
|
+
* has no place for: the platform-authenticator hint. It is attached HERE rather than plumbed through
|
|
447
|
+
* the host, because the probe needs the MODULE and the hook only ever sees the adapter.
|
|
448
|
+
*
|
|
449
|
+
* Structurally a `PasskeyCeremonyAdapter`, so it drops into `nativeAdapter.passkeys` unchanged.
|
|
450
|
+
*/
|
|
451
|
+
interface NativePasskeyCeremonyAdapter extends PasskeyCeremonyAdapter {
|
|
452
|
+
/** `true` / `false` / `null` — and `null` means UNKNOWN, never "no". */
|
|
453
|
+
hasPlatformAuthenticator(): Promise<boolean | null>;
|
|
454
|
+
}
|
|
455
|
+
declare function createNativePasskeyAdapter(options: CreateNativePasskeyAdapterOptions): NativePasskeyCeremonyAdapter;
|
|
456
|
+
/**
|
|
457
|
+
* Ask the host's module whether a *platform* authenticator is available.
|
|
458
|
+
*
|
|
459
|
+
* `null` means **unknown**, and it is `null` in all three shapes of "the module does not really
|
|
460
|
+
* implement this", because in React Native a missing method has three shapes, not one: it is absent
|
|
461
|
+
* (a legacy `NativeModules` object), or it exists but throws synchronously (a TurboModule whose
|
|
462
|
+
* codegen compiled the spec but whose native side did not implement it), or it exists but rejects
|
|
463
|
+
* (an Expo module proxy). None of them may answer `false` — that would be the lie "this device has
|
|
464
|
+
* no platform authenticator" — and none of them may be inferred from `isSupported()`, because a
|
|
465
|
+
* synced passkey is a passkey without a local platform authenticator.
|
|
466
|
+
*/
|
|
467
|
+
declare function probePlatformAuthenticator(mod: NativePasskeyModuleSpec): Promise<boolean | null>;
|
|
468
|
+
|
|
469
|
+
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 CreateNativePasskeyAdapterOptions as i, type DeepLinkProvider as j, type DpopProofInput as k, type NativeDpopModuleSpec as l, type NativePasskeyCeremonyAdapter as m, type NativePasskeyModuleSpec as n, createDefaultExpoAdapter as o, createNativeDpopProver as p, createNativePasskeyAdapter as q, type AppStateSubscription as r, type DeepLinkSubscription as s, type NetInfoSubscription as t, probePlatformAuthenticator as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rakomi/react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "React Native / Expo SDK for Rakomi authentication.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"rakomi",
|
|
@@ -83,11 +83,11 @@
|
|
|
83
83
|
}
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
|
-
"@rakomi/react": "0.
|
|
86
|
+
"@rakomi/react": "0.2.0"
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|
|
89
89
|
"jose": "^6.2.3",
|
|
90
|
-
"@rakomi/sdk-core": "^0.
|
|
90
|
+
"@rakomi/sdk-core": "^0.2.0"
|
|
91
91
|
},
|
|
92
92
|
"scripts": {
|
|
93
93
|
"build": "NODE_OPTIONS=--max-old-space-size=4096 tsup",
|
package/sbom.cdx.json
CHANGED
|
@@ -3,9 +3,15 @@
|
|
|
3
3
|
"specVersion": "1.6",
|
|
4
4
|
"version": 1,
|
|
5
5
|
"metadata": {
|
|
6
|
+
"supplier": {
|
|
7
|
+
"name": "CRE8EVE Sp. z o.o.",
|
|
8
|
+
"url": [
|
|
9
|
+
"https://rakomi.com"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
6
12
|
"authors": [
|
|
7
13
|
{
|
|
8
|
-
"name": "CRE8EVE"
|
|
14
|
+
"name": "CRE8EVE Sp. z o.o."
|
|
9
15
|
}
|
|
10
16
|
],
|
|
11
17
|
"tools": {
|
|
@@ -14,16 +20,16 @@
|
|
|
14
20
|
"type": "application",
|
|
15
21
|
"group": "rakomi",
|
|
16
22
|
"name": "generate-sbom",
|
|
17
|
-
"version": "sha256:
|
|
23
|
+
"version": "sha256:9472206f8105"
|
|
18
24
|
}
|
|
19
25
|
]
|
|
20
26
|
},
|
|
21
27
|
"component": {
|
|
22
28
|
"type": "library",
|
|
23
|
-
"bom-ref": "pkg:npm/%40rakomi%2Freact-native@0.
|
|
29
|
+
"bom-ref": "pkg:npm/%40rakomi%2Freact-native@0.2.0",
|
|
24
30
|
"name": "@rakomi/react-native",
|
|
25
|
-
"version": "0.
|
|
26
|
-
"purl": "pkg:npm/%40rakomi%2Freact-native@0.
|
|
31
|
+
"version": "0.2.0",
|
|
32
|
+
"purl": "pkg:npm/%40rakomi%2Freact-native@0.2.0",
|
|
27
33
|
"licenses": [
|
|
28
34
|
{
|
|
29
35
|
"license": {
|
|
@@ -36,10 +42,10 @@
|
|
|
36
42
|
"components": [
|
|
37
43
|
{
|
|
38
44
|
"type": "library",
|
|
39
|
-
"bom-ref": "pkg:npm/@rakomi/sdk-core@0.
|
|
45
|
+
"bom-ref": "pkg:npm/@rakomi/sdk-core@0.2.0",
|
|
40
46
|
"name": "@rakomi/sdk-core",
|
|
41
|
-
"version": "0.
|
|
42
|
-
"purl": "pkg:npm/@rakomi/sdk-core@0.
|
|
47
|
+
"version": "0.2.0",
|
|
48
|
+
"purl": "pkg:npm/@rakomi/sdk-core@0.2.0",
|
|
43
49
|
"licenses": [
|
|
44
50
|
{
|
|
45
51
|
"license": {
|
|
@@ -50,10 +56,10 @@
|
|
|
50
56
|
},
|
|
51
57
|
{
|
|
52
58
|
"type": "library",
|
|
53
|
-
"bom-ref": "pkg:npm/jose@6.2.
|
|
59
|
+
"bom-ref": "pkg:npm/jose@6.2.9",
|
|
54
60
|
"name": "jose",
|
|
55
|
-
"version": "6.2.
|
|
56
|
-
"purl": "pkg:npm/jose@6.2.
|
|
61
|
+
"version": "6.2.9",
|
|
62
|
+
"purl": "pkg:npm/jose@6.2.9",
|
|
57
63
|
"licenses": [
|
|
58
64
|
{
|
|
59
65
|
"license": {
|