@rakomi/react-native 0.3.0 → 0.4.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 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. The CRA support period for each MAJOR is determined in accordance with
13
- **CRA Art. 13(8)** — at least five years, or the product's expected use time where shorter. The authoritative, machine-readable support windows are published at
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.
@@ -1,6 +1,6 @@
1
1
  THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
2
2
 
3
- For the npm package: @rakomi/react-native@0.3.0
3
+ For the npm package: @rakomi/react-native@0.4.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
@@ -1075,8 +1075,12 @@ function RakomiProvider(props) {
1075
1075
  }
1076
1076
  });
1077
1077
  }, [adapter, http, props.publishableKey, props.tenantId, props.baseUrl, props.tokenEndpoint, props.biometric, props.biometricStrict, props.biometricPrompt, events, dpopSession]);
1078
+ const restoreStarted = react.useRef(false);
1078
1079
  react.useEffect(() => {
1079
- void runtime.restore();
1080
+ if (!restoreStarted.current) {
1081
+ restoreStarted.current = true;
1082
+ void runtime.restore();
1083
+ }
1080
1084
  return () => runtime.destroy();
1081
1085
  }, [runtime]);
1082
1086
  const debounceTimer = react.useRef(null);
@@ -1636,8 +1640,8 @@ function SignIn(props) {
1636
1640
  const [step, setStep] = react.useState({ kind: "methods" });
1637
1641
  const [code, setCode] = react.useState("");
1638
1642
  const [busy, setBusy] = react.useState(false);
1639
- const tokenEndpoint = props.tokenEndpoint ?? "/v1/auth/oauth/callback";
1640
- const totpVerifyEndpoint = props.totpVerifyEndpoint ?? "/v1/auth/mfa/totp/verify";
1643
+ const tokenEndpoint = props.tokenEndpoint ?? "/oauth/token";
1644
+ const totpVerifyEndpoint = props.totpVerifyEndpoint ?? "/v1/auth/mfa/verify-login";
1641
1645
  const providers = props.providers ?? ["google"];
1642
1646
  async function onProviderPress(provider) {
1643
1647
  if (busy) return;
@@ -1679,6 +1683,7 @@ function SignIn(props) {
1679
1683
  const result = await sdkCore.verifyTotp({
1680
1684
  http: ctx.http,
1681
1685
  endpoint: totpVerifyEndpoint,
1686
+ apiKey: ctx.publishableKey,
1682
1687
  challengeToken: step.challengeToken,
1683
1688
  code
1684
1689
  });
@@ -1760,11 +1765,20 @@ function SignUp(props) {
1760
1765
  try {
1761
1766
  const response = await ctx.http.fetch(props.registerEndpoint ?? "/v1/auth/register", {
1762
1767
  method: "POST",
1763
- headers: { "Content-Type": "application/json", Accept: "application/json" },
1768
+ headers: { "Content-Type": "application/json", Accept: "application/json", "X-API-Key": ctx.publishableKey },
1764
1769
  body: JSON.stringify({ email, password, returnTo })
1765
1770
  });
1766
1771
  if (!response.ok) {
1767
- setStep({ kind: "error", error: { code: "SIGN_IN_FAILED", message: `Register failed (${response.status})` } });
1772
+ let parsed = {};
1773
+ try {
1774
+ parsed = await response.json();
1775
+ } catch {
1776
+ }
1777
+ const requestId = sdkCore.extractRequestId(parsed);
1778
+ setStep({
1779
+ kind: "error",
1780
+ error: { code: "SIGN_IN_FAILED", message: parsed.detail ?? `Register failed (${response.status})`, ...requestId && { requestId } }
1781
+ });
1768
1782
  } else {
1769
1783
  setStep({ kind: "verification_required" });
1770
1784
  }
@@ -2686,8 +2700,8 @@ function useMfa() {
2686
2700
  const ctx = useRakomiContext();
2687
2701
  return {
2688
2702
  verifyTotp: async (challengeToken, code, endpoint) => {
2689
- const url = endpoint ?? `${ctx.baseUrl}/v1/auth/mfa/totp/verify`;
2690
- const result = await sdkCore.verifyTotp({ http: ctx.http, endpoint: url, challengeToken, code });
2703
+ const url = endpoint ?? `${ctx.baseUrl}/v1/auth/mfa/verify-login`;
2704
+ const result = await sdkCore.verifyTotp({ http: ctx.http, endpoint: url, apiKey: ctx.publishableKey, challengeToken, code });
2691
2705
  if (result.ok) {
2692
2706
  const nonce = await ctx.beginAuthFlow();
2693
2707
  await ctx.submitOAuthTokens({
package/dist/index.d.cts CHANGED
@@ -394,9 +394,9 @@ interface SignInProps {
394
394
  * path on the API host returns JSON, not the hosted login UI.
395
395
  */
396
396
  authorizationEndpoint?: string;
397
- /** Token endpoint, default `${baseUrl}/v1/auth/oauth/callback`. */
397
+ /** RFC 6749 §3.2 token endpoint used for the PKCE code→token exchange, default `${baseUrl}/oauth/token`. */
398
398
  tokenEndpoint?: string;
399
- /** MFA TOTP verify endpoint, default `${baseUrl}/v1/auth/mfa/totp/verify`. */
399
+ /** MFA TOTP verify endpoint, default `${baseUrl}/v1/auth/mfa/verify-login`. */
400
400
  totpVerifyEndpoint?: string;
401
401
  /** Social providers to render. Default: ['google']. */
402
402
  providers?: string[];
package/dist/index.d.ts CHANGED
@@ -394,9 +394,9 @@ interface SignInProps {
394
394
  * path on the API host returns JSON, not the hosted login UI.
395
395
  */
396
396
  authorizationEndpoint?: string;
397
- /** Token endpoint, default `${baseUrl}/v1/auth/oauth/callback`. */
397
+ /** RFC 6749 §3.2 token endpoint used for the PKCE code→token exchange, default `${baseUrl}/oauth/token`. */
398
398
  tokenEndpoint?: string;
399
- /** MFA TOTP verify endpoint, default `${baseUrl}/v1/auth/mfa/totp/verify`. */
399
+ /** MFA TOTP verify endpoint, default `${baseUrl}/v1/auth/mfa/verify-login`. */
400
400
  totpVerifyEndpoint?: string;
401
401
  /** Social providers to render. Default: ['google']. */
402
402
  providers?: string[];
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 } 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';
@@ -1074,8 +1074,12 @@ function RakomiProvider(props) {
1074
1074
  }
1075
1075
  });
1076
1076
  }, [adapter, http, props.publishableKey, props.tenantId, props.baseUrl, props.tokenEndpoint, props.biometric, props.biometricStrict, props.biometricPrompt, events, dpopSession]);
1077
+ const restoreStarted = useRef(false);
1077
1078
  useEffect(() => {
1078
- void runtime.restore();
1079
+ if (!restoreStarted.current) {
1080
+ restoreStarted.current = true;
1081
+ void runtime.restore();
1082
+ }
1079
1083
  return () => runtime.destroy();
1080
1084
  }, [runtime]);
1081
1085
  const debounceTimer = useRef(null);
@@ -1635,8 +1639,8 @@ function SignIn(props) {
1635
1639
  const [step, setStep] = useState({ kind: "methods" });
1636
1640
  const [code, setCode] = useState("");
1637
1641
  const [busy, setBusy] = useState(false);
1638
- const tokenEndpoint = props.tokenEndpoint ?? "/v1/auth/oauth/callback";
1639
- const totpVerifyEndpoint = props.totpVerifyEndpoint ?? "/v1/auth/mfa/totp/verify";
1642
+ const tokenEndpoint = props.tokenEndpoint ?? "/oauth/token";
1643
+ const totpVerifyEndpoint = props.totpVerifyEndpoint ?? "/v1/auth/mfa/verify-login";
1640
1644
  const providers = props.providers ?? ["google"];
1641
1645
  async function onProviderPress(provider) {
1642
1646
  if (busy) return;
@@ -1678,6 +1682,7 @@ function SignIn(props) {
1678
1682
  const result = await verifyTotp({
1679
1683
  http: ctx.http,
1680
1684
  endpoint: totpVerifyEndpoint,
1685
+ apiKey: ctx.publishableKey,
1681
1686
  challengeToken: step.challengeToken,
1682
1687
  code
1683
1688
  });
@@ -1759,11 +1764,20 @@ function SignUp(props) {
1759
1764
  try {
1760
1765
  const response = await ctx.http.fetch(props.registerEndpoint ?? "/v1/auth/register", {
1761
1766
  method: "POST",
1762
- headers: { "Content-Type": "application/json", Accept: "application/json" },
1767
+ headers: { "Content-Type": "application/json", Accept: "application/json", "X-API-Key": ctx.publishableKey },
1763
1768
  body: JSON.stringify({ email, password, returnTo })
1764
1769
  });
1765
1770
  if (!response.ok) {
1766
- setStep({ kind: "error", error: { code: "SIGN_IN_FAILED", message: `Register failed (${response.status})` } });
1771
+ let parsed = {};
1772
+ try {
1773
+ parsed = await response.json();
1774
+ } catch {
1775
+ }
1776
+ const requestId = extractRequestId(parsed);
1777
+ setStep({
1778
+ kind: "error",
1779
+ error: { code: "SIGN_IN_FAILED", message: parsed.detail ?? `Register failed (${response.status})`, ...requestId && { requestId } }
1780
+ });
1767
1781
  } else {
1768
1782
  setStep({ kind: "verification_required" });
1769
1783
  }
@@ -2685,8 +2699,8 @@ function useMfa() {
2685
2699
  const ctx = useRakomiContext();
2686
2700
  return {
2687
2701
  verifyTotp: async (challengeToken, code, endpoint) => {
2688
- const url = endpoint ?? `${ctx.baseUrl}/v1/auth/mfa/totp/verify`;
2689
- const result = await verifyTotp({ http: ctx.http, endpoint: url, challengeToken, code });
2702
+ const url = endpoint ?? `${ctx.baseUrl}/v1/auth/mfa/verify-login`;
2703
+ const result = await verifyTotp({ http: ctx.http, endpoint: url, apiKey: ctx.publishableKey, challengeToken, code });
2690
2704
  if (result.ok) {
2691
2705
  const nonce = await ctx.beginAuthFlow();
2692
2706
  await ctx.submitOAuthTokens({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rakomi/react-native",
3
- "version": "0.3.0",
3
+ "version": "0.4.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.3.0"
86
+ "@rakomi/react": "0.4.0"
87
87
  },
88
88
  "dependencies": {
89
89
  "jose": "^6.2.3",
90
- "@rakomi/sdk-core": "^0.3.0"
90
+ "@rakomi/sdk-core": "^0.4.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/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.3.0",
29
+ "bom-ref": "pkg:npm/%40rakomi%2Freact-native@0.4.0",
30
30
  "name": "@rakomi/react-native",
31
- "version": "0.3.0",
32
- "purl": "pkg:npm/%40rakomi%2Freact-native@0.3.0",
31
+ "version": "0.4.0",
32
+ "purl": "pkg:npm/%40rakomi%2Freact-native@0.4.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.3.0",
45
+ "bom-ref": "pkg:npm/@rakomi/sdk-core@0.4.0",
46
46
  "name": "@rakomi/sdk-core",
47
- "version": "0.3.0",
48
- "purl": "pkg:npm/@rakomi/sdk-core@0.3.0",
47
+ "version": "0.4.0",
48
+ "purl": "pkg:npm/@rakomi/sdk-core@0.4.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.10",
59
+ "bom-ref": "pkg:npm/jose@6.2.12",
60
60
  "name": "jose",
61
- "version": "6.2.10",
62
- "purl": "pkg:npm/jose@6.2.10",
61
+ "version": "6.2.12",
62
+ "purl": "pkg:npm/jose@6.2.12",
63
63
  "licenses": [
64
64
  {
65
65
  "license": {