@adonis-agora/authkit-react 0.12.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 (82) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +146 -0
  3. package/build/index.d.ts +68 -0
  4. package/build/index.js +36 -0
  5. package/build/src/authkit_provider.d.ts +13 -0
  6. package/build/src/authkit_provider.js +11 -0
  7. package/build/src/client/client.d.ts +172 -0
  8. package/build/src/client/client.js +218 -0
  9. package/build/src/client/context.d.ts +11 -0
  10. package/build/src/client/context.js +28 -0
  11. package/build/src/client/types.d.ts +425 -0
  12. package/build/src/client/types.js +1 -0
  13. package/build/src/components/authenticated.d.ts +11 -0
  14. package/build/src/components/authenticated.js +10 -0
  15. package/build/src/components/authorized_apps.d.ts +10 -0
  16. package/build/src/components/authorized_apps.js +34 -0
  17. package/build/src/components/avatar.d.ts +22 -0
  18. package/build/src/components/avatar.js +15 -0
  19. package/build/src/components/can.d.ts +10 -0
  20. package/build/src/components/can.js +19 -0
  21. package/build/src/components/key_rotation.d.ts +8 -0
  22. package/build/src/components/key_rotation.js +36 -0
  23. package/build/src/components/organization_profile.d.ts +10 -0
  24. package/build/src/components/organization_profile.js +94 -0
  25. package/build/src/components/organization_switcher.d.ts +9 -0
  26. package/build/src/components/organization_switcher.js +65 -0
  27. package/build/src/components/password_strength_meter.d.ts +11 -0
  28. package/build/src/components/password_strength_meter.js +39 -0
  29. package/build/src/components/sign_in_button.d.ts +297 -0
  30. package/build/src/components/sign_in_button.js +16 -0
  31. package/build/src/components/sign_out_button.d.ts +296 -0
  32. package/build/src/components/sign_out_button.js +16 -0
  33. package/build/src/components/user_button.d.ts +8 -0
  34. package/build/src/components/user_button.js +31 -0
  35. package/build/src/components/user_profile.d.ts +8 -0
  36. package/build/src/components/user_profile.js +35 -0
  37. package/build/src/config.d.ts +30 -0
  38. package/build/src/config.js +42 -0
  39. package/build/src/hooks/use_authorized_apps.d.ts +16 -0
  40. package/build/src/hooks/use_authorized_apps.js +15 -0
  41. package/build/src/hooks/use_org_invitations.d.ts +19 -0
  42. package/build/src/hooks/use_org_invitations.js +20 -0
  43. package/build/src/hooks/use_organization.d.ts +23 -0
  44. package/build/src/hooks/use_organization.js +8 -0
  45. package/build/src/hooks/use_organizations.d.ts +18 -0
  46. package/build/src/hooks/use_organizations.js +14 -0
  47. package/build/src/hooks/use_passkey_autofill.d.ts +8 -0
  48. package/build/src/hooks/use_passkey_autofill.js +76 -0
  49. package/build/src/hooks/use_password_strength.d.ts +11 -0
  50. package/build/src/hooks/use_password_strength.js +34 -0
  51. package/build/src/hooks/use_profile.d.ts +13 -0
  52. package/build/src/hooks/use_profile.js +28 -0
  53. package/build/src/hooks/use_resource.d.ts +11 -0
  54. package/build/src/hooks/use_resource.js +48 -0
  55. package/build/src/hooks/use_sessions.d.ts +16 -0
  56. package/build/src/hooks/use_sessions.js +15 -0
  57. package/build/src/hooks/use_sign_in.d.ts +6 -0
  58. package/build/src/hooks/use_sign_in.js +13 -0
  59. package/build/src/hooks/use_sign_out.d.ts +6 -0
  60. package/build/src/hooks/use_sign_out.js +11 -0
  61. package/build/src/hooks/use_switch_organization.d.ts +7 -0
  62. package/build/src/hooks/use_switch_organization.js +42 -0
  63. package/build/src/hooks/use_user.d.ts +6 -0
  64. package/build/src/hooks/use_user.js +5 -0
  65. package/build/src/provider.d.ts +16 -0
  66. package/build/src/provider.js +5 -0
  67. package/build/src/queries/account/index.d.ts +86 -0
  68. package/build/src/queries/account/index.js +150 -0
  69. package/build/src/queries/admin/index.d.ts +226 -0
  70. package/build/src/queries/admin/index.js +287 -0
  71. package/build/src/queries/keys.d.ts +50 -0
  72. package/build/src/queries/keys.js +34 -0
  73. package/build/src/roles.d.ts +7 -0
  74. package/build/src/roles.js +39 -0
  75. package/build/src/types.d.ts +27 -0
  76. package/build/src/types.js +1 -0
  77. package/build/src/use_auth.d.ts +2 -0
  78. package/build/src/use_auth.js +33 -0
  79. package/build/src/utils.d.ts +2 -0
  80. package/build/src/utils.js +18 -0
  81. package/package.json +68 -0
  82. package/styles.css +303 -0
@@ -0,0 +1,16 @@
1
+ import { createElement } from 'react';
2
+ import { useSignOut } from '../hooks/use_sign_out.js';
3
+ import { useAuth } from '../use_auth.js';
4
+ export function SignOutButton({ children = 'Sair', returnTo, className, ...rest }) {
5
+ const { signOut } = useSignOut();
6
+ const { isAuthenticated } = useAuth();
7
+ if (!isAuthenticated)
8
+ return null;
9
+ const opts = returnTo ? { returnTo } : undefined;
10
+ return createElement('button', {
11
+ type: 'button',
12
+ className: ['authkit-button', 'authkit-button--ghost', className].filter(Boolean).join(' '),
13
+ onClick: () => signOut(opts),
14
+ ...rest,
15
+ }, children);
16
+ }
@@ -0,0 +1,8 @@
1
+ export interface UserButtonProps {
2
+ profileLabel?: string;
3
+ signOutLabel?: string;
4
+ className?: string;
5
+ }
6
+ export declare function UserButton({ profileLabel, signOutLabel, className, }: UserButtonProps): import("react").DetailedReactHTMLElement<{
7
+ className: string;
8
+ }, HTMLElement> | null;
@@ -0,0 +1,31 @@
1
+ import { createElement, useState } from 'react';
2
+ import { useAuth } from '../use_auth.js';
3
+ import { useAuthkitConfig } from '../config.js';
4
+ import { useSignOut } from '../hooks/use_sign_out.js';
5
+ import { Avatar } from './avatar.js';
6
+ export function UserButton({ profileLabel = 'Perfil', signOutLabel = 'Sair', className, }) {
7
+ const { user, isAuthenticated } = useAuth();
8
+ const config = useAuthkitConfig();
9
+ const { signOut } = useSignOut();
10
+ const [open, setOpen] = useState(false);
11
+ if (!isAuthenticated || !user)
12
+ return null;
13
+ const trigger = createElement('button', {
14
+ type: 'button',
15
+ className: 'authkit-userbutton__trigger',
16
+ 'aria-haspopup': 'menu',
17
+ 'aria-expanded': open,
18
+ onClick: () => setOpen((v) => !v),
19
+ }, createElement(Avatar, { user }));
20
+ const menu = open
21
+ ? createElement('div', { className: 'authkit-userbutton__menu', role: 'menu' }, createElement('div', { className: 'authkit-userbutton__header' }, createElement('div', { className: 'authkit-userbutton__name' }, user.name ?? user.email), user.name
22
+ ? createElement('div', { className: 'authkit-userbutton__email' }, user.email)
23
+ : null), createElement('a', { className: 'authkit-userbutton__item', role: 'menuitem', href: config.profileUrl }, profileLabel), createElement('button', {
24
+ type: 'button',
25
+ className: 'authkit-userbutton__item',
26
+ role: 'menuitem',
27
+ onClick: () => signOut(),
28
+ }, signOutLabel))
29
+ : null;
30
+ return createElement('div', { className: ['authkit-userbutton', className].filter(Boolean).join(' ') }, trigger, menu);
31
+ }
@@ -0,0 +1,8 @@
1
+ export interface UserProfileProps {
2
+ className?: string;
3
+ }
4
+ declare function UserProfileInner({ className }: UserProfileProps): import("react").DetailedReactHTMLElement<{
5
+ className: string;
6
+ }, HTMLElement> | null;
7
+ export declare function UserProfile(props: Parameters<typeof UserProfileInner>[0]): import("react/jsx-runtime").JSX.Element | null;
8
+ export {};
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createElement, useState } from 'react';
3
+ import { useAuthkitConfig } from '../config.js';
4
+ import { useAuth } from '../use_auth.js';
5
+ import { useProfile } from '../hooks/use_profile.js';
6
+ import { Avatar } from './avatar.js';
7
+ function UserProfileInner({ className }) {
8
+ const { user, isAuthenticated } = useAuth();
9
+ const { actions, loading, error } = useProfile();
10
+ const [name, setName] = useState(user?.name ?? '');
11
+ if (!isAuthenticated || !user)
12
+ return null;
13
+ const onSubmit = (e) => {
14
+ e.preventDefault();
15
+ void actions.update({ name });
16
+ };
17
+ return createElement('div', { className: ['authkit-card', 'authkit-profile', className].filter(Boolean).join(' ') }, createElement('div', { className: 'authkit-profile__header' }, createElement(Avatar, { user, size: 56 }), createElement('div', null, createElement('div', { className: 'authkit-profile__name' }, user.name ?? user.email), createElement('div', { className: 'authkit-profile__email' }, user.email))), createElement('form', { className: 'authkit-profile__form', onSubmit }, createElement('label', { className: 'authkit-label', htmlFor: 'authkit-profile-name' }, 'Nome'), createElement('input', {
18
+ id: 'authkit-profile-name',
19
+ className: 'authkit-input',
20
+ value: name,
21
+ onChange: (e) => setName(e.target.value),
22
+ }), error
23
+ ? createElement('p', { className: 'authkit-error', role: 'alert' }, error.message)
24
+ : null, createElement('button', {
25
+ type: 'submit',
26
+ className: 'authkit-button authkit-button--primary',
27
+ disabled: loading,
28
+ }, loading ? 'Salvando…' : 'Salvar')));
29
+ }
30
+ export function UserProfile(props) {
31
+ const { idp } = useAuthkitConfig();
32
+ if (idp === 'external')
33
+ return null;
34
+ return _jsx(UserProfileInner, { ...props });
35
+ }
@@ -0,0 +1,30 @@
1
+ export interface AuthkitEndpoints {
2
+ profile: string;
3
+ sessions: string;
4
+ apps: string;
5
+ passkeys: string;
6
+ orgs: string;
7
+ orgInvitations: string;
8
+ }
9
+ export type AuthkitIdpMode = 'authkit' | 'external';
10
+ export interface AuthkitConfig {
11
+ loginUrl?: string;
12
+ logoutUrl?: string;
13
+ profileUrl?: string;
14
+ endpoints?: Partial<AuthkitEndpoints>;
15
+ csrfToken?: string;
16
+ idp?: AuthkitIdpMode;
17
+ }
18
+ export interface ResolvedAuthkitConfig {
19
+ loginUrl: string;
20
+ logoutUrl: string;
21
+ profileUrl: string;
22
+ endpoints: AuthkitEndpoints;
23
+ csrfToken?: string;
24
+ idp: AuthkitIdpMode;
25
+ }
26
+ export declare const DEFAULT_CONFIG: ResolvedAuthkitConfig;
27
+ export declare function resolveConfig(config?: AuthkitConfig): ResolvedAuthkitConfig;
28
+ export declare function buildAuthUrl(base: string, returnTo?: string): string;
29
+ export declare const AuthkitConfigContext: import("react").Context<ResolvedAuthkitConfig>;
30
+ export declare function useAuthkitConfig(): ResolvedAuthkitConfig;
@@ -0,0 +1,42 @@
1
+ import { createContext, useContext } from 'react';
2
+ export const DEFAULT_CONFIG = {
3
+ idp: 'authkit',
4
+ loginUrl: '/auth/login',
5
+ logoutUrl: '/account/logout',
6
+ profileUrl: '/account/security',
7
+ endpoints: {
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
+ },
15
+ };
16
+ export function resolveConfig(config) {
17
+ return {
18
+ loginUrl: config?.loginUrl ?? DEFAULT_CONFIG.loginUrl,
19
+ logoutUrl: config?.logoutUrl ?? DEFAULT_CONFIG.logoutUrl,
20
+ profileUrl: config?.profileUrl ?? DEFAULT_CONFIG.profileUrl,
21
+ endpoints: {
22
+ profile: config?.endpoints?.profile ?? DEFAULT_CONFIG.endpoints.profile,
23
+ sessions: config?.endpoints?.sessions ?? DEFAULT_CONFIG.endpoints.sessions,
24
+ apps: config?.endpoints?.apps ?? DEFAULT_CONFIG.endpoints.apps,
25
+ passkeys: config?.endpoints?.passkeys ?? DEFAULT_CONFIG.endpoints.passkeys,
26
+ orgs: config?.endpoints?.orgs ?? DEFAULT_CONFIG.endpoints.orgs,
27
+ orgInvitations: config?.endpoints?.orgInvitations ?? DEFAULT_CONFIG.endpoints.orgInvitations,
28
+ },
29
+ csrfToken: config?.csrfToken,
30
+ idp: config?.idp ?? 'authkit',
31
+ };
32
+ }
33
+ export function buildAuthUrl(base, returnTo) {
34
+ if (!returnTo)
35
+ return base;
36
+ const sep = base.includes('?') ? '&' : '?';
37
+ return `${base}${sep}returnTo=${encodeURIComponent(returnTo)}`;
38
+ }
39
+ export const AuthkitConfigContext = createContext(DEFAULT_CONFIG);
40
+ export function useAuthkitConfig() {
41
+ return useContext(AuthkitConfigContext);
42
+ }
@@ -0,0 +1,16 @@
1
+ import { type ResourceState } from './use_resource.js';
2
+ export interface AuthorizedApp {
3
+ clientId: string;
4
+ name?: string;
5
+ logoUrl?: string;
6
+ scopes?: string[];
7
+ authorizedAt?: string;
8
+ [key: string]: unknown;
9
+ }
10
+ export interface UseAuthorizedAppsResult extends ResourceState<AuthorizedApp[]> {
11
+ actions: {
12
+ refetch(): Promise<void>;
13
+ revoke(clientId: string): Promise<void>;
14
+ };
15
+ }
16
+ export declare function useAuthorizedApps(): UseAuthorizedAppsResult;
@@ -0,0 +1,15 @@
1
+ import { useCallback } from 'react';
2
+ import { useAuthkitConfig } from '../config.js';
3
+ import { jsonRequest, useResource } from './use_resource.js';
4
+ export function useAuthorizedApps() {
5
+ const config = useAuthkitConfig();
6
+ const { data, loading, error, refetch } = useResource(config.endpoints.apps, config.csrfToken);
7
+ const revoke = useCallback(async (clientId) => {
8
+ await jsonRequest(`${config.endpoints.apps}/${encodeURIComponent(clientId)}/revoke`, {
9
+ method: 'POST',
10
+ csrfToken: config.csrfToken,
11
+ });
12
+ await refetch();
13
+ }, [config.endpoints.apps, config.csrfToken, refetch]);
14
+ return { data, loading, error, actions: { refetch, revoke } };
15
+ }
@@ -0,0 +1,19 @@
1
+ import { type ResourceState } from './use_resource.js';
2
+ export interface OrgInvitationEntry {
3
+ id: string;
4
+ organizationId: string;
5
+ orgName: string;
6
+ orgSlug: string;
7
+ email: string;
8
+ role: string;
9
+ expiresAt: string;
10
+ createdAt: string;
11
+ [key: string]: unknown;
12
+ }
13
+ export interface UseOrgInvitationsResult extends ResourceState<OrgInvitationEntry[]> {
14
+ actions: {
15
+ refetch(): Promise<void>;
16
+ accept(token: string): Promise<void>;
17
+ };
18
+ }
19
+ export declare function useOrgInvitations(): UseOrgInvitationsResult;
@@ -0,0 +1,20 @@
1
+ import { useCallback } from 'react';
2
+ import { useAuthkitConfig } from '../config.js';
3
+ import { jsonRequest, useResource } from './use_resource.js';
4
+ export function useOrgInvitations() {
5
+ const config = useAuthkitConfig();
6
+ const { data, loading, error, refetch } = useResource(config.endpoints.orgInvitations, config.csrfToken);
7
+ const accept = useCallback(async (token) => {
8
+ await jsonRequest(`/account/orgs/invitations/${encodeURIComponent(token)}/accept`, {
9
+ method: 'POST',
10
+ csrfToken: config.csrfToken,
11
+ });
12
+ await refetch();
13
+ }, [config.csrfToken, refetch]);
14
+ return {
15
+ data: data?.invitations ?? null,
16
+ loading,
17
+ error,
18
+ actions: { refetch, accept },
19
+ };
20
+ }
@@ -0,0 +1,23 @@
1
+ import { type ResourceState } from './use_resource.js';
2
+ export interface OrgMemberEntry {
3
+ accountId: string;
4
+ email: string | null;
5
+ role: string;
6
+ joinedAt: string;
7
+ }
8
+ export interface ActiveOrgDetail {
9
+ id: string;
10
+ name: string;
11
+ slug: string;
12
+ logoUrl: string | null;
13
+ role: string;
14
+ canManage: boolean;
15
+ members: OrgMemberEntry[];
16
+ [key: string]: unknown;
17
+ }
18
+ export interface UseOrganizationResult extends ResourceState<ActiveOrgDetail> {
19
+ actions: {
20
+ refetch(): Promise<void>;
21
+ };
22
+ }
23
+ export declare function useOrganization(orgId: string | null): UseOrganizationResult;
@@ -0,0 +1,8 @@
1
+ import { useAuthkitConfig } from '../config.js';
2
+ import { useResource } from './use_resource.js';
3
+ export function useOrganization(orgId) {
4
+ const config = useAuthkitConfig();
5
+ const url = orgId ? `${config.endpoints.orgs.replace('/json', '')}/${encodeURIComponent(orgId)}/json` : '';
6
+ const { data, loading, error, refetch } = useResource(url, config.csrfToken);
7
+ return { data, loading: url ? loading : false, error, actions: { refetch } };
8
+ }
@@ -0,0 +1,18 @@
1
+ import { type ResourceState } from './use_resource.js';
2
+ export interface OrgEntry {
3
+ id: string;
4
+ name: string;
5
+ slug: string;
6
+ logoUrl: string | null;
7
+ role: string;
8
+ isActive: boolean;
9
+ [key: string]: unknown;
10
+ }
11
+ export interface UseOrganizationsResult extends ResourceState<OrgEntry[]> {
12
+ activeOrgId: string | null;
13
+ supported: boolean;
14
+ actions: {
15
+ refetch(): Promise<void>;
16
+ };
17
+ }
18
+ export declare function useOrganizations(): UseOrganizationsResult;
@@ -0,0 +1,14 @@
1
+ import { useAuthkitConfig } from '../config.js';
2
+ import { useResource } from './use_resource.js';
3
+ export function useOrganizations() {
4
+ const config = useAuthkitConfig();
5
+ const { data, loading, error, refetch } = useResource(config.endpoints.orgs, config.csrfToken);
6
+ return {
7
+ data: data?.orgs ?? null,
8
+ loading,
9
+ error,
10
+ activeOrgId: data?.activeOrgId ?? null,
11
+ supported: data?.supported ?? true,
12
+ actions: { refetch },
13
+ };
14
+ }
@@ -0,0 +1,8 @@
1
+ export interface UsePasskeyAutofillOptions {
2
+ optionsUrl: string;
3
+ verifyUrl: string;
4
+ onSuccess: (assertion: string) => void;
5
+ csrfToken?: string;
6
+ enabled?: boolean;
7
+ }
8
+ export declare function usePasskeyAutofill(options: UsePasskeyAutofillOptions): void;
@@ -0,0 +1,76 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { useEffect, useRef } from 'react';
10
+ export function usePasskeyAutofill(options) {
11
+ const { optionsUrl, verifyUrl, onSuccess, csrfToken, enabled = true } = options;
12
+ const abortRef = useRef(null);
13
+ useEffect(() => {
14
+ if (!enabled)
15
+ return;
16
+ if (typeof window === 'undefined' || typeof navigator === 'undefined')
17
+ return;
18
+ if (!window.PublicKeyCredential)
19
+ return;
20
+ let cancelled = false;
21
+ const ac = new AbortController();
22
+ abortRef.current = ac;
23
+ const run = async () => {
24
+ try {
25
+ const supported = typeof PublicKeyCredential.isConditionalMediationAvailable === 'function'
26
+ ? await PublicKeyCredential.isConditionalMediationAvailable()
27
+ : false;
28
+ if (!supported || cancelled)
29
+ return;
30
+ let startAuthentication;
31
+ try {
32
+ const mod = await import(__rewriteRelativeImportExtension('@simplewebauthn/browser'));
33
+ startAuthentication = mod.startAuthentication;
34
+ }
35
+ catch {
36
+ try {
37
+ const mod = await import(__rewriteRelativeImportExtension('https://cdn.jsdelivr.net/npm/@simplewebauthn/browser@13/dist/bundle/index.js'));
38
+ startAuthentication = mod.startAuthentication;
39
+ }
40
+ catch {
41
+ return;
42
+ }
43
+ }
44
+ if (!startAuthentication || cancelled)
45
+ return;
46
+ const headers = { 'content-type': 'application/json' };
47
+ if (csrfToken)
48
+ headers['x-csrf-token'] = csrfToken;
49
+ const optRes = await fetch(optionsUrl, {
50
+ method: 'POST',
51
+ headers,
52
+ body: JSON.stringify({}),
53
+ signal: ac.signal,
54
+ });
55
+ if (!optRes.ok || cancelled)
56
+ return;
57
+ const optionsJSON = await optRes.json();
58
+ delete optionsJSON._discoverable;
59
+ if (cancelled)
60
+ return;
61
+ const assertion = await startAuthentication({ optionsJSON, useBrowserAutofill: true, verifyBrowserAutofillInput: true }, ac.signal);
62
+ if (cancelled)
63
+ return;
64
+ onSuccess(JSON.stringify(assertion));
65
+ }
66
+ catch {
67
+ }
68
+ };
69
+ void run();
70
+ return () => {
71
+ cancelled = true;
72
+ ac.abort();
73
+ abortRef.current = null;
74
+ };
75
+ }, [enabled, optionsUrl, verifyUrl, csrfToken]);
76
+ }
@@ -0,0 +1,11 @@
1
+ export type PasswordStrengthScore = 0 | 1 | 2 | 3 | 4;
2
+ export interface PasswordStrengthResult {
3
+ score: PasswordStrengthScore;
4
+ feedback?: string[];
5
+ }
6
+ export type PasswordScorer = (password: string) => PasswordStrengthResult;
7
+ export interface UsePasswordStrengthOptions {
8
+ scorer?: PasswordScorer;
9
+ }
10
+ export declare function heuristicScorer(password: string): PasswordStrengthResult;
11
+ export declare function usePasswordStrength(password: string, options?: UsePasswordStrengthOptions): PasswordStrengthResult;
@@ -0,0 +1,34 @@
1
+ import { useMemo } from 'react';
2
+ export function heuristicScorer(password) {
3
+ if (!password)
4
+ return { score: 0, feedback: ['Enter a password.'] };
5
+ const feedback = [];
6
+ const classes = [/[a-z]/, /[A-Z]/, /[0-9]/, /[^A-Za-z0-9]/];
7
+ const variety = classes.filter((re) => re.test(password)).length;
8
+ const length = password.length;
9
+ let points = 0;
10
+ if (length >= 8)
11
+ points += 1;
12
+ if (length >= 12)
13
+ points += 1;
14
+ if (length >= 16)
15
+ points += 1;
16
+ if (variety >= 2)
17
+ points += 1;
18
+ if (variety >= 3)
19
+ points += 1;
20
+ if (length < 12)
21
+ feedback.push('Use at least 12 characters.');
22
+ if (!/[A-Z]/.test(password))
23
+ feedback.push('Add an uppercase letter.');
24
+ if (!/[0-9]/.test(password))
25
+ feedback.push('Add a number.');
26
+ if (!/[^A-Za-z0-9]/.test(password))
27
+ feedback.push('Add a symbol.');
28
+ const score = Math.max(0, Math.min(4, points));
29
+ return { score, feedback: feedback.length ? feedback : undefined };
30
+ }
31
+ export function usePasswordStrength(password, options = {}) {
32
+ const scorer = options.scorer ?? heuristicScorer;
33
+ return useMemo(() => scorer(password), [password, scorer]);
34
+ }
@@ -0,0 +1,13 @@
1
+ import { type ResourceState } from './use_resource.js';
2
+ import type { AuthUser } from '../types.js';
3
+ export interface ProfileUpdate {
4
+ name?: string;
5
+ avatarUrl?: string;
6
+ [key: string]: unknown;
7
+ }
8
+ export interface UseProfileResult extends ResourceState<AuthUser> {
9
+ actions: {
10
+ update(data: ProfileUpdate): Promise<void>;
11
+ };
12
+ }
13
+ export declare function useProfile(): UseProfileResult;
@@ -0,0 +1,28 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { useAuthkitConfig } from '../config.js';
3
+ import { useAuth } from '../use_auth.js';
4
+ import { jsonRequest } from './use_resource.js';
5
+ export function useProfile() {
6
+ const config = useAuthkitConfig();
7
+ const { user } = useAuth();
8
+ const [state, setState] = useState({
9
+ data: user,
10
+ loading: false,
11
+ error: null,
12
+ });
13
+ const update = useCallback(async (data) => {
14
+ setState((s) => ({ ...s, loading: true, error: null }));
15
+ try {
16
+ const updated = await jsonRequest(config.endpoints.profile, {
17
+ method: 'POST',
18
+ body: JSON.stringify(data),
19
+ csrfToken: config.csrfToken,
20
+ });
21
+ setState({ data: updated ?? { ...user, ...data }, loading: false, error: null });
22
+ }
23
+ catch (err) {
24
+ setState((s) => ({ ...s, loading: false, error: err }));
25
+ }
26
+ }, [config.endpoints.profile, config.csrfToken, user]);
27
+ return { ...state, actions: { update } };
28
+ }
@@ -0,0 +1,11 @@
1
+ export interface ResourceState<T> {
2
+ data: T | null;
3
+ loading: boolean;
4
+ error: Error | null;
5
+ }
6
+ export declare function jsonRequest<T>(url: string, init?: RequestInit & {
7
+ csrfToken?: string;
8
+ }): Promise<T>;
9
+ export declare function useResource<T>(url: string, csrfToken?: string): ResourceState<T> & {
10
+ refetch: () => Promise<void>;
11
+ };
@@ -0,0 +1,48 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ export async function jsonRequest(url, init = {}) {
3
+ const { csrfToken, headers, ...rest } = init;
4
+ const res = await fetch(url, {
5
+ credentials: 'same-origin',
6
+ headers: {
7
+ 'Accept': 'application/json',
8
+ ...(rest.body ? { 'Content-Type': 'application/json' } : {}),
9
+ ...(csrfToken ? { 'X-CSRF-TOKEN': csrfToken } : {}),
10
+ ...headers,
11
+ },
12
+ ...rest,
13
+ });
14
+ if (!res.ok) {
15
+ let message = `Request failed (${res.status})`;
16
+ try {
17
+ const body = await res.json();
18
+ if (body && typeof body.message === 'string')
19
+ message = body.message;
20
+ }
21
+ catch {
22
+ }
23
+ throw new Error(message);
24
+ }
25
+ const text = await res.text();
26
+ return (text ? JSON.parse(text) : null);
27
+ }
28
+ export function useResource(url, csrfToken) {
29
+ const [state, setState] = useState({
30
+ data: null,
31
+ loading: true,
32
+ error: null,
33
+ });
34
+ const refetch = useCallback(async () => {
35
+ setState((s) => ({ ...s, loading: true, error: null }));
36
+ try {
37
+ const data = await jsonRequest(url, { csrfToken });
38
+ setState({ data, loading: false, error: null });
39
+ }
40
+ catch (err) {
41
+ setState({ data: null, loading: false, error: err });
42
+ }
43
+ }, [url, csrfToken]);
44
+ useEffect(() => {
45
+ void refetch();
46
+ }, [refetch]);
47
+ return { ...state, refetch };
48
+ }
@@ -0,0 +1,16 @@
1
+ import { type ResourceState } from './use_resource.js';
2
+ export interface AuthSession {
3
+ id: string;
4
+ device?: string;
5
+ ip?: string;
6
+ lastSeenAt?: string;
7
+ current?: boolean;
8
+ [key: string]: unknown;
9
+ }
10
+ export interface UseSessionsResult extends ResourceState<AuthSession[]> {
11
+ actions: {
12
+ refetch(): Promise<void>;
13
+ revoke(id: string): Promise<void>;
14
+ };
15
+ }
16
+ export declare function useSessions(): UseSessionsResult;
@@ -0,0 +1,15 @@
1
+ import { useCallback } from 'react';
2
+ import { useAuthkitConfig } from '../config.js';
3
+ import { jsonRequest, useResource } from './use_resource.js';
4
+ export function useSessions() {
5
+ const config = useAuthkitConfig();
6
+ const { data, loading, error, refetch } = useResource(config.endpoints.sessions, config.csrfToken);
7
+ const revoke = useCallback(async (id) => {
8
+ await jsonRequest(`${config.endpoints.sessions}/${encodeURIComponent(id)}/revoke`, {
9
+ method: 'POST',
10
+ csrfToken: config.csrfToken,
11
+ });
12
+ await refetch();
13
+ }, [config.endpoints.sessions, config.csrfToken, refetch]);
14
+ return { data, loading, error, actions: { refetch, revoke } };
15
+ }
@@ -0,0 +1,6 @@
1
+ export interface SignInOptions {
2
+ returnTo?: string;
3
+ }
4
+ export declare function useSignIn(): {
5
+ signIn: (opts?: SignInOptions) => void;
6
+ };
@@ -0,0 +1,13 @@
1
+ import { useCallback } from 'react';
2
+ import { useAuthkitConfig, buildAuthUrl } from '../config.js';
3
+ import { currentUrl } from '../utils.js';
4
+ export function useSignIn() {
5
+ const config = useAuthkitConfig();
6
+ const signIn = useCallback((opts) => {
7
+ const returnTo = opts?.returnTo ?? currentUrl();
8
+ const url = buildAuthUrl(config.loginUrl, returnTo);
9
+ if (typeof window !== 'undefined')
10
+ window.location.assign(url);
11
+ }, [config.loginUrl]);
12
+ return { signIn };
13
+ }
@@ -0,0 +1,6 @@
1
+ export interface SignOutOptions {
2
+ returnTo?: string;
3
+ }
4
+ export declare function useSignOut(): {
5
+ signOut: (opts?: SignOutOptions) => void;
6
+ };
@@ -0,0 +1,11 @@
1
+ import { useCallback } from 'react';
2
+ import { useAuthkitConfig, buildAuthUrl } from '../config.js';
3
+ export function useSignOut() {
4
+ const config = useAuthkitConfig();
5
+ const signOut = useCallback((opts) => {
6
+ const url = buildAuthUrl(config.logoutUrl, opts?.returnTo);
7
+ if (typeof window !== 'undefined')
8
+ window.location.assign(url);
9
+ }, [config.logoutUrl]);
10
+ return { signOut };
11
+ }
@@ -0,0 +1,7 @@
1
+ export interface UseSwitchOrganizationResult {
2
+ loading: boolean;
3
+ error: Error | null;
4
+ activate(orgId: string): Promise<void>;
5
+ deactivate(): Promise<void>;
6
+ }
7
+ export declare function useSwitchOrganization(): UseSwitchOrganizationResult;