next-sanctum 0.1.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,236 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+
3
+ require('server-only');
4
+ var headers = require('next/headers');
5
+
6
+ /**
7
+ * Error types for next-sanctum. All failures are normalized to `SanctumError`
8
+ * so consumers can handle them consistently (see plan §10: errors must not leak).
9
+ */ /** Base error for all module failures. */ class SanctumError extends Error {
10
+ constructor(message, options){
11
+ super(message, {
12
+ cause: options.cause
13
+ });
14
+ this.name = "SanctumError";
15
+ this.kind = options.kind;
16
+ this.status = options.status;
17
+ this.data = options.data;
18
+ }
19
+ }
20
+ /** Invalid configuration — fail-fast on init (see resolveConfig). */ class ConfigError extends SanctumError {
21
+ constructor(message, cause){
22
+ super(message, {
23
+ kind: "config",
24
+ cause
25
+ });
26
+ this.name = "ConfigError";
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Resolve the server-side base URL (`SANCTUM_BASE_URL`, falling back to the public var
32
+ * for dev). Shared by `server.ts`/`actions.ts`. Fails fast with a clear ConfigError.
33
+ */ function resolveServerBaseUrl(explicit, label = "server helpers") {
34
+ const baseUrl = explicit ?? process.env.SANCTUM_BASE_URL ?? process.env.NEXT_PUBLIC_SANCTUM_BASE_URL;
35
+ if (!baseUrl) {
36
+ throw new ConfigError(`SANCTUM_BASE_URL (server) is not set for next-sanctum ${label}.`);
37
+ }
38
+ return baseUrl.replace(/\/+$/, "");
39
+ }
40
+
41
+ /**
42
+ * Shared Set-Cookie parsing helpers (pure — no `next`/`server-only` imports, so this
43
+ * stays isomorphic and tree-shakes out of the client graph). Used by `server.ts` and
44
+ * `actions.ts` to mirror Laravel's Set-Cookie into Next's writable cookie store.
45
+ */ /**
46
+ * Read all Set-Cookie headers as an array. Prefers `Headers.getSetCookie()` (Node ≥18.14,
47
+ * all supported Next runtimes). The single-header fallback only handles one cookie — we do
48
+ * NOT attempt to comma-split, which is unreliable (Expires dates contain commas).
49
+ */ function getSetCookies(headers) {
50
+ const anyHeaders = headers;
51
+ if (typeof anyHeaders.getSetCookie === "function") return anyHeaders.getSetCookie();
52
+ const value = headers.get("set-cookie");
53
+ return value ? [
54
+ value
55
+ ] : [];
56
+ }
57
+ /** Parse a single Set-Cookie header. Validates attributes; returns null when unusable. */ function parseSetCookie(header) {
58
+ const segments = header.split(";");
59
+ const first = segments.shift();
60
+ if (!first) return null;
61
+ const eq = first.indexOf("=");
62
+ if (eq === -1) return null;
63
+ const name = first.slice(0, eq).trim();
64
+ if (name === "") return null;
65
+ let value = first.slice(eq + 1).trim();
66
+ if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
67
+ value = value.slice(1, -1);
68
+ }
69
+ const options = {};
70
+ for (const segment of segments){
71
+ const idx = segment.indexOf("=");
72
+ const key = (idx === -1 ? segment : segment.slice(0, idx)).trim().toLowerCase();
73
+ const val = idx === -1 ? "" : segment.slice(idx + 1).trim();
74
+ switch(key){
75
+ case "path":
76
+ options.path = val;
77
+ break;
78
+ case "domain":
79
+ options.domain = val;
80
+ break;
81
+ case "max-age":
82
+ {
83
+ const n = Number(val);
84
+ if (val !== "" && Number.isFinite(n)) options.maxAge = n;
85
+ break;
86
+ }
87
+ case "expires":
88
+ {
89
+ const d = new Date(val);
90
+ if (!Number.isNaN(d.getTime())) options.expires = d;
91
+ break;
92
+ }
93
+ case "samesite":
94
+ {
95
+ const lower = val.toLowerCase();
96
+ if (lower === "lax" || lower === "strict" || lower === "none") {
97
+ options.sameSite = lower;
98
+ }
99
+ break;
100
+ }
101
+ case "secure":
102
+ options.secure = true;
103
+ break;
104
+ case "httponly":
105
+ options.httpOnly = true;
106
+ break;
107
+ }
108
+ }
109
+ return {
110
+ name,
111
+ value,
112
+ options
113
+ };
114
+ }
115
+ /** Mirror a response's Set-Cookie headers into a writable cookie store. */ function applySetCookies(store, response) {
116
+ for (const raw of getSetCookies(response.headers)){
117
+ const parsed = parseSetCookie(raw);
118
+ if (!parsed) continue;
119
+ try {
120
+ store.set(parsed.name, parsed.value, parsed.options);
121
+ } catch {
122
+ // store is read-only (e.g. called from a Server Component) — ignore.
123
+ }
124
+ }
125
+ }
126
+
127
+ const DEFAULTS = {
128
+ csrf: "/sanctum/csrf-cookie",
129
+ login: "/login",
130
+ logout: "/logout",
131
+ register: "/register",
132
+ forgotPassword: "/forgot-password",
133
+ resetPassword: "/reset-password",
134
+ confirmPassword: "/user/confirm-password",
135
+ twoFactorChallenge: "/two-factor-challenge"
136
+ };
137
+ async function readErrors(response) {
138
+ try {
139
+ const data = await response.json();
140
+ return data?.errors;
141
+ } catch {
142
+ return undefined;
143
+ }
144
+ }
145
+ async function statefulPost(path, json, config) {
146
+ const store = await headers.cookies();
147
+ const base = resolveServerBaseUrl(config?.baseUrl, "actions");
148
+ const csrfCookie = config?.csrf?.cookie ?? "XSRF-TOKEN";
149
+ const csrfHeader = config?.csrf?.header ?? "X-XSRF-TOKEN";
150
+ const csrfPath = config?.endpoints?.csrf ?? DEFAULTS.csrf;
151
+ let token = store.get(csrfCookie)?.value;
152
+ if (!token) {
153
+ const csrfResponse = await fetch(`${base}${csrfPath}`, {
154
+ headers: {
155
+ cookie: store.toString(),
156
+ accept: "application/json"
157
+ },
158
+ cache: "no-store"
159
+ });
160
+ applySetCookies(store, csrfResponse);
161
+ token = store.get(csrfCookie)?.value;
162
+ }
163
+ const headers$1 = {
164
+ accept: "application/json",
165
+ "content-type": "application/json",
166
+ cookie: store.toString()
167
+ };
168
+ if (token) headers$1[csrfHeader] = decodeURIComponent(token);
169
+ const response = await fetch(`${base}${path}`, {
170
+ method: "POST",
171
+ headers: headers$1,
172
+ body: JSON.stringify(json ?? {}),
173
+ cache: "no-store"
174
+ });
175
+ applySetCookies(store, response);
176
+ return {
177
+ ok: response.ok,
178
+ status: response.status,
179
+ raw: response
180
+ };
181
+ }
182
+ async function toResult(result) {
183
+ if (!result.ok) {
184
+ return {
185
+ ok: false,
186
+ status: result.status,
187
+ errors: result.status === 422 ? await readErrors(result.raw) : undefined
188
+ };
189
+ }
190
+ return {
191
+ ok: true,
192
+ status: result.status
193
+ };
194
+ }
195
+ /** Login (CSRF → POST /login). Check `twoFactor` on the result before assuming success. */ async function login(credentials, config) {
196
+ const result = await statefulPost(config?.endpoints?.login ?? DEFAULTS.login, credentials, config);
197
+ if (!result.ok) return toResult(result);
198
+ let twoFactor = false;
199
+ try {
200
+ const data = await result.raw.json();
201
+ twoFactor = Boolean(data?.two_factor);
202
+ } catch {
203
+ // empty body → not 2FA
204
+ }
205
+ return {
206
+ ok: true,
207
+ status: result.status,
208
+ twoFactor
209
+ };
210
+ }
211
+ async function logout(config) {
212
+ return toResult(await statefulPost(config?.endpoints?.logout ?? DEFAULTS.logout, {}, config));
213
+ }
214
+ async function register(payload, config) {
215
+ return toResult(await statefulPost(config?.endpoints?.register ?? DEFAULTS.register, payload, config));
216
+ }
217
+ async function twoFactorChallenge(payload, config) {
218
+ return toResult(await statefulPost(config?.endpoints?.twoFactorChallenge ?? DEFAULTS.twoFactorChallenge, payload, config));
219
+ }
220
+ async function forgotPassword(payload, config) {
221
+ return toResult(await statefulPost(config?.endpoints?.forgotPassword ?? DEFAULTS.forgotPassword, payload, config));
222
+ }
223
+ async function resetPassword(payload, config) {
224
+ return toResult(await statefulPost(config?.endpoints?.resetPassword ?? DEFAULTS.resetPassword, payload, config));
225
+ }
226
+ async function confirmPassword(payload, config) {
227
+ return toResult(await statefulPost(config?.endpoints?.confirmPassword ?? DEFAULTS.confirmPassword, payload, config));
228
+ }
229
+
230
+ exports.confirmPassword = confirmPassword;
231
+ exports.forgotPassword = forgotPassword;
232
+ exports.login = login;
233
+ exports.logout = logout;
234
+ exports.register = register;
235
+ exports.resetPassword = resetPassword;
236
+ exports.twoFactorChallenge = twoFactorChallenge;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Public & internal type surface of next-sanctum.
3
+ * TypeScript-first, generic User model, no public `any`.
4
+ */
5
+
6
+ interface LoginCredentials {
7
+ email?: string;
8
+ /** Supports backends that use `username` (config/fortify.php). */
9
+ username?: string;
10
+ password: string;
11
+ remember?: boolean;
12
+ [key: string]: unknown;
13
+ }
14
+ interface TwoFactorChallengePayload {
15
+ code?: string;
16
+ recovery_code?: string;
17
+ }
18
+ interface RegisterPayload {
19
+ name?: string;
20
+ email?: string;
21
+ username?: string;
22
+ password?: string;
23
+ password_confirmation?: string;
24
+ [key: string]: unknown;
25
+ }
26
+ interface ForgotPasswordPayload {
27
+ email: string;
28
+ [key: string]: unknown;
29
+ }
30
+ interface ResetPasswordPayload {
31
+ token: string;
32
+ email: string;
33
+ password: string;
34
+ password_confirmation: string;
35
+ [key: string]: unknown;
36
+ }
37
+ interface ConfirmPasswordPayload {
38
+ password: string;
39
+ }
40
+
41
+ /**
42
+ * Server-action helpers. WRAPPED by the consumer's own Server Action (`'use server'`)
43
+ * — not literally `'use server'` (see §6 of the plan). Runs the CSRF→POST flow and
44
+ * writes Laravel's Set-Cookie into `cookies()` so the session is persisted.
45
+ */
46
+ interface ActionConfig {
47
+ baseUrl?: string;
48
+ endpoints?: {
49
+ csrf?: string;
50
+ login?: string;
51
+ logout?: string;
52
+ register?: string;
53
+ forgotPassword?: string;
54
+ resetPassword?: string;
55
+ confirmPassword?: string;
56
+ twoFactorChallenge?: string;
57
+ };
58
+ csrf?: {
59
+ cookie?: string;
60
+ header?: string;
61
+ };
62
+ }
63
+ interface ActionResult {
64
+ ok: boolean;
65
+ status: number;
66
+ /** True when login requires a 2FA challenge (Fortify `two_factor`). */
67
+ twoFactor?: boolean;
68
+ /** Laravel validation errors (422). */
69
+ errors?: Record<string, string[]>;
70
+ }
71
+ /** Login (CSRF → POST /login). Check `twoFactor` on the result before assuming success. */
72
+ declare function login(credentials: LoginCredentials, config?: ActionConfig): Promise<ActionResult>;
73
+ declare function logout(config?: ActionConfig): Promise<ActionResult>;
74
+ declare function register(payload: RegisterPayload, config?: ActionConfig): Promise<ActionResult>;
75
+ declare function twoFactorChallenge(payload: TwoFactorChallengePayload, config?: ActionConfig): Promise<ActionResult>;
76
+ declare function forgotPassword(payload: ForgotPasswordPayload, config?: ActionConfig): Promise<ActionResult>;
77
+ declare function resetPassword(payload: ResetPasswordPayload, config?: ActionConfig): Promise<ActionResult>;
78
+ declare function confirmPassword(payload: ConfirmPasswordPayload, config?: ActionConfig): Promise<ActionResult>;
79
+
80
+ export { confirmPassword, forgotPassword, login, logout, register, resetPassword, twoFactorChallenge };
81
+ export type { ActionConfig, ActionResult };
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Public & internal type surface of next-sanctum.
3
+ * TypeScript-first, generic User model, no public `any`.
4
+ */
5
+
6
+ interface LoginCredentials {
7
+ email?: string;
8
+ /** Supports backends that use `username` (config/fortify.php). */
9
+ username?: string;
10
+ password: string;
11
+ remember?: boolean;
12
+ [key: string]: unknown;
13
+ }
14
+ interface TwoFactorChallengePayload {
15
+ code?: string;
16
+ recovery_code?: string;
17
+ }
18
+ interface RegisterPayload {
19
+ name?: string;
20
+ email?: string;
21
+ username?: string;
22
+ password?: string;
23
+ password_confirmation?: string;
24
+ [key: string]: unknown;
25
+ }
26
+ interface ForgotPasswordPayload {
27
+ email: string;
28
+ [key: string]: unknown;
29
+ }
30
+ interface ResetPasswordPayload {
31
+ token: string;
32
+ email: string;
33
+ password: string;
34
+ password_confirmation: string;
35
+ [key: string]: unknown;
36
+ }
37
+ interface ConfirmPasswordPayload {
38
+ password: string;
39
+ }
40
+
41
+ /**
42
+ * Server-action helpers. WRAPPED by the consumer's own Server Action (`'use server'`)
43
+ * — not literally `'use server'` (see §6 of the plan). Runs the CSRF→POST flow and
44
+ * writes Laravel's Set-Cookie into `cookies()` so the session is persisted.
45
+ */
46
+ interface ActionConfig {
47
+ baseUrl?: string;
48
+ endpoints?: {
49
+ csrf?: string;
50
+ login?: string;
51
+ logout?: string;
52
+ register?: string;
53
+ forgotPassword?: string;
54
+ resetPassword?: string;
55
+ confirmPassword?: string;
56
+ twoFactorChallenge?: string;
57
+ };
58
+ csrf?: {
59
+ cookie?: string;
60
+ header?: string;
61
+ };
62
+ }
63
+ interface ActionResult {
64
+ ok: boolean;
65
+ status: number;
66
+ /** True when login requires a 2FA challenge (Fortify `two_factor`). */
67
+ twoFactor?: boolean;
68
+ /** Laravel validation errors (422). */
69
+ errors?: Record<string, string[]>;
70
+ }
71
+ /** Login (CSRF → POST /login). Check `twoFactor` on the result before assuming success. */
72
+ declare function login(credentials: LoginCredentials, config?: ActionConfig): Promise<ActionResult>;
73
+ declare function logout(config?: ActionConfig): Promise<ActionResult>;
74
+ declare function register(payload: RegisterPayload, config?: ActionConfig): Promise<ActionResult>;
75
+ declare function twoFactorChallenge(payload: TwoFactorChallengePayload, config?: ActionConfig): Promise<ActionResult>;
76
+ declare function forgotPassword(payload: ForgotPasswordPayload, config?: ActionConfig): Promise<ActionResult>;
77
+ declare function resetPassword(payload: ResetPasswordPayload, config?: ActionConfig): Promise<ActionResult>;
78
+ declare function confirmPassword(payload: ConfirmPasswordPayload, config?: ActionConfig): Promise<ActionResult>;
79
+
80
+ export { confirmPassword, forgotPassword, login, logout, register, resetPassword, twoFactorChallenge };
81
+ export type { ActionConfig, ActionResult };
@@ -0,0 +1,228 @@
1
+ import 'server-only';
2
+ import { cookies } from 'next/headers';
3
+
4
+ /**
5
+ * Error types for next-sanctum. All failures are normalized to `SanctumError`
6
+ * so consumers can handle them consistently (see plan §10: errors must not leak).
7
+ */ /** Base error for all module failures. */ class SanctumError extends Error {
8
+ constructor(message, options){
9
+ super(message, {
10
+ cause: options.cause
11
+ });
12
+ this.name = "SanctumError";
13
+ this.kind = options.kind;
14
+ this.status = options.status;
15
+ this.data = options.data;
16
+ }
17
+ }
18
+ /** Invalid configuration — fail-fast on init (see resolveConfig). */ class ConfigError extends SanctumError {
19
+ constructor(message, cause){
20
+ super(message, {
21
+ kind: "config",
22
+ cause
23
+ });
24
+ this.name = "ConfigError";
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Resolve the server-side base URL (`SANCTUM_BASE_URL`, falling back to the public var
30
+ * for dev). Shared by `server.ts`/`actions.ts`. Fails fast with a clear ConfigError.
31
+ */ function resolveServerBaseUrl(explicit, label = "server helpers") {
32
+ const baseUrl = explicit ?? process.env.SANCTUM_BASE_URL ?? process.env.NEXT_PUBLIC_SANCTUM_BASE_URL;
33
+ if (!baseUrl) {
34
+ throw new ConfigError(`SANCTUM_BASE_URL (server) is not set for next-sanctum ${label}.`);
35
+ }
36
+ return baseUrl.replace(/\/+$/, "");
37
+ }
38
+
39
+ /**
40
+ * Shared Set-Cookie parsing helpers (pure — no `next`/`server-only` imports, so this
41
+ * stays isomorphic and tree-shakes out of the client graph). Used by `server.ts` and
42
+ * `actions.ts` to mirror Laravel's Set-Cookie into Next's writable cookie store.
43
+ */ /**
44
+ * Read all Set-Cookie headers as an array. Prefers `Headers.getSetCookie()` (Node ≥18.14,
45
+ * all supported Next runtimes). The single-header fallback only handles one cookie — we do
46
+ * NOT attempt to comma-split, which is unreliable (Expires dates contain commas).
47
+ */ function getSetCookies(headers) {
48
+ const anyHeaders = headers;
49
+ if (typeof anyHeaders.getSetCookie === "function") return anyHeaders.getSetCookie();
50
+ const value = headers.get("set-cookie");
51
+ return value ? [
52
+ value
53
+ ] : [];
54
+ }
55
+ /** Parse a single Set-Cookie header. Validates attributes; returns null when unusable. */ function parseSetCookie(header) {
56
+ const segments = header.split(";");
57
+ const first = segments.shift();
58
+ if (!first) return null;
59
+ const eq = first.indexOf("=");
60
+ if (eq === -1) return null;
61
+ const name = first.slice(0, eq).trim();
62
+ if (name === "") return null;
63
+ let value = first.slice(eq + 1).trim();
64
+ if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
65
+ value = value.slice(1, -1);
66
+ }
67
+ const options = {};
68
+ for (const segment of segments){
69
+ const idx = segment.indexOf("=");
70
+ const key = (idx === -1 ? segment : segment.slice(0, idx)).trim().toLowerCase();
71
+ const val = idx === -1 ? "" : segment.slice(idx + 1).trim();
72
+ switch(key){
73
+ case "path":
74
+ options.path = val;
75
+ break;
76
+ case "domain":
77
+ options.domain = val;
78
+ break;
79
+ case "max-age":
80
+ {
81
+ const n = Number(val);
82
+ if (val !== "" && Number.isFinite(n)) options.maxAge = n;
83
+ break;
84
+ }
85
+ case "expires":
86
+ {
87
+ const d = new Date(val);
88
+ if (!Number.isNaN(d.getTime())) options.expires = d;
89
+ break;
90
+ }
91
+ case "samesite":
92
+ {
93
+ const lower = val.toLowerCase();
94
+ if (lower === "lax" || lower === "strict" || lower === "none") {
95
+ options.sameSite = lower;
96
+ }
97
+ break;
98
+ }
99
+ case "secure":
100
+ options.secure = true;
101
+ break;
102
+ case "httponly":
103
+ options.httpOnly = true;
104
+ break;
105
+ }
106
+ }
107
+ return {
108
+ name,
109
+ value,
110
+ options
111
+ };
112
+ }
113
+ /** Mirror a response's Set-Cookie headers into a writable cookie store. */ function applySetCookies(store, response) {
114
+ for (const raw of getSetCookies(response.headers)){
115
+ const parsed = parseSetCookie(raw);
116
+ if (!parsed) continue;
117
+ try {
118
+ store.set(parsed.name, parsed.value, parsed.options);
119
+ } catch {
120
+ // store is read-only (e.g. called from a Server Component) — ignore.
121
+ }
122
+ }
123
+ }
124
+
125
+ const DEFAULTS = {
126
+ csrf: "/sanctum/csrf-cookie",
127
+ login: "/login",
128
+ logout: "/logout",
129
+ register: "/register",
130
+ forgotPassword: "/forgot-password",
131
+ resetPassword: "/reset-password",
132
+ confirmPassword: "/user/confirm-password",
133
+ twoFactorChallenge: "/two-factor-challenge"
134
+ };
135
+ async function readErrors(response) {
136
+ try {
137
+ const data = await response.json();
138
+ return data?.errors;
139
+ } catch {
140
+ return undefined;
141
+ }
142
+ }
143
+ async function statefulPost(path, json, config) {
144
+ const store = await cookies();
145
+ const base = resolveServerBaseUrl(config?.baseUrl, "actions");
146
+ const csrfCookie = config?.csrf?.cookie ?? "XSRF-TOKEN";
147
+ const csrfHeader = config?.csrf?.header ?? "X-XSRF-TOKEN";
148
+ const csrfPath = config?.endpoints?.csrf ?? DEFAULTS.csrf;
149
+ let token = store.get(csrfCookie)?.value;
150
+ if (!token) {
151
+ const csrfResponse = await fetch(`${base}${csrfPath}`, {
152
+ headers: {
153
+ cookie: store.toString(),
154
+ accept: "application/json"
155
+ },
156
+ cache: "no-store"
157
+ });
158
+ applySetCookies(store, csrfResponse);
159
+ token = store.get(csrfCookie)?.value;
160
+ }
161
+ const headers = {
162
+ accept: "application/json",
163
+ "content-type": "application/json",
164
+ cookie: store.toString()
165
+ };
166
+ if (token) headers[csrfHeader] = decodeURIComponent(token);
167
+ const response = await fetch(`${base}${path}`, {
168
+ method: "POST",
169
+ headers,
170
+ body: JSON.stringify(json ?? {}),
171
+ cache: "no-store"
172
+ });
173
+ applySetCookies(store, response);
174
+ return {
175
+ ok: response.ok,
176
+ status: response.status,
177
+ raw: response
178
+ };
179
+ }
180
+ async function toResult(result) {
181
+ if (!result.ok) {
182
+ return {
183
+ ok: false,
184
+ status: result.status,
185
+ errors: result.status === 422 ? await readErrors(result.raw) : undefined
186
+ };
187
+ }
188
+ return {
189
+ ok: true,
190
+ status: result.status
191
+ };
192
+ }
193
+ /** Login (CSRF → POST /login). Check `twoFactor` on the result before assuming success. */ async function login(credentials, config) {
194
+ const result = await statefulPost(config?.endpoints?.login ?? DEFAULTS.login, credentials, config);
195
+ if (!result.ok) return toResult(result);
196
+ let twoFactor = false;
197
+ try {
198
+ const data = await result.raw.json();
199
+ twoFactor = Boolean(data?.two_factor);
200
+ } catch {
201
+ // empty body → not 2FA
202
+ }
203
+ return {
204
+ ok: true,
205
+ status: result.status,
206
+ twoFactor
207
+ };
208
+ }
209
+ async function logout(config) {
210
+ return toResult(await statefulPost(config?.endpoints?.logout ?? DEFAULTS.logout, {}, config));
211
+ }
212
+ async function register(payload, config) {
213
+ return toResult(await statefulPost(config?.endpoints?.register ?? DEFAULTS.register, payload, config));
214
+ }
215
+ async function twoFactorChallenge(payload, config) {
216
+ return toResult(await statefulPost(config?.endpoints?.twoFactorChallenge ?? DEFAULTS.twoFactorChallenge, payload, config));
217
+ }
218
+ async function forgotPassword(payload, config) {
219
+ return toResult(await statefulPost(config?.endpoints?.forgotPassword ?? DEFAULTS.forgotPassword, payload, config));
220
+ }
221
+ async function resetPassword(payload, config) {
222
+ return toResult(await statefulPost(config?.endpoints?.resetPassword ?? DEFAULTS.resetPassword, payload, config));
223
+ }
224
+ async function confirmPassword(payload, config) {
225
+ return toResult(await statefulPost(config?.endpoints?.confirmPassword ?? DEFAULTS.confirmPassword, payload, config));
226
+ }
227
+
228
+ export { confirmPassword, forgotPassword, login, logout, register, resetPassword, twoFactorChallenge };