@adonis-agora/authkit-react 0.16.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.
- package/build/index.d.ts +92 -84
- package/build/index.js +48 -44
- package/build/src/authkit_provider.js +1 -1
- package/build/src/client/client.d.ts +1 -1
- package/build/src/client/client.js +4 -2
- package/build/src/client/context.d.ts +1 -1
- package/build/src/client/context.js +1 -1
- package/build/src/components/authenticated.js +1 -1
- package/build/src/components/can_permission.d.ts +1 -1
- package/build/src/components/can_permission.js +2 -2
- package/build/src/components/interaction_form.d.ts +3 -3
- package/build/src/components/interaction_form.js +3 -3
- package/build/src/components/key_rotation.js +1 -1
- package/build/src/components/magic_link_button.d.ts +2 -2
- package/build/src/components/magic_link_button.js +7 -7
- package/build/src/components/oauth_button.d.ts +2 -2
- package/build/src/components/oauth_button.js +5 -5
- package/build/src/components/organization_profile.js +3 -3
- package/build/src/components/passkey_button.d.ts +2 -2
- package/build/src/components/passkey_button.js +9 -9
- package/build/src/components/password_strength_meter.js +1 -4
- package/build/src/components/user_button.js +1 -1
- package/build/src/components/user_profile.js +1 -1
- package/build/src/config.d.ts +1 -1
- package/build/src/config.js +16 -19
- package/build/src/hooks/use_can.js +8 -8
- package/build/src/hooks/use_organization.js +3 -1
- package/build/src/hooks/use_passkey_assertion.d.ts +12 -0
- package/build/src/hooks/use_passkey_assertion.js +19 -0
- package/build/src/hooks/use_passkey_autofill.js +10 -11
- package/build/src/hooks/use_passkey_login.js +2 -2
- package/build/src/hooks/use_passkey_registration.d.ts +12 -0
- package/build/src/hooks/use_passkey_registration.js +19 -0
- package/build/src/hooks/use_profile.d.ts +1 -1
- package/build/src/hooks/use_profile.js +5 -1
- package/build/src/hooks/use_resource.js +1 -1
- package/build/src/hooks/use_sign_in.js +1 -1
- package/build/src/hooks/use_sign_out.js +1 -1
- package/build/src/interaction/urls.d.ts +1 -1
- package/build/src/interaction/urls.js +2 -2
- package/build/src/passkey/authenticate.d.ts +20 -1
- package/build/src/passkey/authenticate.js +51 -31
- package/build/src/passkey/classic_form.d.ts +9 -0
- package/build/src/passkey/classic_form.js +18 -0
- package/build/src/passkey/sudo.d.ts +12 -0
- package/build/src/passkey/sudo.js +20 -0
- package/build/src/queries/account/index.d.ts +1 -1
- package/build/src/queries/admin/index.d.ts +1 -1
- package/build/src/use_auth.js +1 -1
- package/build/src/utils.js +6 -6
- package/package.json +7 -1
package/build/src/config.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { createContext, useContext } from
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
2
|
export const DEFAULT_CONFIG = {
|
|
3
|
-
idp:
|
|
4
|
-
loginUrl:
|
|
5
|
-
logoutUrl:
|
|
6
|
-
profileUrl:
|
|
3
|
+
idp: 'authkit',
|
|
4
|
+
loginUrl: '/auth/login',
|
|
5
|
+
logoutUrl: '/account/logout',
|
|
6
|
+
profileUrl: '/account/security',
|
|
7
7
|
endpoints: {
|
|
8
|
-
profile:
|
|
9
|
-
sessions:
|
|
10
|
-
apps:
|
|
11
|
-
passkeys:
|
|
12
|
-
orgs:
|
|
13
|
-
orgInvitations:
|
|
14
|
-
can:
|
|
8
|
+
profile: '/account/security/profile',
|
|
9
|
+
sessions: '/account/security',
|
|
10
|
+
apps: '/account/apps',
|
|
11
|
+
passkeys: '/account/mfa/passkeys',
|
|
12
|
+
orgs: '/account/orgs/json',
|
|
13
|
+
orgInvitations: '/account/orgs/invitations/json',
|
|
14
|
+
can: '/authz/can',
|
|
15
15
|
},
|
|
16
16
|
};
|
|
17
17
|
export function resolveConfig(config) {
|
|
@@ -25,20 +25,17 @@ export function resolveConfig(config) {
|
|
|
25
25
|
apps: config?.endpoints?.apps ?? DEFAULT_CONFIG.endpoints.apps,
|
|
26
26
|
passkeys: config?.endpoints?.passkeys ?? DEFAULT_CONFIG.endpoints.passkeys,
|
|
27
27
|
orgs: config?.endpoints?.orgs ?? DEFAULT_CONFIG.endpoints.orgs,
|
|
28
|
-
orgInvitations: config?.endpoints?.orgInvitations ??
|
|
29
|
-
|
|
30
|
-
can: config?.endpoints?.can ??
|
|
31
|
-
config?.canPath ??
|
|
32
|
-
DEFAULT_CONFIG.endpoints.can,
|
|
28
|
+
orgInvitations: config?.endpoints?.orgInvitations ?? DEFAULT_CONFIG.endpoints.orgInvitations,
|
|
29
|
+
can: config?.endpoints?.can ?? config?.canPath ?? DEFAULT_CONFIG.endpoints.can,
|
|
33
30
|
},
|
|
34
31
|
csrfToken: config?.csrfToken,
|
|
35
|
-
idp: config?.idp ??
|
|
32
|
+
idp: config?.idp ?? 'authkit',
|
|
36
33
|
};
|
|
37
34
|
}
|
|
38
35
|
export function buildAuthUrl(base, returnTo) {
|
|
39
36
|
if (!returnTo)
|
|
40
37
|
return base;
|
|
41
|
-
const sep = base.includes(
|
|
38
|
+
const sep = base.includes('?') ? '&' : '?';
|
|
42
39
|
return `${base}${sep}returnTo=${encodeURIComponent(returnTo)}`;
|
|
43
40
|
}
|
|
44
41
|
export const AuthkitConfigContext = createContext(DEFAULT_CONFIG);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { useAuthkitConfig } from
|
|
4
|
-
import { AuthContext } from
|
|
5
|
-
import { jsonRequest } from
|
|
6
|
-
const ANON_PRINCIPAL =
|
|
1
|
+
import { usePage } from '@inertiajs/react';
|
|
2
|
+
import { useContext, useEffect, useState } from 'react';
|
|
3
|
+
import { useAuthkitConfig } from '../config.js';
|
|
4
|
+
import { AuthContext } from '../provider.js';
|
|
5
|
+
import { jsonRequest } from './use_resource.js';
|
|
6
|
+
const ANON_PRINCIPAL = 'anon';
|
|
7
7
|
function usePrincipalId() {
|
|
8
8
|
const fromContext = useContext(AuthContext);
|
|
9
9
|
let fromPage;
|
|
@@ -16,7 +16,7 @@ function usePrincipalId() {
|
|
|
16
16
|
return (fromContext ?? fromPage)?.user?.id ?? ANON_PRINCIPAL;
|
|
17
17
|
}
|
|
18
18
|
function cacheKey(path, principal, permission, resource) {
|
|
19
|
-
return [principal, path, permission, resource ??
|
|
19
|
+
return [principal, path, permission, resource ?? ''].join('|');
|
|
20
20
|
}
|
|
21
21
|
export const canCache = {
|
|
22
22
|
resolved: new Map(),
|
|
@@ -38,7 +38,7 @@ export async function checkCan(path, permission, resource, csrfToken, principal
|
|
|
38
38
|
if (pending)
|
|
39
39
|
return pending;
|
|
40
40
|
const promise = jsonRequest(path, {
|
|
41
|
-
method:
|
|
41
|
+
method: 'POST',
|
|
42
42
|
csrfToken,
|
|
43
43
|
body: JSON.stringify({ permission, ...(resource ? { resource } : {}) }),
|
|
44
44
|
})
|
|
@@ -2,7 +2,9 @@ import { useAuthkitConfig } from '../config.js';
|
|
|
2
2
|
import { useResource } from './use_resource.js';
|
|
3
3
|
export function useOrganization(orgId) {
|
|
4
4
|
const config = useAuthkitConfig();
|
|
5
|
-
const url = orgId
|
|
5
|
+
const url = orgId
|
|
6
|
+
? `${config.endpoints.orgs.replace('/json', '')}/${encodeURIComponent(orgId)}/json`
|
|
7
|
+
: '';
|
|
6
8
|
const { data, loading, error, refetch } = useResource(url, config.csrfToken);
|
|
7
9
|
return { data, loading: url ? loading : false, error, actions: { refetch } };
|
|
8
10
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface UsePasskeyAssertionOptions {
|
|
2
|
+
optionsUrl: string;
|
|
3
|
+
actionUrl: string;
|
|
4
|
+
csrfToken?: string;
|
|
5
|
+
returnTo?: string | null;
|
|
6
|
+
}
|
|
7
|
+
export interface UsePasskeyAssertionResult {
|
|
8
|
+
run: () => Promise<void>;
|
|
9
|
+
running: boolean;
|
|
10
|
+
error: Error | null;
|
|
11
|
+
}
|
|
12
|
+
export declare function usePasskeyAssertion(options: UsePasskeyAssertionOptions): UsePasskeyAssertionResult;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
import { runPasskeyAssertion } from '../passkey/sudo.js';
|
|
3
|
+
export function usePasskeyAssertion(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 runPasskeyAssertion({ 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,12 +1,12 @@
|
|
|
1
|
-
import { useEffect, useRef } from
|
|
2
|
-
import { loadStartAuthentication
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { loadStartAuthentication } from '../passkey/authenticate.js';
|
|
3
3
|
export function usePasskeyAutofill(options) {
|
|
4
|
-
const { optionsUrl, verifyUrl, onSuccess, csrfToken, enabled = true
|
|
4
|
+
const { optionsUrl, verifyUrl, onSuccess, csrfToken, enabled = true } = options;
|
|
5
5
|
const abortRef = useRef(null);
|
|
6
6
|
useEffect(() => {
|
|
7
7
|
if (!enabled)
|
|
8
8
|
return;
|
|
9
|
-
if (typeof window ===
|
|
9
|
+
if (typeof window === 'undefined' || typeof navigator === 'undefined')
|
|
10
10
|
return;
|
|
11
11
|
if (!window.PublicKeyCredential)
|
|
12
12
|
return;
|
|
@@ -15,8 +15,7 @@ export function usePasskeyAutofill(options) {
|
|
|
15
15
|
abortRef.current = ac;
|
|
16
16
|
const run = async () => {
|
|
17
17
|
try {
|
|
18
|
-
const supported = typeof PublicKeyCredential
|
|
19
|
-
.isConditionalMediationAvailable === "function"
|
|
18
|
+
const supported = typeof PublicKeyCredential.isConditionalMediationAvailable === 'function'
|
|
20
19
|
? await PublicKeyCredential.isConditionalMediationAvailable()
|
|
21
20
|
: false;
|
|
22
21
|
if (!supported || cancelled)
|
|
@@ -31,12 +30,12 @@ export function usePasskeyAutofill(options) {
|
|
|
31
30
|
if (cancelled)
|
|
32
31
|
return;
|
|
33
32
|
const headers = {
|
|
34
|
-
|
|
33
|
+
'content-type': 'application/json',
|
|
35
34
|
};
|
|
36
35
|
if (csrfToken)
|
|
37
|
-
headers[
|
|
36
|
+
headers['x-csrf-token'] = csrfToken;
|
|
38
37
|
const optRes = await fetch(optionsUrl, {
|
|
39
|
-
method:
|
|
38
|
+
method: 'POST',
|
|
40
39
|
headers,
|
|
41
40
|
body: JSON.stringify({}),
|
|
42
41
|
signal: ac.signal,
|
|
@@ -44,7 +43,7 @@ export function usePasskeyAutofill(options) {
|
|
|
44
43
|
if (!optRes.ok || cancelled)
|
|
45
44
|
return;
|
|
46
45
|
const optionsJSON = await optRes.json();
|
|
47
|
-
|
|
46
|
+
optionsJSON._discoverable = undefined;
|
|
48
47
|
if (cancelled)
|
|
49
48
|
return;
|
|
50
49
|
const assertion = await startAuthentication({
|
|
@@ -65,5 +64,5 @@ export function usePasskeyAutofill(options) {
|
|
|
65
64
|
ac.abort();
|
|
66
65
|
abortRef.current = null;
|
|
67
66
|
};
|
|
68
|
-
}, [enabled, optionsUrl, verifyUrl, csrfToken]);
|
|
67
|
+
}, [enabled, optionsUrl, verifyUrl, onSuccess, csrfToken]);
|
|
69
68
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useCallback, useState } from
|
|
2
|
-
import { authenticatePasskey, submitPasskeyVerification
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
import { authenticatePasskey, submitPasskeyVerification } from '../passkey/authenticate.js';
|
|
3
3
|
export function usePasskeyLogin(options) {
|
|
4
4
|
const { optionsUrl, verifyUrl, csrfToken, onSuccess } = options;
|
|
5
5
|
const [busy, setBusy] = useState(false);
|
|
@@ -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
|
+
}
|
|
@@ -18,7 +18,11 @@ export function useProfile() {
|
|
|
18
18
|
body: JSON.stringify(data),
|
|
19
19
|
csrfToken: config.csrfToken,
|
|
20
20
|
});
|
|
21
|
-
setState({
|
|
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
|
-
|
|
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 {
|
|
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 {
|
|
2
|
+
import { buildAuthUrl, useAuthkitConfig } from '../config.js';
|
|
3
3
|
export function useSignOut() {
|
|
4
4
|
const config = useAuthkitConfig();
|
|
5
5
|
const signOut = useCallback((opts) => {
|
|
@@ -8,5 +8,5 @@ export interface InteractionUrls {
|
|
|
8
8
|
passkeyVerify: string;
|
|
9
9
|
}
|
|
10
10
|
export declare function interactionUrls(uid: string, basePath?: string): InteractionUrls;
|
|
11
|
-
export type InteractionPostStep =
|
|
11
|
+
export type InteractionPostStep = 'identifier' | 'login' | 'magic';
|
|
12
12
|
export declare function oauthRedirectUrl(provider: string, uid: string, basePath?: string): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function interactionUrls(uid, basePath =
|
|
1
|
+
export function interactionUrls(uid, basePath = '/auth/interaction') {
|
|
2
2
|
const base = `${basePath}/${uid}`;
|
|
3
3
|
return {
|
|
4
4
|
identifier: `${base}/identifier`,
|
|
@@ -10,6 +10,6 @@ export function interactionUrls(uid, basePath = "/auth/interaction") {
|
|
|
10
10
|
passkeyVerify: `${base}/passkey/verify`,
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
|
-
export function oauthRedirectUrl(provider, uid, basePath =
|
|
13
|
+
export function oauthRedirectUrl(provider, uid, basePath = '/auth') {
|
|
14
14
|
return `${basePath}/${provider}/redirect/${uid}`;
|
|
15
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
|
|
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
|
-
|
|
10
|
-
|
|
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
|
|
25
|
+
const mod = await importModule();
|
|
13
26
|
return mod.startAuthentication;
|
|
14
27
|
}
|
|
15
|
-
catch {
|
|
16
|
-
|
|
17
|
-
return mod.startAuthentication;
|
|
28
|
+
catch (cause) {
|
|
29
|
+
throw missingPeerError(cause);
|
|
18
30
|
}
|
|
19
31
|
}
|
|
20
|
-
export async function
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
43
|
+
'content-type': 'application/json',
|
|
26
44
|
};
|
|
27
45
|
if (csrfToken)
|
|
28
|
-
headers[
|
|
46
|
+
headers['x-csrf-token'] = csrfToken;
|
|
29
47
|
const res = await doFetch(optionsUrl, {
|
|
30
|
-
method:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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 {
|
|
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 {
|
|
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>;
|
package/build/src/use_auth.js
CHANGED
|
@@ -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 = {
|
package/build/src/utils.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
export function deriveInitials(name, email) {
|
|
2
|
-
const source = (name ??
|
|
2
|
+
const source = (name ?? '').trim();
|
|
3
3
|
if (source) {
|
|
4
4
|
const parts = source.split(/\s+/).filter(Boolean);
|
|
5
5
|
if (parts.length === 1)
|
|
6
6
|
return parts[0].slice(0, 2).toUpperCase();
|
|
7
7
|
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
|
8
8
|
}
|
|
9
|
-
const e = (email ??
|
|
9
|
+
const e = (email ?? '').trim();
|
|
10
10
|
if (e)
|
|
11
11
|
return e[0].toUpperCase();
|
|
12
|
-
return
|
|
12
|
+
return '?';
|
|
13
13
|
}
|
|
14
14
|
export function currentUrl() {
|
|
15
|
-
if (typeof window ===
|
|
16
|
-
return
|
|
15
|
+
if (typeof window === 'undefined')
|
|
16
|
+
return '';
|
|
17
17
|
return window.location.pathname + window.location.search;
|
|
18
18
|
}
|
|
19
19
|
export function buttonClass(variant, extra) {
|
|
20
|
-
return [
|
|
20
|
+
return ['authkit-button', variant, extra].filter(Boolean).join(' ');
|
|
21
21
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonis-agora/authkit-react",
|
|
3
|
-
"version": "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
|
},
|