@adonis-agora/authkit-react 0.15.0 → 0.17.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.
Files changed (52) hide show
  1. package/build/index.d.ts +92 -76
  2. package/build/index.js +48 -40
  3. package/build/src/authkit_provider.js +1 -1
  4. package/build/src/client/client.d.ts +1 -1
  5. package/build/src/client/client.js +4 -2
  6. package/build/src/client/context.d.ts +1 -1
  7. package/build/src/client/context.js +1 -1
  8. package/build/src/components/authenticated.js +1 -1
  9. package/build/src/components/can_permission.d.ts +1 -1
  10. package/build/src/components/can_permission.js +2 -2
  11. package/build/src/components/interaction_form.d.ts +298 -0
  12. package/build/src/components/interaction_form.js +6 -0
  13. package/build/src/components/key_rotation.js +1 -1
  14. package/build/src/components/magic_link_button.d.ts +8 -0
  15. package/build/src/components/magic_link_button.js +10 -0
  16. package/build/src/components/oauth_button.d.ts +296 -0
  17. package/build/src/components/oauth_button.js +13 -0
  18. package/build/src/components/organization_profile.js +3 -3
  19. package/build/src/components/passkey_button.d.ts +2 -2
  20. package/build/src/components/passkey_button.js +9 -10
  21. package/build/src/components/password_strength_meter.js +1 -4
  22. package/build/src/components/user_button.js +1 -1
  23. package/build/src/components/user_profile.js +1 -1
  24. package/build/src/config.d.ts +1 -1
  25. package/build/src/config.js +16 -19
  26. package/build/src/hooks/use_can.js +8 -8
  27. package/build/src/hooks/use_organization.js +3 -1
  28. package/build/src/hooks/use_passkey_assertion.d.ts +12 -0
  29. package/build/src/hooks/use_passkey_assertion.js +19 -0
  30. package/build/src/hooks/use_passkey_autofill.js +10 -11
  31. package/build/src/hooks/use_passkey_login.js +2 -2
  32. package/build/src/hooks/use_passkey_registration.d.ts +12 -0
  33. package/build/src/hooks/use_passkey_registration.js +19 -0
  34. package/build/src/hooks/use_profile.d.ts +1 -1
  35. package/build/src/hooks/use_profile.js +5 -1
  36. package/build/src/hooks/use_resource.js +1 -1
  37. package/build/src/hooks/use_sign_in.js +1 -1
  38. package/build/src/hooks/use_sign_out.js +1 -1
  39. package/build/src/interaction/urls.d.ts +12 -0
  40. package/build/src/interaction/urls.js +15 -0
  41. package/build/src/passkey/authenticate.d.ts +20 -1
  42. package/build/src/passkey/authenticate.js +51 -31
  43. package/build/src/passkey/classic_form.d.ts +9 -0
  44. package/build/src/passkey/classic_form.js +18 -0
  45. package/build/src/passkey/sudo.d.ts +12 -0
  46. package/build/src/passkey/sudo.js +20 -0
  47. package/build/src/queries/account/index.d.ts +1 -1
  48. package/build/src/queries/admin/index.d.ts +1 -1
  49. package/build/src/use_auth.js +1 -1
  50. package/build/src/utils.d.ts +1 -0
  51. package/build/src/utils.js +3 -0
  52. package/package.json +7 -1
@@ -0,0 +1,12 @@
1
+ export interface UsePasskeyRegistrationOptions {
2
+ optionsUrl: string;
3
+ actionUrl: string;
4
+ csrfToken?: string;
5
+ returnTo?: string | null;
6
+ }
7
+ export interface UsePasskeyRegistrationResult {
8
+ run: () => Promise<void>;
9
+ running: boolean;
10
+ error: Error | null;
11
+ }
12
+ export declare function usePasskeyRegistration(options: UsePasskeyRegistrationOptions): UsePasskeyRegistrationResult;
@@ -0,0 +1,19 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { runPasskeyRegistration } from '../passkey/sudo.js';
3
+ export function usePasskeyRegistration(options) {
4
+ const { optionsUrl, actionUrl, csrfToken, returnTo } = options;
5
+ const [running, setRunning] = useState(false);
6
+ const [error, setError] = useState(null);
7
+ const run = useCallback(async () => {
8
+ setError(null);
9
+ setRunning(true);
10
+ try {
11
+ await runPasskeyRegistration({ optionsUrl, actionUrl, csrfToken, returnTo });
12
+ }
13
+ catch (err) {
14
+ setError(err instanceof Error ? err : new Error(String(err)));
15
+ setRunning(false);
16
+ }
17
+ }, [optionsUrl, actionUrl, csrfToken, returnTo]);
18
+ return { run, running, error };
19
+ }
@@ -1,5 +1,5 @@
1
- import { type ResourceState } from './use_resource.js';
2
1
  import type { AuthUser } from '../types.js';
2
+ import { type ResourceState } from './use_resource.js';
3
3
  export interface ProfileUpdate {
4
4
  name?: string;
5
5
  avatarUrl?: string;
@@ -18,7 +18,11 @@ export function useProfile() {
18
18
  body: JSON.stringify(data),
19
19
  csrfToken: config.csrfToken,
20
20
  });
21
- setState({ data: updated ?? { ...user, ...data }, loading: false, error: null });
21
+ setState({
22
+ data: updated ?? { ...user, ...data },
23
+ loading: false,
24
+ error: null,
25
+ });
22
26
  }
23
27
  catch (err) {
24
28
  setState((s) => ({ ...s, loading: false, error: err }));
@@ -4,7 +4,7 @@ export async function jsonRequest(url, init = {}) {
4
4
  const res = await fetch(url, {
5
5
  credentials: 'same-origin',
6
6
  headers: {
7
- 'Accept': 'application/json',
7
+ Accept: 'application/json',
8
8
  ...(rest.body ? { 'Content-Type': 'application/json' } : {}),
9
9
  ...(csrfToken ? { 'X-CSRF-TOKEN': csrfToken } : {}),
10
10
  ...headers,
@@ -1,5 +1,5 @@
1
1
  import { useCallback } from 'react';
2
- import { useAuthkitConfig, buildAuthUrl } from '../config.js';
2
+ import { buildAuthUrl, useAuthkitConfig } from '../config.js';
3
3
  import { currentUrl } from '../utils.js';
4
4
  export function useSignIn() {
5
5
  const config = useAuthkitConfig();
@@ -1,5 +1,5 @@
1
1
  import { useCallback } from 'react';
2
- import { useAuthkitConfig, buildAuthUrl } from '../config.js';
2
+ import { buildAuthUrl, useAuthkitConfig } from '../config.js';
3
3
  export function useSignOut() {
4
4
  const config = useAuthkitConfig();
5
5
  const signOut = useCallback((opts) => {
@@ -0,0 +1,12 @@
1
+ export interface InteractionUrls {
2
+ identifier: string;
3
+ login: string;
4
+ magic: string;
5
+ signup: string;
6
+ switch: string;
7
+ passkeyOptions: string;
8
+ passkeyVerify: string;
9
+ }
10
+ export declare function interactionUrls(uid: string, basePath?: string): InteractionUrls;
11
+ export type InteractionPostStep = 'identifier' | 'login' | 'magic';
12
+ export declare function oauthRedirectUrl(provider: string, uid: string, basePath?: string): string;
@@ -0,0 +1,15 @@
1
+ export function interactionUrls(uid, basePath = '/auth/interaction') {
2
+ const base = `${basePath}/${uid}`;
3
+ return {
4
+ identifier: `${base}/identifier`,
5
+ login: `${base}/login`,
6
+ magic: `${base}/magic`,
7
+ signup: `${base}/signup`,
8
+ switch: `${base}/switch`,
9
+ passkeyOptions: `${base}/passkey/options`,
10
+ passkeyVerify: `${base}/passkey/verify`,
11
+ };
12
+ }
13
+ export function oauthRedirectUrl(provider, uid, basePath = '/auth') {
14
+ return `${basePath}/${provider}/redirect/${uid}`;
15
+ }
@@ -3,7 +3,15 @@ export type StartAuthenticationFn = (opts: {
3
3
  useBrowserAutofill?: boolean;
4
4
  verifyBrowserAutofillInput?: boolean;
5
5
  }, signal?: AbortSignal) => Promise<unknown>;
6
- export declare function loadStartAuthentication(): Promise<StartAuthenticationFn>;
6
+ export type StartRegistrationFn = (opts: {
7
+ optionsJSON: unknown;
8
+ }) => Promise<unknown>;
9
+ declare function importWebAuthnBrowser(): Promise<{
10
+ startAuthentication: StartAuthenticationFn;
11
+ startRegistration: StartRegistrationFn;
12
+ }>;
13
+ export declare function loadStartAuthentication(importModule?: typeof importWebAuthnBrowser): Promise<StartAuthenticationFn>;
14
+ export declare function loadStartRegistration(importModule?: typeof importWebAuthnBrowser): Promise<StartRegistrationFn>;
7
15
  export interface AuthenticatePasskeyOptions {
8
16
  optionsUrl: string;
9
17
  csrfToken?: string;
@@ -14,9 +22,20 @@ export interface PasskeyCeremonyDeps {
14
22
  loadStartAuthentication?: () => Promise<StartAuthenticationFn>;
15
23
  }
16
24
  export declare function authenticatePasskey(options: AuthenticatePasskeyOptions, deps?: PasskeyCeremonyDeps): Promise<string>;
25
+ export interface RegisterPasskeyOptions {
26
+ optionsUrl: string;
27
+ csrfToken?: string;
28
+ signal?: AbortSignal;
29
+ }
30
+ export interface PasskeyRegistrationDeps {
31
+ fetch?: typeof globalThis.fetch;
32
+ loadStartRegistration?: () => Promise<StartRegistrationFn>;
33
+ }
34
+ export declare function registerPasskey(options: RegisterPasskeyOptions, deps?: PasskeyRegistrationDeps): Promise<string>;
17
35
  export interface SubmitPasskeyVerificationOptions {
18
36
  verifyUrl: string;
19
37
  assertion: string;
20
38
  csrfToken?: string;
21
39
  }
22
40
  export declare function submitPasskeyVerification(options: SubmitPasskeyVerificationOptions): void;
41
+ export {};
@@ -6,28 +6,46 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
6
6
  }
7
7
  return path;
8
8
  };
9
- const CDN_URL = "https://cdn.jsdelivr.net/npm/@simplewebauthn/browser@13/dist/bundle/index.js";
10
- export async function loadStartAuthentication() {
9
+ import { submitClassicForm } from './classic_form.js';
10
+ async function importWebAuthnBrowser() {
11
+ return await import(__rewriteRelativeImportExtension('@simplewebauthn/browser'));
12
+ }
13
+ function missingPeerError(cause) {
14
+ return new Error('@adonis-agora/authkit-react: a passkey requer o pacote ' +
15
+ '`@simplewebauthn/browser`, que não foi encontrado. Instale-o no seu app ' +
16
+ '(`npm i @simplewebauthn/browser@^13`) — ele é uma peer dependency ' +
17
+ 'opcional, e não uma dependência direta, porque quem faz o bundle do ' +
18
+ 'frontend é o seu app: embutir uma segunda cópia da lib duplicaria o ' +
19
+ 'código e brigaria com a versão que você já usa. Este pacote também não ' +
20
+ 'cai num CDN público: isso colocaria um terceiro dentro do caminho de ' +
21
+ 'autenticação, em silêncio.', { cause });
22
+ }
23
+ export async function loadStartAuthentication(importModule = importWebAuthnBrowser) {
11
24
  try {
12
- const mod = await import(__rewriteRelativeImportExtension("@simplewebauthn/browser"));
25
+ const mod = await importModule();
13
26
  return mod.startAuthentication;
14
27
  }
15
- catch {
16
- const mod = await import(__rewriteRelativeImportExtension(CDN_URL));
17
- return mod.startAuthentication;
28
+ catch (cause) {
29
+ throw missingPeerError(cause);
18
30
  }
19
31
  }
20
- export async function authenticatePasskey(options, deps = {}) {
21
- const { optionsUrl, csrfToken, signal } = options;
22
- const doFetch = deps.fetch ?? globalThis.fetch;
23
- const loadFn = deps.loadStartAuthentication ?? loadStartAuthentication;
32
+ export async function loadStartRegistration(importModule = importWebAuthnBrowser) {
33
+ try {
34
+ const mod = await importModule();
35
+ return mod.startRegistration;
36
+ }
37
+ catch (cause) {
38
+ throw missingPeerError(cause);
39
+ }
40
+ }
41
+ async function fetchCeremonyOptions(optionsUrl, csrfToken, doFetch, signal) {
24
42
  const headers = {
25
- "content-type": "application/json",
43
+ 'content-type': 'application/json',
26
44
  };
27
45
  if (csrfToken)
28
- headers["x-csrf-token"] = csrfToken;
46
+ headers['x-csrf-token'] = csrfToken;
29
47
  const res = await doFetch(optionsUrl, {
30
- method: "POST",
48
+ method: 'POST',
31
49
  headers,
32
50
  body: JSON.stringify({}),
33
51
  signal,
@@ -35,29 +53,31 @@ export async function authenticatePasskey(options, deps = {}) {
35
53
  if (!res.ok) {
36
54
  throw new Error(`passkey options request failed: ${res.status}`);
37
55
  }
38
- const optionsJSON = await res.json();
56
+ return res.json();
57
+ }
58
+ export async function authenticatePasskey(options, deps = {}) {
59
+ const { optionsUrl, csrfToken, signal } = options;
60
+ const doFetch = deps.fetch ?? globalThis.fetch;
61
+ const loadFn = deps.loadStartAuthentication ?? loadStartAuthentication;
62
+ const optionsJSON = await fetchCeremonyOptions(optionsUrl, csrfToken, doFetch, signal);
39
63
  const startAuthentication = await loadFn();
40
64
  const assertion = await startAuthentication({ optionsJSON }, signal);
41
65
  return JSON.stringify(assertion);
42
66
  }
67
+ export async function registerPasskey(options, deps = {}) {
68
+ const { optionsUrl, csrfToken, signal } = options;
69
+ const doFetch = deps.fetch ?? globalThis.fetch;
70
+ const loadFn = deps.loadStartRegistration ?? loadStartRegistration;
71
+ const optionsJSON = await fetchCeremonyOptions(optionsUrl, csrfToken, doFetch, signal);
72
+ const startRegistration = await loadFn();
73
+ const attestation = await startRegistration({ optionsJSON });
74
+ return JSON.stringify(attestation);
75
+ }
43
76
  export function submitPasskeyVerification(options) {
44
77
  const { verifyUrl, assertion, csrfToken } = options;
45
- if (typeof document === "undefined")
46
- return;
47
- const form = document.createElement("form");
48
- form.method = "POST";
49
- form.action = verifyUrl;
50
- form.hidden = true;
78
+ const fields = {};
51
79
  if (csrfToken)
52
- form.appendChild(hiddenInput("_csrf", csrfToken));
53
- form.appendChild(hiddenInput("response", assertion));
54
- document.body.appendChild(form);
55
- form.submit();
56
- }
57
- function hiddenInput(name, value) {
58
- const input = document.createElement("input");
59
- input.type = "hidden";
60
- input.name = name;
61
- input.value = value;
62
- return input;
80
+ fields._csrf = csrfToken;
81
+ fields.response = assertion;
82
+ submitClassicForm({ action: verifyUrl, fields });
63
83
  }
@@ -0,0 +1,9 @@
1
+ export interface SubmitClassicFormOptions {
2
+ action: string;
3
+ fields: Record<string, string>;
4
+ method?: string;
5
+ }
6
+ export interface SubmitClassicFormDeps {
7
+ document?: Document;
8
+ }
9
+ export declare function submitClassicForm(options: SubmitClassicFormOptions, deps?: SubmitClassicFormDeps): void;
@@ -0,0 +1,18 @@
1
+ export function submitClassicForm(options, deps = {}) {
2
+ const doc = deps.document ?? (typeof document !== 'undefined' ? document : undefined);
3
+ if (!doc)
4
+ return;
5
+ const form = doc.createElement('form');
6
+ form.method = options.method ?? 'POST';
7
+ form.action = options.action;
8
+ form.hidden = true;
9
+ for (const [name, value] of Object.entries(options.fields)) {
10
+ const input = doc.createElement('input');
11
+ input.type = 'hidden';
12
+ input.name = name;
13
+ input.value = value;
14
+ form.appendChild(input);
15
+ }
16
+ doc.body.appendChild(form);
17
+ form.submit();
18
+ }
@@ -0,0 +1,12 @@
1
+ import { type PasskeyCeremonyDeps, type PasskeyRegistrationDeps } from './authenticate.js';
2
+ import { type SubmitClassicFormDeps } from './classic_form.js';
3
+ export interface RunPasskeyFlowOptions {
4
+ optionsUrl: string;
5
+ actionUrl: string;
6
+ csrfToken?: string;
7
+ returnTo?: string | null;
8
+ }
9
+ export type RunPasskeyAssertionDeps = PasskeyCeremonyDeps & SubmitClassicFormDeps;
10
+ export type RunPasskeyRegistrationDeps = PasskeyRegistrationDeps & SubmitClassicFormDeps;
11
+ export declare function runPasskeyAssertion(options: RunPasskeyFlowOptions, deps?: RunPasskeyAssertionDeps): Promise<void>;
12
+ export declare function runPasskeyRegistration(options: RunPasskeyFlowOptions, deps?: RunPasskeyRegistrationDeps): Promise<void>;
@@ -0,0 +1,20 @@
1
+ import { authenticatePasskey, registerPasskey, } from './authenticate.js';
2
+ import { submitClassicForm } from './classic_form.js';
3
+ function flowFields(response, csrfToken, returnTo) {
4
+ const fields = { response };
5
+ if (csrfToken)
6
+ fields._csrf = csrfToken;
7
+ if (returnTo)
8
+ fields.return_to = returnTo;
9
+ return fields;
10
+ }
11
+ export async function runPasskeyAssertion(options, deps = {}) {
12
+ const { optionsUrl, actionUrl, csrfToken, returnTo } = options;
13
+ const assertion = await authenticatePasskey({ optionsUrl, csrfToken }, { fetch: deps.fetch, loadStartAuthentication: deps.loadStartAuthentication });
14
+ submitClassicForm({ action: actionUrl, fields: flowFields(assertion, csrfToken, returnTo) }, { document: deps.document });
15
+ }
16
+ export async function runPasskeyRegistration(options, deps = {}) {
17
+ const { optionsUrl, actionUrl, csrfToken, returnTo } = options;
18
+ const attestation = await registerPasskey({ optionsUrl, csrfToken }, { fetch: deps.fetch, loadStartRegistration: deps.loadStartRegistration });
19
+ submitClassicForm({ action: actionUrl, fields: flowFields(attestation, csrfToken, returnTo) }, { document: deps.document });
20
+ }
@@ -1,4 +1,4 @@
1
- import type { AccountMe, AccountSecurityOverview, UpdateProfileInput, UpdateProfileResult, ChangePasswordInput, RequestEmailChangeInput, OkResult, EmailChangeResult, AccountSessionsResult, RevokeSessionResult, RevokeOthersResult, RevokeAllResult, AccountAppsResult, RevokeAppResult, AccountMfaStatus, AccountPasskeysResult, RemovePasskeyResult, AccountTokensResult, CreatedPatResult, RevokeTokenResult, CreateTokenInput, AccountOrgsResult, AccountOrgDetail, AccountOrgInvitationsResult } from '../../client/types.js';
1
+ import type { AccountAppsResult, AccountMe, AccountMfaStatus, AccountOrgDetail, AccountOrgInvitationsResult, AccountOrgsResult, AccountPasskeysResult, AccountSecurityOverview, AccountSessionsResult, AccountTokensResult, ChangePasswordInput, CreateTokenInput, CreatedPatResult, EmailChangeResult, OkResult, RemovePasskeyResult, RequestEmailChangeInput, RevokeAllResult, RevokeAppResult, RevokeOthersResult, RevokeSessionResult, RevokeTokenResult, UpdateProfileInput, UpdateProfileResult } from '../../client/types.js';
2
2
  export declare function useMeQueryOptions(): {
3
3
  queryKey: readonly ["authkit", "account", "me"];
4
4
  queryFn: () => Promise<AccountMe>;
@@ -1,4 +1,4 @@
1
- import type { AdminOverview, AdminUser, AdminUserListResult, CreateUserInput, UpdateUserInput, UserSessionsResult, RevokeSessionsResult, AdminClientListResult, AdminClient, CreatedClientResult, RegenerateSecretResult, CreateClientInput, UpdateClientInput, RoleListResult, RoleCatalogEntry, CreateRoleInput, UpdateRoleInput, AdminOrgListResult, AdminOrgDetail, AdminOrgInvitation, AuditListResult, AuditListParams, SettingListResult, SettingEntry, ImpersonationPanel, AdminOrgEntry, CreateOrgInput, UpdateOrgInput, KeysStatus, KeysRotateInput, KeysRotateResult } from '../../client/types.js';
1
+ import type { AdminClient, AdminClientListResult, AdminOrgDetail, AdminOrgEntry, AdminOrgInvitation, AdminOrgListResult, AdminOverview, AdminUser, AdminUserListResult, AuditListParams, AuditListResult, CreateClientInput, CreateOrgInput, CreateRoleInput, CreateUserInput, CreatedClientResult, ImpersonationPanel, KeysRotateInput, KeysRotateResult, KeysStatus, RegenerateSecretResult, RevokeSessionsResult, RoleCatalogEntry, RoleListResult, SettingEntry, SettingListResult, UpdateClientInput, UpdateOrgInput, UpdateRoleInput, UpdateUserInput, UserSessionsResult } from '../../client/types.js';
2
2
  export declare function useOverviewQueryOptions(): {
3
3
  queryKey: readonly ["authkit", "admin", "overview"];
4
4
  queryFn: () => Promise<AdminOverview>;
@@ -1,5 +1,5 @@
1
- import { useContext, useMemo } from 'react';
2
1
  import { usePage } from '@inertiajs/react';
2
+ import { useContext, useMemo } from 'react';
3
3
  import { AuthContext } from './provider.js';
4
4
  import { hasAllGlobalRoles as hasAllGlobalRolesPure, hasAnyGlobalRole as hasAnyGlobalRolePure, hasGlobalRole as hasGlobalRolePure, } from './roles.js';
5
5
  const UNAUTHENTICATED = {
@@ -1,2 +1,3 @@
1
1
  export declare function deriveInitials(name?: string | null, email?: string | null): string;
2
2
  export declare function currentUrl(): string;
3
+ export declare function buttonClass(variant?: string | null, extra?: string | null): string;
@@ -16,3 +16,6 @@ export function currentUrl() {
16
16
  return '';
17
17
  return window.location.pathname + window.location.search;
18
18
  }
19
+ export function buttonClass(variant, extra) {
20
+ return ['authkit-button', variant, extra].filter(Boolean).join(' ');
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonis-agora/authkit-react",
3
- "version": "0.15.0",
3
+ "version": "0.17.0",
4
4
  "description": "Frontend ergonomics over AuthKit for AdonisJS + Inertia + React apps: a typed useAuth() hook, role-gating hooks and gating components.",
5
5
  "license": "MIT",
6
6
  "author": "dudousxd",
@@ -39,10 +39,16 @@
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@inertiajs/react": "^3.3.0",
42
+ "@simplewebauthn/browser": "^13.3.0",
42
43
  "@tanstack/react-query": "^5.100.14",
43
44
  "react": "^19.2.6",
44
45
  "react-dom": "^19.2.6"
45
46
  },
47
+ "peerDependenciesMeta": {
48
+ "@simplewebauthn/browser": {
49
+ "optional": true
50
+ }
51
+ },
46
52
  "dependencies": {
47
53
  "@adonis-agora/authkit-core": "0.7.0"
48
54
  },