@absolutejs/auth 0.27.0-beta.8 → 0.27.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.
@@ -0,0 +1,11 @@
1
+ import type { AbuseContext } from './config';
2
+ export declare const verifyHcaptcha: ({ secret }: {
3
+ secret: string;
4
+ }) => (token: string | undefined, context: AbuseContext) => Promise<boolean>;
5
+ export declare const verifyRecaptcha: ({ minScore, secret }: {
6
+ minScore?: number;
7
+ secret: string;
8
+ }) => (token: string | undefined, context: AbuseContext) => Promise<boolean>;
9
+ export declare const verifyTurnstile: ({ secret }: {
10
+ secret: string;
11
+ }) => (token: string | undefined, context: AbuseContext) => Promise<boolean>;
@@ -0,0 +1,27 @@
1
+ export type AuthEventName = 'postLogin' | 'postLogout' | 'postMfa' | 'postOauthCallback' | 'postRegister' | 'preLogin' | 'preRegister';
2
+ export type AuthActionContext<UserType> = {
3
+ email?: string;
4
+ event: AuthEventName;
5
+ ip?: string;
6
+ metadata?: Record<string, unknown>;
7
+ user?: UserType;
8
+ userAgent?: string;
9
+ };
10
+ export type AuthActionResult = {
11
+ kind: 'deny';
12
+ reason: string;
13
+ } | {
14
+ kind: 'pass';
15
+ } | {
16
+ kind: 'redirect';
17
+ url: string;
18
+ };
19
+ export type AuthAction<UserType> = {
20
+ event: AuthEventName | AuthEventName[];
21
+ handler: (context: AuthActionContext<UserType>) => AuthActionResult | Promise<AuthActionResult>;
22
+ name: string;
23
+ };
24
+ export type AuthPipeline<UserType> = {
25
+ run: (event: AuthEventName, context: Omit<AuthActionContext<UserType>, 'event'>) => Promise<AuthActionResult>;
26
+ };
27
+ export declare const createActionPipeline: <UserType>(actions: AuthAction<UserType>[]) => AuthPipeline<UserType>;
@@ -1,9 +1,13 @@
1
- import type { KnownDeviceStore, LoginHistoryStore, RiskAction, RiskAssessment, RiskContext, RiskSignal } from './types';
1
+ import type { KnownDeviceStore, LoginHistoryStore, RiskAction, RiskAssessment, RiskContext, RiskSignal, RiskThresholds, RiskWeights, WeightedRiskAssessment } from './types';
2
2
  export type AdaptiveConfig = {
3
3
  historyLimit?: number;
4
4
  knownDeviceStore: KnownDeviceStore;
5
5
  loginHistoryStore: LoginHistoryStore;
6
6
  maxTravelKmh?: number;
7
+ offHours?: {
8
+ end: number;
9
+ start: number;
10
+ };
7
11
  rules?: Partial<Record<RiskSignal, RiskAction>>;
8
12
  velocityMaxAttempts?: number;
9
13
  velocityWindowMs?: number;
@@ -14,9 +18,17 @@ export declare const createRiskEngine: (config: AdaptiveConfig) => {
14
18
  recordAttempt: (context: RiskContext & {
15
19
  outcome: RiskAction;
16
20
  }) => Promise<void>;
21
+ scoreRisk: (context: RiskContext, options?: {
22
+ thresholds?: RiskThresholds;
23
+ weights?: RiskWeights;
24
+ }) => Promise<WeightedRiskAssessment>;
17
25
  trustDevice: (userId: string, deviceId: string, label?: string) => Promise<void>;
18
26
  };
19
27
  export declare const recordLoginAttempt: (config: AdaptiveConfig, context: RiskContext & {
20
28
  outcome: RiskAction;
21
29
  }) => Promise<void>;
30
+ export declare const scoreRisk: (config: AdaptiveConfig & {
31
+ thresholds?: RiskThresholds;
32
+ weights?: RiskWeights;
33
+ }, context: RiskContext) => Promise<WeightedRiskAssessment>;
22
34
  export declare const trustDevice: (config: AdaptiveConfig, userId: string, deviceId: string, label?: string) => Promise<void>;
@@ -0,0 +1,2 @@
1
+ export type DeviceSignals = Record<string, unknown>;
2
+ export declare const fingerprintDevice: (signals: DeviceSignals) => Promise<string>;
@@ -1,5 +1,5 @@
1
1
  export type RiskAction = 'allow' | 'deny' | 'step_up';
2
- export type RiskSignal = 'impossible_travel' | 'new_country' | 'new_device' | 'velocity';
2
+ export type RiskSignal = 'impossible_travel' | 'new_country' | 'new_device' | 'off_hours' | 'proxy' | 'velocity';
3
3
  export type GeoPoint = {
4
4
  country?: string;
5
5
  latitude?: number;
@@ -9,6 +9,8 @@ export type RiskContext = {
9
9
  deviceId: string;
10
10
  geo?: GeoPoint;
11
11
  ipAddress?: string;
12
+ isProxy?: boolean;
13
+ localHour?: number;
12
14
  now?: number;
13
15
  userId: string;
14
16
  };
@@ -20,6 +22,16 @@ export type RiskAssessment = {
20
22
  action: RiskAction;
21
23
  reasons: RiskReason[];
22
24
  };
25
+ export type RiskWeights = Partial<Record<RiskSignal, number>>;
26
+ export type RiskThresholds = {
27
+ deny: number;
28
+ stepUp: number;
29
+ };
30
+ export type WeightedRiskAssessment = {
31
+ action: RiskAction;
32
+ reasons: RiskReason[];
33
+ score: number;
34
+ };
23
35
  export type KnownDevice = {
24
36
  deviceId: string;
25
37
  firstSeenAt: number;
@@ -0,0 +1,2 @@
1
+ import type { AuditEvent } from './types';
2
+ export declare const exportAuditCsv: (events: AuditEvent[]) => string;
@@ -15,4 +15,5 @@ export type AuditEventFilter = {
15
15
  export type AuditSink = {
16
16
  append: (event: AuditEvent) => Promise<void>;
17
17
  list?: (filter?: AuditEventFilter) => Promise<AuditEvent[]>;
18
+ prune?: (before: number) => Promise<number>;
18
19
  };
@@ -0,0 +1,258 @@
1
+ export type AuthClientError = {
2
+ body: unknown;
3
+ message: string;
4
+ status: number;
5
+ };
6
+ export type AuthClientResult<T> = {
7
+ data: null;
8
+ error: AuthClientError;
9
+ } | {
10
+ data: T;
11
+ error: null;
12
+ };
13
+ export type AuthClientRoutes = {
14
+ emailVerify?: string;
15
+ emailVerifyRequest?: string;
16
+ login?: string;
17
+ magicLinkRequest?: string;
18
+ magicLinkVerify?: string;
19
+ mfaChallenge?: string;
20
+ mfaSetup?: string;
21
+ mfaVerifySetup?: string;
22
+ passkeyAuthenticateOptions?: string;
23
+ passkeyAuthenticateVerify?: string;
24
+ passkeyList?: string;
25
+ passkeyRegisterOptions?: string;
26
+ passkeyRegisterVerify?: string;
27
+ passkeyRemove?: string;
28
+ passwordReset?: string;
29
+ passwordResetRequest?: string;
30
+ register?: string;
31
+ sessions?: string;
32
+ signout?: string;
33
+ };
34
+ export type AuthClientConfig = {
35
+ baseUrl?: string;
36
+ credentials?: RequestCredentials;
37
+ fetch?: typeof fetch;
38
+ routes?: AuthClientRoutes;
39
+ };
40
+ export declare const createAuthClient: ({ baseUrl, credentials, fetch: fetchImpl, routes }?: AuthClientConfig) => {
41
+ emailVerification: {
42
+ request: (body: {
43
+ email: string;
44
+ }) => Promise<{
45
+ data: null;
46
+ error: AuthClientError;
47
+ } | {
48
+ data: {
49
+ ok: true;
50
+ };
51
+ error: null;
52
+ }>;
53
+ verify: (body: {
54
+ token: string;
55
+ }) => Promise<{
56
+ data: null;
57
+ error: AuthClientError;
58
+ } | {
59
+ data: {
60
+ ok: true;
61
+ };
62
+ error: null;
63
+ }>;
64
+ };
65
+ mfa: {
66
+ challenge: (body: {
67
+ code: string;
68
+ }) => Promise<{
69
+ data: null;
70
+ error: AuthClientError;
71
+ } | {
72
+ data: {
73
+ status: "authenticated";
74
+ };
75
+ error: null;
76
+ }>;
77
+ setup: () => Promise<{
78
+ data: null;
79
+ error: AuthClientError;
80
+ } | {
81
+ data: {
82
+ secret: string;
83
+ uri: string;
84
+ };
85
+ error: null;
86
+ }>;
87
+ verifySetup: (body: {
88
+ code: string;
89
+ }) => Promise<{
90
+ data: null;
91
+ error: AuthClientError;
92
+ } | {
93
+ data: {
94
+ backupCodes: string[];
95
+ };
96
+ error: null;
97
+ }>;
98
+ };
99
+ passkeys: {
100
+ authenticateOptions: () => Promise<{
101
+ data: null;
102
+ error: AuthClientError;
103
+ } | {
104
+ data: unknown;
105
+ error: null;
106
+ }>;
107
+ authenticateVerify: (response: unknown) => Promise<{
108
+ data: null;
109
+ error: AuthClientError;
110
+ } | {
111
+ data: {
112
+ status: "authenticated";
113
+ };
114
+ error: null;
115
+ }>;
116
+ list: () => Promise<{
117
+ data: null;
118
+ error: AuthClientError;
119
+ } | {
120
+ data: unknown[];
121
+ error: null;
122
+ }>;
123
+ registerOptions: () => Promise<{
124
+ data: null;
125
+ error: AuthClientError;
126
+ } | {
127
+ data: unknown;
128
+ error: null;
129
+ }>;
130
+ registerVerify: (response: unknown) => Promise<{
131
+ data: null;
132
+ error: AuthClientError;
133
+ } | {
134
+ data: {
135
+ ok: true;
136
+ };
137
+ error: null;
138
+ }>;
139
+ remove: (credentialId: string) => Promise<{
140
+ data: null;
141
+ error: AuthClientError;
142
+ } | {
143
+ data: {
144
+ ok: true;
145
+ };
146
+ error: null;
147
+ }>;
148
+ };
149
+ passwordless: {
150
+ requestMagicLink: (body: {
151
+ email: string;
152
+ }) => Promise<{
153
+ data: null;
154
+ error: AuthClientError;
155
+ } | {
156
+ data: {
157
+ ok: true;
158
+ };
159
+ error: null;
160
+ }>;
161
+ verifyMagicLink: (body: {
162
+ token: string;
163
+ }) => Promise<{
164
+ data: null;
165
+ error: AuthClientError;
166
+ } | {
167
+ data: {
168
+ status: "authenticated";
169
+ };
170
+ error: null;
171
+ }>;
172
+ };
173
+ passwordReset: {
174
+ confirm: (body: {
175
+ password: string;
176
+ token: string;
177
+ }) => Promise<{
178
+ data: null;
179
+ error: AuthClientError;
180
+ } | {
181
+ data: {
182
+ ok: true;
183
+ };
184
+ error: null;
185
+ }>;
186
+ request: (body: {
187
+ email: string;
188
+ }) => Promise<{
189
+ data: null;
190
+ error: AuthClientError;
191
+ } | {
192
+ data: {
193
+ ok: true;
194
+ };
195
+ error: null;
196
+ }>;
197
+ };
198
+ sessions: {
199
+ list: () => Promise<{
200
+ data: null;
201
+ error: AuthClientError;
202
+ } | {
203
+ data: unknown[];
204
+ error: null;
205
+ }>;
206
+ revoke: (sessionId: string) => Promise<{
207
+ data: null;
208
+ error: AuthClientError;
209
+ } | {
210
+ data: {
211
+ ok: true;
212
+ };
213
+ error: null;
214
+ }>;
215
+ };
216
+ signIn: {
217
+ email: (body: {
218
+ email: string;
219
+ password: string;
220
+ }) => Promise<{
221
+ data: null;
222
+ error: AuthClientError;
223
+ } | {
224
+ data: {
225
+ passwordCompromised?: boolean;
226
+ status: "authenticated" | "mfa_required";
227
+ };
228
+ error: null;
229
+ }>;
230
+ };
231
+ signUp: {
232
+ email: (body: {
233
+ email: string;
234
+ password: string;
235
+ [extra: string]: unknown;
236
+ }) => Promise<{
237
+ data: null;
238
+ error: AuthClientError;
239
+ } | {
240
+ data: {
241
+ status: "authenticated";
242
+ } | {
243
+ status: "verification_required";
244
+ };
245
+ error: null;
246
+ }>;
247
+ };
248
+ signOut: () => Promise<{
249
+ data: null;
250
+ error: AuthClientError;
251
+ } | {
252
+ data: {
253
+ ok: true;
254
+ } | null;
255
+ error: null;
256
+ }>;
257
+ };
258
+ export type AuthClient = ReturnType<typeof createAuthClient>;
@@ -0,0 +1 @@
1
+ export * from './createAuthClient';
@@ -0,0 +1,127 @@
1
+ // @bun
2
+ // src/client/createAuthClient.ts
3
+ var DEFAULT_ROUTES = {
4
+ emailVerify: "/auth/verify-email",
5
+ emailVerifyRequest: "/auth/verify-email/request",
6
+ login: "/auth/login",
7
+ magicLinkRequest: "/auth/passwordless/magic-link",
8
+ magicLinkVerify: "/auth/passwordless/magic-link/verify",
9
+ mfaChallenge: "/auth/mfa/totp/challenge",
10
+ mfaSetup: "/auth/mfa/totp/setup",
11
+ mfaVerifySetup: "/auth/mfa/totp/verify",
12
+ passkeyAuthenticateOptions: "/auth/webauthn/authenticate/options",
13
+ passkeyAuthenticateVerify: "/auth/webauthn/authenticate/verify",
14
+ passkeyList: "/auth/webauthn/credentials",
15
+ passkeyRegisterOptions: "/auth/webauthn/register/options",
16
+ passkeyRegisterVerify: "/auth/webauthn/register/verify",
17
+ passkeyRemove: "/auth/webauthn/credentials",
18
+ passwordReset: "/auth/reset-password",
19
+ passwordResetRequest: "/auth/reset-password/request",
20
+ register: "/auth/register",
21
+ sessions: "/auth/sessions",
22
+ signout: "/auth/signout"
23
+ };
24
+ var succeed = (data) => ({
25
+ data,
26
+ error: null
27
+ });
28
+ var fail = (error) => ({
29
+ data: null,
30
+ error
31
+ });
32
+ var errorFor = (response, body) => ({
33
+ body,
34
+ message: typeof body === "string" ? body : readMessage(body) ?? response.statusText,
35
+ status: response.status
36
+ });
37
+ var createAuthClient = ({
38
+ baseUrl = "",
39
+ credentials = "same-origin",
40
+ fetch: fetchImpl = fetch,
41
+ routes
42
+ } = {}) => {
43
+ const resolvedRoutes = {
44
+ ...DEFAULT_ROUTES,
45
+ ...routes
46
+ };
47
+ const request = async (path, init) => {
48
+ try {
49
+ const response = await fetchImpl(`${baseUrl}${path}`, {
50
+ credentials,
51
+ ...init
52
+ });
53
+ const text = await response.text();
54
+ const body = text === "" ? null : safeJson(text);
55
+ if (!response.ok)
56
+ return fail(errorFor(response, body));
57
+ return succeed(body);
58
+ } catch (caught) {
59
+ const message = caught instanceof Error ? caught.message : "network";
60
+ return fail({ body: null, message, status: 0 });
61
+ }
62
+ };
63
+ const post = (path, body, method = "POST") => request(path, {
64
+ body: body === undefined ? undefined : JSON.stringify(body),
65
+ headers: body === undefined ? undefined : { "content-type": "application/json" },
66
+ method
67
+ });
68
+ const get = (path) => request(path, { method: "GET" });
69
+ const del = (path) => request(path, { method: "DELETE" });
70
+ return {
71
+ emailVerification: {
72
+ request: (body) => post(resolvedRoutes.emailVerifyRequest, body),
73
+ verify: (body) => post(resolvedRoutes.emailVerify, body)
74
+ },
75
+ mfa: {
76
+ challenge: (body) => post(resolvedRoutes.mfaChallenge, body),
77
+ setup: () => post(resolvedRoutes.mfaSetup),
78
+ verifySetup: (body) => post(resolvedRoutes.mfaVerifySetup, body)
79
+ },
80
+ passkeys: {
81
+ authenticateOptions: () => post(resolvedRoutes.passkeyAuthenticateOptions),
82
+ authenticateVerify: (response) => post(resolvedRoutes.passkeyAuthenticateVerify, response),
83
+ list: () => get(resolvedRoutes.passkeyList),
84
+ registerOptions: () => post(resolvedRoutes.passkeyRegisterOptions),
85
+ registerVerify: (response) => post(resolvedRoutes.passkeyRegisterVerify, response),
86
+ remove: (credentialId) => del(`${resolvedRoutes.passkeyRemove}/${encodeURIComponent(credentialId)}`)
87
+ },
88
+ passwordless: {
89
+ requestMagicLink: (body) => post(resolvedRoutes.magicLinkRequest, body),
90
+ verifyMagicLink: (body) => post(resolvedRoutes.magicLinkVerify, body)
91
+ },
92
+ passwordReset: {
93
+ confirm: (body) => post(resolvedRoutes.passwordReset, body),
94
+ request: (body) => post(resolvedRoutes.passwordResetRequest, body)
95
+ },
96
+ sessions: {
97
+ list: () => get(resolvedRoutes.sessions),
98
+ revoke: (sessionId) => del(`${resolvedRoutes.sessions}/${encodeURIComponent(sessionId)}`)
99
+ },
100
+ signIn: {
101
+ email: (body) => post(resolvedRoutes.login, body)
102
+ },
103
+ signUp: {
104
+ email: (body) => post(resolvedRoutes.register, body)
105
+ },
106
+ signOut: () => post(resolvedRoutes.signout)
107
+ };
108
+ };
109
+ var safeJson = (text) => {
110
+ try {
111
+ return JSON.parse(text);
112
+ } catch {
113
+ return text;
114
+ }
115
+ };
116
+ var readMessage = (body) => {
117
+ if (typeof body !== "object" || body === null)
118
+ return;
119
+ const message = Reflect.get(body, "message");
120
+ return typeof message === "string" ? message : undefined;
121
+ };
122
+ export {
123
+ createAuthClient
124
+ };
125
+
126
+ //# debugId=AA9996133C04E1BA64756E2164756E21
127
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/client/createAuthClient.ts"],
4
+ "sourcesContent": [
5
+ "// Framework-agnostic client SDK over the auth endpoints. Thin fetch wrappers with\n// configurable routes, a uniform `{ data, error }` return, and same-origin cookies on by\n// default — the primitive the React hooks (`./react`) and future Vue/Solid composables wrap.\n// HTMX is the special case (declarative server fragments via `./htmx`); for every other UI\n// framework the natural primitive is this client + your framework's reactivity.\n\nexport type AuthClientError = {\n\tbody: unknown;\n\tmessage: string;\n\tstatus: number;\n};\n\nexport type AuthClientResult<T> =\n\t| { data: null; error: AuthClientError }\n\t| { data: T; error: null };\n\n// Each entry is the URL the consumer's auth() mounted that flow at. Defaults match the\n// package defaults; override to match your own custom routes.\nexport type AuthClientRoutes = {\n\temailVerify?: string;\n\temailVerifyRequest?: string;\n\tlogin?: string;\n\tmagicLinkRequest?: string;\n\tmagicLinkVerify?: string;\n\tmfaChallenge?: string;\n\tmfaSetup?: string;\n\tmfaVerifySetup?: string;\n\tpasskeyAuthenticateOptions?: string;\n\tpasskeyAuthenticateVerify?: string;\n\tpasskeyList?: string;\n\tpasskeyRegisterOptions?: string;\n\tpasskeyRegisterVerify?: string;\n\tpasskeyRemove?: string;\n\tpasswordReset?: string;\n\tpasswordResetRequest?: string;\n\tregister?: string;\n\tsessions?: string;\n\tsignout?: string;\n};\n\nconst DEFAULT_ROUTES: Required<AuthClientRoutes> = {\n\temailVerify: '/auth/verify-email',\n\temailVerifyRequest: '/auth/verify-email/request',\n\tlogin: '/auth/login',\n\tmagicLinkRequest: '/auth/passwordless/magic-link',\n\tmagicLinkVerify: '/auth/passwordless/magic-link/verify',\n\tmfaChallenge: '/auth/mfa/totp/challenge',\n\tmfaSetup: '/auth/mfa/totp/setup',\n\tmfaVerifySetup: '/auth/mfa/totp/verify',\n\tpasskeyAuthenticateOptions: '/auth/webauthn/authenticate/options',\n\tpasskeyAuthenticateVerify: '/auth/webauthn/authenticate/verify',\n\tpasskeyList: '/auth/webauthn/credentials',\n\tpasskeyRegisterOptions: '/auth/webauthn/register/options',\n\tpasskeyRegisterVerify: '/auth/webauthn/register/verify',\n\tpasskeyRemove: '/auth/webauthn/credentials',\n\tpasswordReset: '/auth/reset-password',\n\tpasswordResetRequest: '/auth/reset-password/request',\n\tregister: '/auth/register',\n\tsessions: '/auth/sessions',\n\tsignout: '/auth/signout'\n};\n\nexport type AuthClientConfig = {\n\tbaseUrl?: string;\n\tcredentials?: RequestCredentials;\n\tfetch?: typeof fetch;\n\troutes?: AuthClientRoutes;\n};\n\nconst succeed = <T>(data: T): AuthClientResult<T> => ({\n\tdata,\n\terror: null\n});\n\nconst fail = (error: AuthClientError): AuthClientResult<never> => ({\n\tdata: null,\n\terror\n});\n\nconst errorFor = (response: Response, body: unknown): AuthClientError => ({\n\tbody,\n\tmessage:\n\t\ttypeof body === 'string'\n\t\t\t? body\n\t\t\t: (readMessage(body) ?? response.statusText),\n\tstatus: response.status\n});\n\nexport const createAuthClient = ({\n\tbaseUrl = '',\n\tcredentials = 'same-origin',\n\tfetch: fetchImpl = fetch,\n\troutes\n}: AuthClientConfig = {}) => {\n\tconst resolvedRoutes: Required<AuthClientRoutes> = {\n\t\t...DEFAULT_ROUTES,\n\t\t...routes\n\t};\n\n\tconst request = async <T>(path: string, init: RequestInit) => {\n\t\ttry {\n\t\t\tconst response = await fetchImpl(`${baseUrl}${path}`, {\n\t\t\t\tcredentials,\n\t\t\t\t...init\n\t\t\t});\n\t\t\tconst text = await response.text();\n\t\t\tconst body: unknown = text === '' ? null : safeJson(text);\n\t\t\tif (!response.ok) return fail(errorFor(response, body));\n\n\t\t\t// Trust the caller's expectation at the deserialization boundary.\n\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- HTTP response is unknown until parsed; the call site declares the expected T\n\t\t\treturn succeed(body as T);\n\t\t} catch (caught) {\n\t\t\tconst message = caught instanceof Error ? caught.message : 'network';\n\n\t\t\treturn fail({ body: null, message, status: 0 });\n\t\t}\n\t};\n\n\tconst post = <T>(path: string, body?: unknown, method = 'POST') =>\n\t\trequest<T>(path, {\n\t\t\tbody: body === undefined ? undefined : JSON.stringify(body),\n\t\t\theaders:\n\t\t\t\tbody === undefined ? undefined : { 'content-type': 'application/json' },\n\t\t\tmethod\n\t\t});\n\n\tconst get = <T>(path: string) => request<T>(path, { method: 'GET' });\n\n\tconst del = <T>(path: string) => request<T>(path, { method: 'DELETE' });\n\n\treturn {\n\t\temailVerification: {\n\t\t\trequest: (body: { email: string }) =>\n\t\t\t\tpost<{ ok: true }>(resolvedRoutes.emailVerifyRequest, body),\n\t\t\tverify: (body: { token: string }) =>\n\t\t\t\tpost<{ ok: true }>(resolvedRoutes.emailVerify, body)\n\t\t},\n\t\tmfa: {\n\t\t\tchallenge: (body: { code: string }) =>\n\t\t\t\tpost<{ status: 'authenticated' }>(resolvedRoutes.mfaChallenge, body),\n\t\t\tsetup: () =>\n\t\t\t\tpost<{ secret: string; uri: string }>(resolvedRoutes.mfaSetup),\n\t\t\tverifySetup: (body: { code: string }) =>\n\t\t\t\tpost<{ backupCodes: string[] }>(\n\t\t\t\t\tresolvedRoutes.mfaVerifySetup,\n\t\t\t\t\tbody\n\t\t\t\t)\n\t\t},\n\t\tpasskeys: {\n\t\t\tauthenticateOptions: () =>\n\t\t\t\tpost<unknown>(resolvedRoutes.passkeyAuthenticateOptions),\n\t\t\tauthenticateVerify: (response: unknown) =>\n\t\t\t\tpost<{ status: 'authenticated' }>(\n\t\t\t\t\tresolvedRoutes.passkeyAuthenticateVerify,\n\t\t\t\t\tresponse\n\t\t\t\t),\n\t\t\tlist: () => get<unknown[]>(resolvedRoutes.passkeyList),\n\t\t\tregisterOptions: () =>\n\t\t\t\tpost<unknown>(resolvedRoutes.passkeyRegisterOptions),\n\t\t\tregisterVerify: (response: unknown) =>\n\t\t\t\tpost<{ ok: true }>(resolvedRoutes.passkeyRegisterVerify, response),\n\t\t\tremove: (credentialId: string) =>\n\t\t\t\tdel<{ ok: true }>(\n\t\t\t\t\t`${resolvedRoutes.passkeyRemove}/${encodeURIComponent(credentialId)}`\n\t\t\t\t)\n\t\t},\n\t\tpasswordless: {\n\t\t\trequestMagicLink: (body: { email: string }) =>\n\t\t\t\tpost<{ ok: true }>(resolvedRoutes.magicLinkRequest, body),\n\t\t\tverifyMagicLink: (body: { token: string }) =>\n\t\t\t\tpost<{ status: 'authenticated' }>(\n\t\t\t\t\tresolvedRoutes.magicLinkVerify,\n\t\t\t\t\tbody\n\t\t\t\t)\n\t\t},\n\t\tpasswordReset: {\n\t\t\tconfirm: (body: { password: string; token: string }) =>\n\t\t\t\tpost<{ ok: true }>(resolvedRoutes.passwordReset, body),\n\t\t\trequest: (body: { email: string }) =>\n\t\t\t\tpost<{ ok: true }>(resolvedRoutes.passwordResetRequest, body)\n\t\t},\n\t\tsessions: {\n\t\t\tlist: () => get<unknown[]>(resolvedRoutes.sessions),\n\t\t\trevoke: (sessionId: string) =>\n\t\t\t\tdel<{ ok: true }>(\n\t\t\t\t\t`${resolvedRoutes.sessions}/${encodeURIComponent(sessionId)}`\n\t\t\t\t)\n\t\t},\n\t\tsignIn: {\n\t\t\temail: (body: { email: string; password: string }) =>\n\t\t\t\tpost<{\n\t\t\t\t\tpasswordCompromised?: boolean;\n\t\t\t\t\tstatus: 'authenticated' | 'mfa_required';\n\t\t\t\t}>(resolvedRoutes.login, body)\n\t\t},\n\t\tsignUp: {\n\t\t\temail: (body: {\n\t\t\t\temail: string;\n\t\t\t\tpassword: string;\n\t\t\t\t[extra: string]: unknown;\n\t\t\t}) =>\n\t\t\t\tpost<\n\t\t\t\t\t| { status: 'authenticated' }\n\t\t\t\t\t| { status: 'verification_required' }\n\t\t\t\t>(resolvedRoutes.register, body)\n\t\t},\n\t\tsignOut: () => post<{ ok: true } | null>(resolvedRoutes.signout)\n\t};\n};\n\nconst safeJson = (text: string) => {\n\ttry {\n\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- deserialization boundary\n\t\treturn JSON.parse(text) as unknown;\n\t} catch {\n\t\treturn text;\n\t}\n};\n\nconst readMessage = (body: unknown) => {\n\tif (typeof body !== 'object' || body === null) return undefined;\n\tconst message: unknown = Reflect.get(body, 'message');\n\n\treturn typeof message === 'string' ? message : undefined;\n};\n\nexport type AuthClient = ReturnType<typeof createAuthClient>;\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "AA9996133C04E1BA64756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,62 @@
1
+ import type { AuthClient, AuthClientError } from './createAuthClient';
2
+ type Mutator<Args, Data> = (args: Args) => Promise<{
3
+ data: Data | null;
4
+ error: AuthClientError | null;
5
+ }>;
6
+ type MutationState<Args, Data> = {
7
+ data: Data | null;
8
+ error: AuthClientError | null;
9
+ isPending: boolean;
10
+ mutate: Mutator<Args, Data>;
11
+ reset: () => void;
12
+ };
13
+ export declare const useMagicLink: (client: AuthClient) => MutationState<{
14
+ email: string;
15
+ }, {
16
+ ok: true;
17
+ }>;
18
+ export declare const useMfaChallenge: (client: AuthClient) => MutationState<{
19
+ code: string;
20
+ }, {
21
+ status: "authenticated";
22
+ }>;
23
+ export declare const usePasswordReset: (client: AuthClient) => MutationState<{
24
+ email: string;
25
+ }, {
26
+ ok: true;
27
+ }>;
28
+ export declare const useSessions: (client: AuthClient) => {
29
+ data: unknown[] | null;
30
+ error: AuthClientError | null;
31
+ isPending: boolean;
32
+ refetch: () => Promise<void>;
33
+ revoke: (sessionId: string) => Promise<{
34
+ data: null;
35
+ error: AuthClientError;
36
+ } | {
37
+ data: {
38
+ ok: true;
39
+ };
40
+ error: null;
41
+ }>;
42
+ };
43
+ export declare const useSignIn: (client: AuthClient) => MutationState<{
44
+ email: string;
45
+ password: string;
46
+ }, {
47
+ passwordCompromised?: boolean;
48
+ status: "authenticated" | "mfa_required";
49
+ }>;
50
+ export declare const useSignOut: (client: AuthClient) => MutationState<unknown, {
51
+ ok: true;
52
+ }>;
53
+ export declare const useSignUp: (client: AuthClient) => MutationState<{
54
+ [extra: string]: unknown;
55
+ email: string;
56
+ password: string;
57
+ }, {
58
+ status: "authenticated";
59
+ } | {
60
+ status: "verification_required";
61
+ }>;
62
+ export {};
@@ -0,0 +1,75 @@
1
+ // @bun
2
+ // src/client/react.ts
3
+ import { useCallback, useEffect, useRef, useState } from "react";
4
+ var useMutation = (run) => {
5
+ const [data, setData] = useState(null);
6
+ const [error, setError] = useState(null);
7
+ const [isPending, setIsPending] = useState(false);
8
+ const mountedRef = useRef(true);
9
+ useEffect(() => () => {
10
+ mountedRef.current = false;
11
+ }, []);
12
+ const mutate = useCallback(async (args) => {
13
+ setIsPending(true);
14
+ setError(null);
15
+ const result = await run(args);
16
+ if (mountedRef.current) {
17
+ setData(result.data);
18
+ setError(result.error);
19
+ setIsPending(false);
20
+ }
21
+ return result;
22
+ }, [run]);
23
+ const reset = useCallback(() => {
24
+ setData(null);
25
+ setError(null);
26
+ setIsPending(false);
27
+ }, []);
28
+ return { data, error, isPending, mutate, reset };
29
+ };
30
+ var useMagicLink = (client) => useMutation(client.passwordless.requestMagicLink);
31
+ var useMfaChallenge = (client) => useMutation(client.mfa.challenge);
32
+ var usePasswordReset = (client) => useMutation(client.passwordReset.request);
33
+ var useSessions = (client) => {
34
+ const [data, setData] = useState(null);
35
+ const [error, setError] = useState(null);
36
+ const [isPending, setIsPending] = useState(true);
37
+ const mountedRef = useRef(true);
38
+ useEffect(() => () => {
39
+ mountedRef.current = false;
40
+ }, []);
41
+ const refetch = useCallback(async () => {
42
+ setIsPending(true);
43
+ const result = await client.sessions.list();
44
+ if (mountedRef.current) {
45
+ setData(result.data);
46
+ setError(result.error);
47
+ setIsPending(false);
48
+ }
49
+ }, [client]);
50
+ useEffect(() => {
51
+ refetch();
52
+ }, [refetch]);
53
+ const revoke = useCallback(async (sessionId) => {
54
+ const result = await client.sessions.revoke(sessionId);
55
+ if (result.error === null)
56
+ await refetch();
57
+ return result;
58
+ }, [client, refetch]);
59
+ return { data, error, isPending, refetch, revoke };
60
+ };
61
+ var useSignIn = (client) => useMutation(client.signIn.email);
62
+ var useSignOut = (client) => useMutation(client.signOut);
63
+ var useSignUp = (client) => useMutation(client.signUp.email);
64
+ export {
65
+ useSignUp,
66
+ useSignOut,
67
+ useSignIn,
68
+ useSessions,
69
+ usePasswordReset,
70
+ useMfaChallenge,
71
+ useMagicLink
72
+ };
73
+
74
+ //# debugId=6E80ACAADF4D834764756E2164756E21
75
+ //# sourceMappingURL=react.js.map