@rakomi/react-native 0.3.1 → 0.5.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/README.md +10 -0
- package/SECURITY.md +2 -2
- package/THIRD-PARTY-NOTICES +1 -1
- package/dist/index.cjs +55 -8
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +56 -9
- package/package.json +4 -4
- package/sbom.cdx.json +9 -9
package/README.md
CHANGED
|
@@ -24,6 +24,16 @@ React Native / Expo SDK for [Rakomi](https://rakomi.com) — EU-native auth-as-a
|
|
|
24
24
|
> Token-manager runtime, JWKS verification, social-provider deep-link auto-handler, a bare-RN adapter
|
|
25
25
|
> example, and a demo app land in subsequent 0.x releases.
|
|
26
26
|
|
|
27
|
+
## Ask your agent
|
|
28
|
+
|
|
29
|
+
Add the Rakomi MCP server to your coding agent, then ask it directly:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
claude mcp add --transport http rakomi https://mcp.rakomi.com/mcp
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
"List the AI agents connected to my Rakomi tenant."
|
|
36
|
+
|
|
27
37
|
## Install
|
|
28
38
|
|
|
29
39
|
```sh
|
package/SECURITY.md
CHANGED
|
@@ -9,8 +9,8 @@ While these packages remain pre-1.0 (`0.x`), they carry **no stability or suppor
|
|
|
9
9
|
(SemVer 2.0.0 §4); the latest `0.x` line receives security updates on a best-effort basis.
|
|
10
10
|
|
|
11
11
|
From version **1.0** onward, Rakomi maintains the current (N) and previous (N-1) MAJOR in parallel, with N-1 receiving
|
|
12
|
-
security-only fixes.
|
|
13
|
-
**
|
|
12
|
+
security-only fixes. For each MAJOR version of the Rakomi SDKs, CRE8EVE commits to a CRA support period of
|
|
13
|
+
**at least five years (60 months)**, meeting the support-period requirement of **CRA Art. 13(8)**. The authoritative, machine-readable support windows are published at
|
|
14
14
|
[`https://api.rakomi.com/.well-known/sdk-support.json`](https://api.rakomi.com/.well-known/sdk-support.json)
|
|
15
15
|
and rendered for humans on the [SDK Support & Lifecycle page](https://rakomi.com/sdk-support). This
|
|
16
16
|
document points at that single source rather than re-typing dated rows.
|
package/THIRD-PARTY-NOTICES
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
|
|
2
2
|
|
|
3
|
-
For the npm package: @rakomi/react-native@0.
|
|
3
|
+
For the npm package: @rakomi/react-native@0.5.0
|
|
4
4
|
|
|
5
5
|
This package's published bundle (`dist/`) INLINES the third-party components listed below.
|
|
6
6
|
Their copyright and permission notices are reproduced verbatim, as required by their licenses
|
package/dist/index.cjs
CHANGED
|
@@ -299,8 +299,19 @@ function deriveDefaultJwksUri(tokenEndpoint) {
|
|
|
299
299
|
}
|
|
300
300
|
var RAKOMI_PLATFORM_ISSUER_DEFAULT = "https://api.rakomi.com";
|
|
301
301
|
var RAKOMI_PLATFORM_AUDIENCE_DEFAULT = "https://api.rakomi.com";
|
|
302
|
-
function deriveDefaultIssuer(
|
|
303
|
-
|
|
302
|
+
function deriveDefaultIssuer(options) {
|
|
303
|
+
if (options.baseUrl) {
|
|
304
|
+
try {
|
|
305
|
+
new URL(options.baseUrl);
|
|
306
|
+
return sdkCore.resolveExpectedIssuer(options.baseUrl);
|
|
307
|
+
} catch {
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
try {
|
|
311
|
+
return new URL(options.tokenEndpoint).origin;
|
|
312
|
+
} catch {
|
|
313
|
+
return RAKOMI_PLATFORM_ISSUER_DEFAULT;
|
|
314
|
+
}
|
|
304
315
|
}
|
|
305
316
|
var REFRESH_TOKEN_PREFIX = "v1:";
|
|
306
317
|
var REFRESH_BUFFER_SECONDS = 60;
|
|
@@ -360,7 +371,7 @@ var TokenRuntime = class _TokenRuntime {
|
|
|
360
371
|
this.clearTimeoutFn = options.clearTimeout ?? globalThis.clearTimeout.bind(globalThis);
|
|
361
372
|
this.jwksUri = options.jwksUri ?? deriveDefaultJwksUri(options.tokenEndpoint);
|
|
362
373
|
this.jwksTtlMs = options.jwksTtlMs ?? 24 * 60 * 60 * 1e3;
|
|
363
|
-
this.expectedIssuer = options.expectedIssuer ?? deriveDefaultIssuer(options.tokenEndpoint);
|
|
374
|
+
this.expectedIssuer = options.expectedIssuer ?? deriveDefaultIssuer({ baseUrl: options.baseUrl, tokenEndpoint: options.tokenEndpoint });
|
|
364
375
|
this.expectedAudience = options.expectedAudience ?? RAKOMI_PLATFORM_AUDIENCE_DEFAULT;
|
|
365
376
|
}
|
|
366
377
|
/**
|
|
@@ -545,11 +556,32 @@ var TokenRuntime = class _TokenRuntime {
|
|
|
545
556
|
this.storageKeyResolved = await sdkCore.deriveTenantStorageKey(this.crypto, this.tenantId, "refresh_token");
|
|
546
557
|
return this.storageKeyResolved;
|
|
547
558
|
}
|
|
559
|
+
/**
|
|
560
|
+
* The persisted JWKS-cache slot, scoped per-host in addition to per-tenant.
|
|
561
|
+
*
|
|
562
|
+
* `deriveTenantStorageKey`'s own domain separation is tenant-only, which was correct while every
|
|
563
|
+
* tenant verified against ONE platform-wide JWKS document. Now that a tenant's issuer host can
|
|
564
|
+
* change across the app's lifetime (a custom domain bound after this SDK version first shipped, or
|
|
565
|
+
* simply a dev pointed at a different host between builds), a slot keyed by tenant alone would
|
|
566
|
+
* silently reuse a PREVIOUS host's persisted document as this instance's "fresh" cache on cold
|
|
567
|
+
* start — cross-host reuse this offline-verification path must never do. The host segment is not
|
|
568
|
+
* secret (it is the caller's own `jwksUri` configuration), so a plain suffix is enough; this only
|
|
569
|
+
* needs to be a stable, DIFFERENT key per host, not a cryptographically-opaque one.
|
|
570
|
+
*/
|
|
548
571
|
async resolveJwksKey() {
|
|
549
572
|
if (this.jwksKeyResolved) return this.jwksKeyResolved;
|
|
550
|
-
|
|
573
|
+
const base = await sdkCore.deriveTenantStorageKey(this.crypto, this.tenantId, "jwks_cache");
|
|
574
|
+
const host = this.jwksHost();
|
|
575
|
+
this.jwksKeyResolved = host ? `${base}.h.${host}` : base;
|
|
551
576
|
return this.jwksKeyResolved;
|
|
552
577
|
}
|
|
578
|
+
jwksHost() {
|
|
579
|
+
try {
|
|
580
|
+
return new URL(this.jwksUri).hostname;
|
|
581
|
+
} catch {
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
553
585
|
async getOrCreateJwksCache() {
|
|
554
586
|
if (this.jwksCache) return this.jwksCache;
|
|
555
587
|
const key = await this.resolveJwksKey();
|
|
@@ -1051,6 +1083,7 @@ function RakomiProvider(props) {
|
|
|
1051
1083
|
clientId: props.publishableKey,
|
|
1052
1084
|
tenantId: props.tenantId ?? props.publishableKey,
|
|
1053
1085
|
tokenEndpoint,
|
|
1086
|
+
baseUrl: props.baseUrl,
|
|
1054
1087
|
storage: adapter.storage,
|
|
1055
1088
|
http,
|
|
1056
1089
|
crypto: adapter.crypto,
|
|
@@ -1075,8 +1108,12 @@ function RakomiProvider(props) {
|
|
|
1075
1108
|
}
|
|
1076
1109
|
});
|
|
1077
1110
|
}, [adapter, http, props.publishableKey, props.tenantId, props.baseUrl, props.tokenEndpoint, props.biometric, props.biometricStrict, props.biometricPrompt, events, dpopSession]);
|
|
1111
|
+
const restoreStarted = react.useRef(false);
|
|
1078
1112
|
react.useEffect(() => {
|
|
1079
|
-
|
|
1113
|
+
if (!restoreStarted.current) {
|
|
1114
|
+
restoreStarted.current = true;
|
|
1115
|
+
void runtime.restore();
|
|
1116
|
+
}
|
|
1080
1117
|
return () => runtime.destroy();
|
|
1081
1118
|
}, [runtime]);
|
|
1082
1119
|
const debounceTimer = react.useRef(null);
|
|
@@ -1679,6 +1716,7 @@ function SignIn(props) {
|
|
|
1679
1716
|
const result = await sdkCore.verifyTotp({
|
|
1680
1717
|
http: ctx.http,
|
|
1681
1718
|
endpoint: totpVerifyEndpoint,
|
|
1719
|
+
apiKey: ctx.publishableKey,
|
|
1682
1720
|
challengeToken: step.challengeToken,
|
|
1683
1721
|
code
|
|
1684
1722
|
});
|
|
@@ -1760,11 +1798,20 @@ function SignUp(props) {
|
|
|
1760
1798
|
try {
|
|
1761
1799
|
const response = await ctx.http.fetch(props.registerEndpoint ?? "/v1/auth/register", {
|
|
1762
1800
|
method: "POST",
|
|
1763
|
-
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
1801
|
+
headers: { "Content-Type": "application/json", Accept: "application/json", "X-API-Key": ctx.publishableKey },
|
|
1764
1802
|
body: JSON.stringify({ email, password, returnTo })
|
|
1765
1803
|
});
|
|
1766
1804
|
if (!response.ok) {
|
|
1767
|
-
|
|
1805
|
+
let parsed = {};
|
|
1806
|
+
try {
|
|
1807
|
+
parsed = await response.json();
|
|
1808
|
+
} catch {
|
|
1809
|
+
}
|
|
1810
|
+
const requestId = sdkCore.extractRequestId(parsed);
|
|
1811
|
+
setStep({
|
|
1812
|
+
kind: "error",
|
|
1813
|
+
error: { code: "SIGN_IN_FAILED", message: parsed.detail ?? `Register failed (${response.status})`, ...requestId && { requestId } }
|
|
1814
|
+
});
|
|
1768
1815
|
} else {
|
|
1769
1816
|
setStep({ kind: "verification_required" });
|
|
1770
1817
|
}
|
|
@@ -2687,7 +2734,7 @@ function useMfa() {
|
|
|
2687
2734
|
return {
|
|
2688
2735
|
verifyTotp: async (challengeToken, code, endpoint) => {
|
|
2689
2736
|
const url = endpoint ?? `${ctx.baseUrl}/v1/auth/mfa/verify-login`;
|
|
2690
|
-
const result = await sdkCore.verifyTotp({ http: ctx.http, endpoint: url, challengeToken, code });
|
|
2737
|
+
const result = await sdkCore.verifyTotp({ http: ctx.http, endpoint: url, apiKey: ctx.publishableKey, challengeToken, code });
|
|
2691
2738
|
if (result.ok) {
|
|
2692
2739
|
const nonce = await ctx.beginAuthFlow();
|
|
2693
2740
|
await ctx.submitOAuthTokens({
|
package/dist/index.d.cts
CHANGED
|
@@ -495,7 +495,7 @@ declare const MAX_WEBHOOK_TOLERANCE = 600;
|
|
|
495
495
|
/** Portable webhook-verify error class — the value IS the cross-SDK conformance `error_class`. */
|
|
496
496
|
type WebhookVerifyErrorClass = 'invalid_signature' | 'invalid_secret' | 'missing_header' | 'timestamp_too_old' | 'timestamp_too_new' | 'invalid_body';
|
|
497
497
|
/** Open-set publisher event type (the known catalog widened with the base string for forward-compat). */
|
|
498
|
-
type PublisherEventType = 'app.installed' | 'app.uninstalled' | 'app.install.scope_bump' | 'app.install.receipts_revoked' | 'publisher.created' | 'publisher.domain_verified' | 'publisher.dpa_accepted' | 'app.created' | 'app.version_published' | 'app.state_changed' | 'publisher.review_requested' | 'publisher.review_denied' | 'publisher.review_stale' | 'publisher.verified' | 'publisher.deverified' | 'publisher.subscription_activated' | 'publisher.subscription_lapsed';
|
|
498
|
+
type PublisherEventType = 'app.installed' | 'app.uninstalled' | 'app.install.scope_bump' | 'app.install.receipts_revoked' | 'publisher.created' | 'publisher.domain_verified' | 'publisher.dpa_accepted' | 'app.created' | 'app.version_published' | 'app.state_changed' | 'publisher.review_requested' | 'publisher.review_denied' | 'publisher.review_stale' | 'publisher.verified' | 'publisher.deverified' | 'publisher.subscription_activated' | 'publisher.subscription_lapsed' | 'app.version_pending_review' | 'app.pending_version_approved' | 'app.pending_version_rejected';
|
|
499
499
|
type PublisherWebhookEventType = PublisherEventType | (string & {});
|
|
500
500
|
/** The flat publisher-webhook delivery body. Unknown fields tolerated (forward-compat). No end-user PII. */
|
|
501
501
|
interface PublisherWebhookEvent {
|
package/dist/index.d.ts
CHANGED
|
@@ -495,7 +495,7 @@ declare const MAX_WEBHOOK_TOLERANCE = 600;
|
|
|
495
495
|
/** Portable webhook-verify error class — the value IS the cross-SDK conformance `error_class`. */
|
|
496
496
|
type WebhookVerifyErrorClass = 'invalid_signature' | 'invalid_secret' | 'missing_header' | 'timestamp_too_old' | 'timestamp_too_new' | 'invalid_body';
|
|
497
497
|
/** Open-set publisher event type (the known catalog widened with the base string for forward-compat). */
|
|
498
|
-
type PublisherEventType = 'app.installed' | 'app.uninstalled' | 'app.install.scope_bump' | 'app.install.receipts_revoked' | 'publisher.created' | 'publisher.domain_verified' | 'publisher.dpa_accepted' | 'app.created' | 'app.version_published' | 'app.state_changed' | 'publisher.review_requested' | 'publisher.review_denied' | 'publisher.review_stale' | 'publisher.verified' | 'publisher.deverified' | 'publisher.subscription_activated' | 'publisher.subscription_lapsed';
|
|
498
|
+
type PublisherEventType = 'app.installed' | 'app.uninstalled' | 'app.install.scope_bump' | 'app.install.receipts_revoked' | 'publisher.created' | 'publisher.domain_verified' | 'publisher.dpa_accepted' | 'app.created' | 'app.version_published' | 'app.state_changed' | 'publisher.review_requested' | 'publisher.review_denied' | 'publisher.review_stale' | 'publisher.verified' | 'publisher.deverified' | 'publisher.subscription_activated' | 'publisher.subscription_lapsed' | 'app.version_pending_review' | 'app.pending_version_approved' | 'app.pending_version_rejected';
|
|
499
499
|
type PublisherWebhookEventType = PublisherEventType | (string & {});
|
|
500
500
|
/** The flat publisher-webhook delivery body. Unknown fields tolerated (forward-compat). No end-user PII. */
|
|
501
501
|
interface PublisherWebhookEvent {
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createContext, useMemo, useReducer, useRef, useEffect, useState, useCallback, useContext } from 'react';
|
|
2
|
-
import { createAuthorizationEndpointCache, EventLog, reduce, INITIAL_SNAPSHOT, createTranslator, isSignedIn, hasPermission, hasRole, extractOrgClaims, parseAuthConfigResponse, generatePkce, issueState, buildAuthorizationUrl, consumeState, exchangeAuthCode, parseOAuthCallbackError, scorePassword, isSafeUrl, passkeyError, decodeUser, assertPasskey, stepUpWithPasskey, registerPasskey, listPasskeys, renamePasskey, deletePasskey, verifyMagicLink, requestMagicLink, verifyEmailOtp, requestEmailOtp, register, verifyTotp, MfaStepUpUnavailableError, decodeSession, deriveTenantStorageKey, createJwksCache, refreshAccessToken, decodeJwtPayload, PasskeyCeremonyError } from '@rakomi/sdk-core';
|
|
2
|
+
import { createAuthorizationEndpointCache, EventLog, reduce, INITIAL_SNAPSHOT, createTranslator, isSignedIn, hasPermission, hasRole, extractOrgClaims, parseAuthConfigResponse, generatePkce, issueState, buildAuthorizationUrl, consumeState, exchangeAuthCode, parseOAuthCallbackError, scorePassword, isSafeUrl, passkeyError, decodeUser, assertPasskey, stepUpWithPasskey, registerPasskey, listPasskeys, renamePasskey, deletePasskey, verifyMagicLink, requestMagicLink, verifyEmailOtp, requestEmailOtp, register, verifyTotp, MfaStepUpUnavailableError, extractRequestId, decodeSession, deriveTenantStorageKey, createJwksCache, refreshAccessToken, decodeJwtPayload, PasskeyCeremonyError, resolveExpectedIssuer } from '@rakomi/sdk-core';
|
|
3
3
|
export { MfaStepUpRequiredError, MfaStepUpUnavailableError, base64url, createAuthorizationEndpointCache, createTranslator, decodeJwtPayload, decodeSession, decodeUser, deriveAuthorizationEndpointFallback, generatePkce, getErrorMessage, hasPermission, hasRole, isSafeUrl, networkError, parseOAuthCallbackError, parseTokenEndpointError, scorePassword, selectPluralForm } from '@rakomi/sdk-core';
|
|
4
4
|
import { jwtVerify } from 'jose';
|
|
5
5
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
@@ -298,8 +298,19 @@ function deriveDefaultJwksUri(tokenEndpoint) {
|
|
|
298
298
|
}
|
|
299
299
|
var RAKOMI_PLATFORM_ISSUER_DEFAULT = "https://api.rakomi.com";
|
|
300
300
|
var RAKOMI_PLATFORM_AUDIENCE_DEFAULT = "https://api.rakomi.com";
|
|
301
|
-
function deriveDefaultIssuer(
|
|
302
|
-
|
|
301
|
+
function deriveDefaultIssuer(options) {
|
|
302
|
+
if (options.baseUrl) {
|
|
303
|
+
try {
|
|
304
|
+
new URL(options.baseUrl);
|
|
305
|
+
return resolveExpectedIssuer(options.baseUrl);
|
|
306
|
+
} catch {
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
return new URL(options.tokenEndpoint).origin;
|
|
311
|
+
} catch {
|
|
312
|
+
return RAKOMI_PLATFORM_ISSUER_DEFAULT;
|
|
313
|
+
}
|
|
303
314
|
}
|
|
304
315
|
var REFRESH_TOKEN_PREFIX = "v1:";
|
|
305
316
|
var REFRESH_BUFFER_SECONDS = 60;
|
|
@@ -359,7 +370,7 @@ var TokenRuntime = class _TokenRuntime {
|
|
|
359
370
|
this.clearTimeoutFn = options.clearTimeout ?? globalThis.clearTimeout.bind(globalThis);
|
|
360
371
|
this.jwksUri = options.jwksUri ?? deriveDefaultJwksUri(options.tokenEndpoint);
|
|
361
372
|
this.jwksTtlMs = options.jwksTtlMs ?? 24 * 60 * 60 * 1e3;
|
|
362
|
-
this.expectedIssuer = options.expectedIssuer ?? deriveDefaultIssuer(options.tokenEndpoint);
|
|
373
|
+
this.expectedIssuer = options.expectedIssuer ?? deriveDefaultIssuer({ baseUrl: options.baseUrl, tokenEndpoint: options.tokenEndpoint });
|
|
363
374
|
this.expectedAudience = options.expectedAudience ?? RAKOMI_PLATFORM_AUDIENCE_DEFAULT;
|
|
364
375
|
}
|
|
365
376
|
/**
|
|
@@ -544,11 +555,32 @@ var TokenRuntime = class _TokenRuntime {
|
|
|
544
555
|
this.storageKeyResolved = await deriveTenantStorageKey(this.crypto, this.tenantId, "refresh_token");
|
|
545
556
|
return this.storageKeyResolved;
|
|
546
557
|
}
|
|
558
|
+
/**
|
|
559
|
+
* The persisted JWKS-cache slot, scoped per-host in addition to per-tenant.
|
|
560
|
+
*
|
|
561
|
+
* `deriveTenantStorageKey`'s own domain separation is tenant-only, which was correct while every
|
|
562
|
+
* tenant verified against ONE platform-wide JWKS document. Now that a tenant's issuer host can
|
|
563
|
+
* change across the app's lifetime (a custom domain bound after this SDK version first shipped, or
|
|
564
|
+
* simply a dev pointed at a different host between builds), a slot keyed by tenant alone would
|
|
565
|
+
* silently reuse a PREVIOUS host's persisted document as this instance's "fresh" cache on cold
|
|
566
|
+
* start — cross-host reuse this offline-verification path must never do. The host segment is not
|
|
567
|
+
* secret (it is the caller's own `jwksUri` configuration), so a plain suffix is enough; this only
|
|
568
|
+
* needs to be a stable, DIFFERENT key per host, not a cryptographically-opaque one.
|
|
569
|
+
*/
|
|
547
570
|
async resolveJwksKey() {
|
|
548
571
|
if (this.jwksKeyResolved) return this.jwksKeyResolved;
|
|
549
|
-
|
|
572
|
+
const base = await deriveTenantStorageKey(this.crypto, this.tenantId, "jwks_cache");
|
|
573
|
+
const host = this.jwksHost();
|
|
574
|
+
this.jwksKeyResolved = host ? `${base}.h.${host}` : base;
|
|
550
575
|
return this.jwksKeyResolved;
|
|
551
576
|
}
|
|
577
|
+
jwksHost() {
|
|
578
|
+
try {
|
|
579
|
+
return new URL(this.jwksUri).hostname;
|
|
580
|
+
} catch {
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
552
584
|
async getOrCreateJwksCache() {
|
|
553
585
|
if (this.jwksCache) return this.jwksCache;
|
|
554
586
|
const key = await this.resolveJwksKey();
|
|
@@ -1050,6 +1082,7 @@ function RakomiProvider(props) {
|
|
|
1050
1082
|
clientId: props.publishableKey,
|
|
1051
1083
|
tenantId: props.tenantId ?? props.publishableKey,
|
|
1052
1084
|
tokenEndpoint,
|
|
1085
|
+
baseUrl: props.baseUrl,
|
|
1053
1086
|
storage: adapter.storage,
|
|
1054
1087
|
http,
|
|
1055
1088
|
crypto: adapter.crypto,
|
|
@@ -1074,8 +1107,12 @@ function RakomiProvider(props) {
|
|
|
1074
1107
|
}
|
|
1075
1108
|
});
|
|
1076
1109
|
}, [adapter, http, props.publishableKey, props.tenantId, props.baseUrl, props.tokenEndpoint, props.biometric, props.biometricStrict, props.biometricPrompt, events, dpopSession]);
|
|
1110
|
+
const restoreStarted = useRef(false);
|
|
1077
1111
|
useEffect(() => {
|
|
1078
|
-
|
|
1112
|
+
if (!restoreStarted.current) {
|
|
1113
|
+
restoreStarted.current = true;
|
|
1114
|
+
void runtime.restore();
|
|
1115
|
+
}
|
|
1079
1116
|
return () => runtime.destroy();
|
|
1080
1117
|
}, [runtime]);
|
|
1081
1118
|
const debounceTimer = useRef(null);
|
|
@@ -1678,6 +1715,7 @@ function SignIn(props) {
|
|
|
1678
1715
|
const result = await verifyTotp({
|
|
1679
1716
|
http: ctx.http,
|
|
1680
1717
|
endpoint: totpVerifyEndpoint,
|
|
1718
|
+
apiKey: ctx.publishableKey,
|
|
1681
1719
|
challengeToken: step.challengeToken,
|
|
1682
1720
|
code
|
|
1683
1721
|
});
|
|
@@ -1759,11 +1797,20 @@ function SignUp(props) {
|
|
|
1759
1797
|
try {
|
|
1760
1798
|
const response = await ctx.http.fetch(props.registerEndpoint ?? "/v1/auth/register", {
|
|
1761
1799
|
method: "POST",
|
|
1762
|
-
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
1800
|
+
headers: { "Content-Type": "application/json", Accept: "application/json", "X-API-Key": ctx.publishableKey },
|
|
1763
1801
|
body: JSON.stringify({ email, password, returnTo })
|
|
1764
1802
|
});
|
|
1765
1803
|
if (!response.ok) {
|
|
1766
|
-
|
|
1804
|
+
let parsed = {};
|
|
1805
|
+
try {
|
|
1806
|
+
parsed = await response.json();
|
|
1807
|
+
} catch {
|
|
1808
|
+
}
|
|
1809
|
+
const requestId = extractRequestId(parsed);
|
|
1810
|
+
setStep({
|
|
1811
|
+
kind: "error",
|
|
1812
|
+
error: { code: "SIGN_IN_FAILED", message: parsed.detail ?? `Register failed (${response.status})`, ...requestId && { requestId } }
|
|
1813
|
+
});
|
|
1767
1814
|
} else {
|
|
1768
1815
|
setStep({ kind: "verification_required" });
|
|
1769
1816
|
}
|
|
@@ -2686,7 +2733,7 @@ function useMfa() {
|
|
|
2686
2733
|
return {
|
|
2687
2734
|
verifyTotp: async (challengeToken, code, endpoint) => {
|
|
2688
2735
|
const url = endpoint ?? `${ctx.baseUrl}/v1/auth/mfa/verify-login`;
|
|
2689
|
-
const result = await verifyTotp({ http: ctx.http, endpoint: url, challengeToken, code });
|
|
2736
|
+
const result = await verifyTotp({ http: ctx.http, endpoint: url, apiKey: ctx.publishableKey, challengeToken, code });
|
|
2690
2737
|
if (result.ok) {
|
|
2691
2738
|
const nonce = await ctx.beginAuthFlow();
|
|
2692
2739
|
await ctx.submitOAuthTokens({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rakomi/react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "React Native / Expo SDK for Rakomi authentication.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"rakomi",
|
|
@@ -83,16 +83,16 @@
|
|
|
83
83
|
}
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
|
-
"@rakomi/react": "0.
|
|
86
|
+
"@rakomi/react": "0.5.0"
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|
|
89
89
|
"jose": "^6.2.3",
|
|
90
|
-
"@rakomi/sdk-core": "^0.
|
|
90
|
+
"@rakomi/sdk-core": "^0.5.0"
|
|
91
91
|
},
|
|
92
92
|
"scripts": {
|
|
93
93
|
"build": "NODE_OPTIONS=--max-old-space-size=4096 tsup",
|
|
94
94
|
"typecheck": "tsc --noEmit && tsc -p tsconfig.typetest.json",
|
|
95
95
|
"lint": "eslint src/ test/ --max-warnings=0",
|
|
96
|
-
"test": "vitest run"
|
|
96
|
+
"test": "node ../../scripts/assert-not-story-agent-worktree.mjs test && node ../../scripts/ci/vitest-fork-crash-retry.mjs --results ./test-results.json -- vitest run"
|
|
97
97
|
}
|
|
98
98
|
}
|
package/sbom.cdx.json
CHANGED
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
},
|
|
27
27
|
"component": {
|
|
28
28
|
"type": "library",
|
|
29
|
-
"bom-ref": "pkg:npm/%40rakomi%2Freact-native@0.
|
|
29
|
+
"bom-ref": "pkg:npm/%40rakomi%2Freact-native@0.5.0",
|
|
30
30
|
"name": "@rakomi/react-native",
|
|
31
|
-
"version": "0.
|
|
32
|
-
"purl": "pkg:npm/%40rakomi%2Freact-native@0.
|
|
31
|
+
"version": "0.5.0",
|
|
32
|
+
"purl": "pkg:npm/%40rakomi%2Freact-native@0.5.0",
|
|
33
33
|
"licenses": [
|
|
34
34
|
{
|
|
35
35
|
"license": {
|
|
@@ -42,10 +42,10 @@
|
|
|
42
42
|
"components": [
|
|
43
43
|
{
|
|
44
44
|
"type": "library",
|
|
45
|
-
"bom-ref": "pkg:npm/@rakomi/sdk-core@0.
|
|
45
|
+
"bom-ref": "pkg:npm/@rakomi/sdk-core@0.5.0",
|
|
46
46
|
"name": "@rakomi/sdk-core",
|
|
47
|
-
"version": "0.
|
|
48
|
-
"purl": "pkg:npm/@rakomi/sdk-core@0.
|
|
47
|
+
"version": "0.5.0",
|
|
48
|
+
"purl": "pkg:npm/@rakomi/sdk-core@0.5.0",
|
|
49
49
|
"licenses": [
|
|
50
50
|
{
|
|
51
51
|
"license": {
|
|
@@ -56,10 +56,10 @@
|
|
|
56
56
|
},
|
|
57
57
|
{
|
|
58
58
|
"type": "library",
|
|
59
|
-
"bom-ref": "pkg:npm/jose@6.2.
|
|
59
|
+
"bom-ref": "pkg:npm/jose@6.2.12",
|
|
60
60
|
"name": "jose",
|
|
61
|
-
"version": "6.2.
|
|
62
|
-
"purl": "pkg:npm/jose@6.2.
|
|
61
|
+
"version": "6.2.12",
|
|
62
|
+
"purl": "pkg:npm/jose@6.2.12",
|
|
63
63
|
"licenses": [
|
|
64
64
|
{
|
|
65
65
|
"license": {
|