@oxyhq/services 21.0.0 → 21.0.1

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.
Files changed (88) hide show
  1. package/lib/commonjs/index.js +13 -0
  2. package/lib/commonjs/index.js.map +1 -1
  3. package/lib/commonjs/ui/components/OxyAccountDialog.js +48 -4
  4. package/lib/commonjs/ui/components/OxyAccountDialog.js.map +1 -1
  5. package/lib/commonjs/ui/context/OxyContext.js +69 -1
  6. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  7. package/lib/commonjs/ui/context/passkeyFlow.js +117 -0
  8. package/lib/commonjs/ui/context/passkeyFlow.js.map +1 -0
  9. package/lib/commonjs/ui/hooks/queries/queryKeys.js +18 -2
  10. package/lib/commonjs/ui/hooks/queries/queryKeys.js.map +1 -1
  11. package/lib/commonjs/ui/hooks/queries/useAuthMethods.js +49 -0
  12. package/lib/commonjs/ui/hooks/queries/useAuthMethods.js.map +1 -0
  13. package/lib/commonjs/webauthn/passkeyClient.js +39 -0
  14. package/lib/commonjs/webauthn/passkeyClient.js.map +1 -0
  15. package/lib/commonjs/webauthn/passkeyClient.native.js +34 -0
  16. package/lib/commonjs/webauthn/passkeyClient.native.js.map +1 -0
  17. package/lib/commonjs/webauthn/passkeyClient.web.js +57 -0
  18. package/lib/commonjs/webauthn/passkeyClient.web.js.map +1 -0
  19. package/lib/module/index.js +2 -1
  20. package/lib/module/index.js.map +1 -1
  21. package/lib/module/ui/components/OxyAccountDialog.js +48 -4
  22. package/lib/module/ui/components/OxyAccountDialog.js.map +1 -1
  23. package/lib/module/ui/context/OxyContext.js +69 -1
  24. package/lib/module/ui/context/OxyContext.js.map +1 -1
  25. package/lib/module/ui/context/passkeyFlow.js +110 -0
  26. package/lib/module/ui/context/passkeyFlow.js.map +1 -0
  27. package/lib/module/ui/hooks/queries/queryKeys.js +15 -0
  28. package/lib/module/ui/hooks/queries/queryKeys.js.map +1 -1
  29. package/lib/module/ui/hooks/queries/useAuthMethods.js +45 -0
  30. package/lib/module/ui/hooks/queries/useAuthMethods.js.map +1 -0
  31. package/lib/module/webauthn/passkeyClient.js +33 -0
  32. package/lib/module/webauthn/passkeyClient.js.map +1 -0
  33. package/lib/module/webauthn/passkeyClient.native.js +28 -0
  34. package/lib/module/webauthn/passkeyClient.native.js.map +1 -0
  35. package/lib/module/webauthn/passkeyClient.web.js +52 -0
  36. package/lib/module/webauthn/passkeyClient.web.js.map +1 -0
  37. package/lib/typescript/commonjs/index.d.ts +2 -1
  38. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  39. package/lib/typescript/commonjs/ui/components/OxyAccountDialog.d.ts.map +1 -1
  40. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  41. package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts +26 -0
  42. package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
  43. package/lib/typescript/commonjs/ui/context/passkeyFlow.d.ts +85 -0
  44. package/lib/typescript/commonjs/ui/context/passkeyFlow.d.ts.map +1 -0
  45. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +2 -2
  46. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  47. package/lib/typescript/commonjs/ui/hooks/queries/queryKeys.d.ts +9 -0
  48. package/lib/typescript/commonjs/ui/hooks/queries/queryKeys.d.ts.map +1 -1
  49. package/lib/typescript/commonjs/ui/hooks/queries/useAuthMethods.d.ts +212 -0
  50. package/lib/typescript/commonjs/ui/hooks/queries/useAuthMethods.d.ts.map +1 -0
  51. package/lib/typescript/commonjs/webauthn/passkeyClient.d.ts +20 -0
  52. package/lib/typescript/commonjs/webauthn/passkeyClient.d.ts.map +1 -0
  53. package/lib/typescript/commonjs/webauthn/passkeyClient.native.d.ts +15 -0
  54. package/lib/typescript/commonjs/webauthn/passkeyClient.native.d.ts.map +1 -0
  55. package/lib/typescript/commonjs/webauthn/passkeyClient.web.d.ts +36 -0
  56. package/lib/typescript/commonjs/webauthn/passkeyClient.web.d.ts.map +1 -0
  57. package/lib/typescript/module/index.d.ts +2 -1
  58. package/lib/typescript/module/index.d.ts.map +1 -1
  59. package/lib/typescript/module/ui/components/OxyAccountDialog.d.ts.map +1 -1
  60. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  61. package/lib/typescript/module/ui/context/oxyContextTypes.d.ts +26 -0
  62. package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
  63. package/lib/typescript/module/ui/context/passkeyFlow.d.ts +85 -0
  64. package/lib/typescript/module/ui/context/passkeyFlow.d.ts.map +1 -0
  65. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +2 -2
  66. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  67. package/lib/typescript/module/ui/hooks/queries/queryKeys.d.ts +9 -0
  68. package/lib/typescript/module/ui/hooks/queries/queryKeys.d.ts.map +1 -1
  69. package/lib/typescript/module/ui/hooks/queries/useAuthMethods.d.ts +212 -0
  70. package/lib/typescript/module/ui/hooks/queries/useAuthMethods.d.ts.map +1 -0
  71. package/lib/typescript/module/webauthn/passkeyClient.d.ts +20 -0
  72. package/lib/typescript/module/webauthn/passkeyClient.d.ts.map +1 -0
  73. package/lib/typescript/module/webauthn/passkeyClient.native.d.ts +15 -0
  74. package/lib/typescript/module/webauthn/passkeyClient.native.d.ts.map +1 -0
  75. package/lib/typescript/module/webauthn/passkeyClient.web.d.ts +36 -0
  76. package/lib/typescript/module/webauthn/passkeyClient.web.d.ts.map +1 -0
  77. package/package.json +4 -3
  78. package/src/index.ts +2 -0
  79. package/src/ui/components/OxyAccountDialog.tsx +57 -0
  80. package/src/ui/context/OxyContext.tsx +76 -0
  81. package/src/ui/context/__tests__/passkeyFlow.test.ts +237 -0
  82. package/src/ui/context/oxyContextTypes.ts +21 -0
  83. package/src/ui/context/passkeyFlow.ts +146 -0
  84. package/src/ui/hooks/queries/queryKeys.ts +14 -0
  85. package/src/ui/hooks/queries/useAuthMethods.ts +45 -0
  86. package/src/webauthn/passkeyClient.native.ts +26 -0
  87. package/src/webauthn/passkeyClient.ts +31 -0
  88. package/src/webauthn/passkeyClient.web.ts +56 -0
@@ -71,6 +71,12 @@ export const queryKeys = {
71
71
  [...queryKeys.security.all, 'infinite', limit, eventType] as const,
72
72
  },
73
73
 
74
+ // Linked authentication methods (passwords, identity keys, passkeys, social)
75
+ authMethods: {
76
+ all: ['authMethods'] as const,
77
+ list: (userId?: string) => [...queryKeys.authMethods.all, 'list', userId || 'current'] as const,
78
+ },
79
+
74
80
  // Storage usage queries
75
81
  storage: {
76
82
  all: ['storage'] as const,
@@ -159,6 +165,14 @@ export const invalidateStorageQueries = (queryClient: QueryClient): void => {
159
165
  queryClient.invalidateQueries({ queryKey: queryKeys.storage.all });
160
166
  };
161
167
 
168
+ /**
169
+ * Helper to invalidate the user's linked auth-methods list (call after linking
170
+ * or removing a passkey / password / identity key).
171
+ */
172
+ export const invalidateAuthMethodsQueries = (queryClient: QueryClient): void => {
173
+ queryClient.invalidateQueries({ queryKey: queryKeys.authMethods.all });
174
+ };
175
+
162
176
  /**
163
177
  * Helper to invalidate all payments / wallet / subscription queries
164
178
  */
@@ -0,0 +1,45 @@
1
+ import { useMemo } from 'react';
2
+ import { useQuery } from '@tanstack/react-query';
3
+ import type { AuthMethodEntry, AuthMethodsResponse } from '@oxyhq/contracts';
4
+ import { queryKeys } from './queryKeys';
5
+ import { useOxy } from '../../context/OxyContext';
6
+
7
+ /** Stable empty list so the derived selectors keep a stable reference while loading. */
8
+ const EMPTY_METHODS: readonly AuthMethodEntry[] = Object.freeze([]);
9
+
10
+ /**
11
+ * The current account's linked authentication methods (`GET /auth/methods`),
12
+ * plus convenience projections. Backs the security / account surfaces that let a
13
+ * user see and manage their passwords, identity key, social logins, and passkeys.
14
+ *
15
+ * `passkeys` is the `type === 'webauthn'` subset — one entry per registered
16
+ * credential. Invalidate via `queryKeys.authMethods.all` after linking a passkey
17
+ * (`useOxy().addPasskey()` does this) or removing one.
18
+ */
19
+ export const useAuthMethods = (options?: { enabled?: boolean }) => {
20
+ const { oxyServices, activeSessionId } = useOxy();
21
+
22
+ const query = useQuery<AuthMethodsResponse>({
23
+ queryKey: queryKeys.authMethods.list(),
24
+ queryFn: () => oxyServices.listAuthMethods(),
25
+ enabled: options?.enabled !== false && !!activeSessionId,
26
+ staleTime: 5 * 60 * 1000, // 5 minutes
27
+ gcTime: 10 * 60 * 1000, // 10 minutes
28
+ });
29
+
30
+ const methods = query.data?.methods ?? EMPTY_METHODS;
31
+ const passkeys = useMemo(
32
+ () => methods.filter((method) => method.type === 'webauthn'),
33
+ [methods],
34
+ );
35
+
36
+ return {
37
+ ...query,
38
+ /** Every linked auth method. Empty while loading. */
39
+ methods,
40
+ /** The registered passkeys (a `type === 'webauthn'` subset of `methods`). */
41
+ passkeys,
42
+ /** The account's DID, or `null` while loading. */
43
+ did: query.data?.did ?? null,
44
+ };
45
+ };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Passkey / WebAuthn ceremony client — NATIVE implementation.
3
+ *
4
+ * React Native has no browser WebAuthn API, and Oxy's native passkey story is
5
+ * Commons (shared-keychain / QR handoff), NOT `navigator.credentials`. So this
6
+ * variant imports NOTHING browser-specific and mirrors the clean default:
7
+ * unsupported, and the ceremony helpers throw if a caller skipped the gate.
8
+ */
9
+
10
+ const WEBAUTHN_WEB_ONLY_MESSAGE =
11
+ 'WebAuthn is web-only; native uses Commons. Passkeys are not available on this platform.';
12
+
13
+ /** Always `false` on native — there is no browser WebAuthn API. */
14
+ export function isPasskeySupported(): boolean {
15
+ return false;
16
+ }
17
+
18
+ /** Not available on native — throws. Guard with {@link isPasskeySupported} first. */
19
+ export async function runRegistrationCeremony(_optionsJSON: unknown): Promise<unknown> {
20
+ throw new Error(WEBAUTHN_WEB_ONLY_MESSAGE);
21
+ }
22
+
23
+ /** Not available on native — throws. Guard with {@link isPasskeySupported} first. */
24
+ export async function runAuthenticationCeremony(_optionsJSON: unknown): Promise<unknown> {
25
+ throw new Error(WEBAUTHN_WEB_ONLY_MESSAGE);
26
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Passkey / WebAuthn ceremony client — clean default (non-web) implementation.
3
+ *
4
+ * This is the base module TypeScript resolves for TYPES and any bundler that
5
+ * does not pick a platform variant. It intentionally imports NOTHING browser-
6
+ * specific (no `@simplewebauthn/browser`) so it stays safe for Metro, native
7
+ * builds, and SSR. The real ceremony lives in `passkeyClient.web.ts`; native
8
+ * passkeys are Commons' job, so there is no WebAuthn ceremony here.
9
+ *
10
+ * `isPasskeySupported()` returns `false` (no relying-party origin / no browser
11
+ * WebAuthn API), and the ceremony helpers throw a clear error so a caller that
12
+ * skipped the support gate fails loudly instead of silently.
13
+ */
14
+
15
+ const WEBAUTHN_WEB_ONLY_MESSAGE =
16
+ 'WebAuthn is web-only; native uses Commons. Passkeys are not available on this platform.';
17
+
18
+ /** Always `false` off the web — there is no relying-party origin or WebAuthn API. */
19
+ export function isPasskeySupported(): boolean {
20
+ return false;
21
+ }
22
+
23
+ /** Not available off the web — throws. Guard with {@link isPasskeySupported} first. */
24
+ export async function runRegistrationCeremony(_optionsJSON: unknown): Promise<unknown> {
25
+ throw new Error(WEBAUTHN_WEB_ONLY_MESSAGE);
26
+ }
27
+
28
+ /** Not available off the web — throws. Guard with {@link isPasskeySupported} first. */
29
+ export async function runAuthenticationCeremony(_optionsJSON: unknown): Promise<unknown> {
30
+ throw new Error(WEBAUTHN_WEB_ONLY_MESSAGE);
31
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Passkey / WebAuthn ceremony client — WEB implementation.
3
+ *
4
+ * The browser half of the passkey flow: it drives `navigator.credentials`
5
+ * through `@simplewebauthn/browser`, turning the OPAQUE options the server
6
+ * emitted (`OxyServices.webauthn{Register,Login}Options`) into the opaque
7
+ * response the matching `verify` endpoint consumes. Oxy never owns those wire
8
+ * shapes — `@simplewebauthn/browser` does — so options flow in as `unknown` and
9
+ * the single cast at this boundary hands them to the library's own types.
10
+ *
11
+ * This file is selected ONLY on web (react-native-web / Vite RNW): the sibling
12
+ * `passkeyClient.native.ts` + the clean default `passkeyClient.ts` deliberately
13
+ * do NOT import `@simplewebauthn/browser` (a browser-only dependency), per the
14
+ * platform-split rule. Native passkeys are Commons' job — there is no WebAuthn
15
+ * ceremony off the web.
16
+ */
17
+
18
+ import {
19
+ startRegistration,
20
+ startAuthentication,
21
+ type PublicKeyCredentialCreationOptionsJSON,
22
+ type PublicKeyCredentialRequestOptionsJSON,
23
+ type RegistrationResponseJSON,
24
+ type AuthenticationResponseJSON,
25
+ } from '@simplewebauthn/browser';
26
+
27
+ /**
28
+ * True iff this browser can run a WebAuthn ceremony (exposes the
29
+ * `PublicKeyCredential` global). Gate all passkey UI/entrypoints on this so an
30
+ * unsupported browser fails loudly rather than throwing mid-ceremony.
31
+ */
32
+ export function isPasskeySupported(): boolean {
33
+ return typeof window !== 'undefined' && typeof window.PublicKeyCredential !== 'undefined';
34
+ }
35
+
36
+ /**
37
+ * Run the WebAuthn REGISTRATION ceremony (`navigator.credentials.create`) from
38
+ * the server's opaque creation options, returning the opaque attestation
39
+ * response for `OxyServices.webauthnRegisterVerify`.
40
+ */
41
+ export async function runRegistrationCeremony(
42
+ optionsJSON: unknown,
43
+ ): Promise<RegistrationResponseJSON> {
44
+ return startRegistration({ optionsJSON: optionsJSON as PublicKeyCredentialCreationOptionsJSON });
45
+ }
46
+
47
+ /**
48
+ * Run the WebAuthn AUTHENTICATION ceremony (`navigator.credentials.get`) from
49
+ * the server's opaque request options, returning the opaque assertion response
50
+ * for `OxyServices.webauthnLoginVerify`.
51
+ */
52
+ export async function runAuthenticationCeremony(
53
+ optionsJSON: unknown,
54
+ ): Promise<AuthenticationResponseJSON> {
55
+ return startAuthentication({ optionsJSON: optionsJSON as PublicKeyCredentialRequestOptionsJSON });
56
+ }