@draftlab/auth 0.15.0 → 0.15.1
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/dist/adapters/node.d.mts +0 -1
- package/dist/client.d.mts +293 -287
- package/dist/client.mjs +1 -0
- package/dist/core.d.mts +22 -23
- package/dist/core.mjs +3 -3
- package/dist/error.d.mts +53 -53
- package/dist/keys.d.mts +0 -1
- package/dist/mutex.d.mts +14 -14
- package/dist/provider/apple.d.mts +34 -35
- package/dist/provider/code.d.mts +75 -85
- package/dist/provider/discord.d.mts +49 -50
- package/dist/provider/facebook.d.mts +49 -50
- package/dist/provider/github.d.mts +50 -51
- package/dist/provider/gitlab.d.mts +34 -35
- package/dist/provider/google.d.mts +49 -50
- package/dist/provider/linkedin.d.mts +47 -48
- package/dist/provider/magiclink.d.mts +28 -38
- package/dist/provider/microsoft.d.mts +67 -68
- package/dist/provider/oauth2.d.mts +75 -76
- package/dist/provider/passkey.d.mts +20 -21
- package/dist/provider/password.d.mts +174 -202
- package/dist/provider/provider.d.mts +105 -107
- package/dist/provider/reddit.d.mts +33 -34
- package/dist/provider/slack.d.mts +34 -35
- package/dist/provider/spotify.d.mts +34 -35
- package/dist/provider/totp.d.mts +43 -44
- package/dist/provider/twitch.d.mts +33 -34
- package/dist/provider/vercel.d.mts +65 -66
- package/dist/revocation.d.mts +29 -30
- package/dist/storage/memory.d.mts +11 -12
- package/dist/storage/storage.d.mts +110 -110
- package/dist/storage/turso.d.mts +0 -1
- package/dist/storage/unstorage.d.mts +0 -1
- package/dist/subject.d.mts +0 -1
- package/dist/themes/theme.d.mts +101 -101
- package/dist/toolkit/client.d.mts +56 -57
- package/dist/toolkit/providers/facebook.d.mts +0 -1
- package/dist/toolkit/providers/github.d.mts +0 -1
- package/dist/toolkit/providers/google.d.mts +0 -1
- package/dist/toolkit/storage.d.mts +8 -8
- package/dist/ui/base.d.mts +0 -1
- package/dist/ui/code.d.mts +5 -6
- package/dist/ui/form.d.mts +6 -7
- package/dist/ui/icon.d.mts +0 -1
- package/dist/ui/magiclink.d.mts +5 -6
- package/dist/ui/passkey.d.mts +0 -1
- package/dist/ui/password.d.mts +2 -3
- package/dist/ui/select.d.mts +0 -1
- package/dist/ui/totp.d.mts +0 -1
- package/dist/util.d.mts +0 -1
- package/package.json +3 -3
|
@@ -2,7 +2,6 @@ import { Provider } from "./provider.mjs";
|
|
|
2
2
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
3
3
|
|
|
4
4
|
//#region src/provider/password.d.ts
|
|
5
|
-
|
|
6
5
|
/**
|
|
7
6
|
* Password-based authentication provider for Draft Auth.
|
|
8
7
|
* Supports user registration, login, and password changes with email verification.
|
|
@@ -69,19 +68,19 @@ import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
|
69
68
|
*/
|
|
70
69
|
interface PasswordHasher<T> {
|
|
71
70
|
/**
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
* Hashes a plaintext password for secure storage.
|
|
72
|
+
*
|
|
73
|
+
* @param password - The plaintext password to hash
|
|
74
|
+
* @returns Promise resolving to the hash data structure
|
|
75
|
+
*/
|
|
77
76
|
hash(password: string): Promise<T>;
|
|
78
77
|
/**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
78
|
+
* Verifies a plaintext password against a stored hash.
|
|
79
|
+
*
|
|
80
|
+
* @param password - The plaintext password to verify
|
|
81
|
+
* @param compare - The stored hash data to compare against
|
|
82
|
+
* @returns Promise resolving to true if password matches
|
|
83
|
+
*/
|
|
85
84
|
verify(password: string, compare: T): Promise<boolean>;
|
|
86
85
|
}
|
|
87
86
|
/**
|
|
@@ -89,158 +88,158 @@ interface PasswordHasher<T> {
|
|
|
89
88
|
*/
|
|
90
89
|
interface PasswordConfig {
|
|
91
90
|
/**
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
* Length of verification codes sent to users.
|
|
92
|
+
* @internal
|
|
93
|
+
* @default 6
|
|
94
|
+
*/
|
|
96
95
|
readonly length?: number;
|
|
97
96
|
/**
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
* Password hashing implementation to use.
|
|
98
|
+
* @internal
|
|
99
|
+
* @default ScryptHasher()
|
|
100
|
+
*/
|
|
102
101
|
readonly hasher?: PasswordHasher<unknown>;
|
|
103
102
|
/**
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
103
|
+
* Request handler for rendering the login screen.
|
|
104
|
+
* Receives the request, optional form data, and any login errors.
|
|
105
|
+
*
|
|
106
|
+
* @param req - The HTTP request object
|
|
107
|
+
* @param form - Form data from POST requests (if any)
|
|
108
|
+
* @param error - Login error to display (if any)
|
|
109
|
+
* @returns Promise resolving to the login page response
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* login: async (req, form, error) => {
|
|
114
|
+
* const html = renderLoginPage({
|
|
115
|
+
* email: form?.get('email'),
|
|
116
|
+
* error: error?.type
|
|
117
|
+
* })
|
|
118
|
+
* return new Response(html, {
|
|
119
|
+
* headers: { 'Content-Type': 'text/html' }
|
|
120
|
+
* })
|
|
121
|
+
* }
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
125
124
|
login: (req: Request, form?: FormData, error?: PasswordLoginError) => Promise<Response>;
|
|
126
125
|
/**
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
126
|
+
* Request handler for rendering the registration screen.
|
|
127
|
+
* Handles both initial registration form and email verification.
|
|
128
|
+
*
|
|
129
|
+
* @param req - The HTTP request object
|
|
130
|
+
* @param state - Current registration state (start or code verification)
|
|
131
|
+
* @param form - Form data from POST requests (if any)
|
|
132
|
+
* @param error - Registration error to display (if any)
|
|
133
|
+
* @returns Promise resolving to the registration page response
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```ts
|
|
137
|
+
* register: async (req, state, form, error) => {
|
|
138
|
+
* if (state.type === 'start') {
|
|
139
|
+
* return new Response(renderRegistrationForm(error))
|
|
140
|
+
* } else {
|
|
141
|
+
* return new Response(renderCodeVerification(state.email, error))
|
|
142
|
+
* }
|
|
143
|
+
* }
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
147
146
|
register: (req: Request, state: PasswordRegisterState, form?: FormData, error?: PasswordRegisterError) => Promise<Response>;
|
|
148
147
|
/**
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
148
|
+
* Request handler for rendering the password change screen.
|
|
149
|
+
* Handles email entry, code verification, and password update steps.
|
|
150
|
+
*
|
|
151
|
+
* @param req - The HTTP request object
|
|
152
|
+
* @param state - Current password change state
|
|
153
|
+
* @param form - Form data from POST requests (if any)
|
|
154
|
+
* @param error - Password change error to display (if any)
|
|
155
|
+
* @returns Promise resolving to the password change page response
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```ts
|
|
159
|
+
* change: async (req, state, form, error) => {
|
|
160
|
+
* switch (state.type) {
|
|
161
|
+
* case 'start':
|
|
162
|
+
* return new Response(renderEmailForm(error))
|
|
163
|
+
* case 'code':
|
|
164
|
+
* return new Response(renderCodeForm(state.email, error))
|
|
165
|
+
* case 'update':
|
|
166
|
+
* return new Response(renderPasswordForm(error))
|
|
167
|
+
* }
|
|
168
|
+
* }
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
172
171
|
change: (req: Request, state: PasswordChangeState, form?: FormData, error?: PasswordChangeError) => Promise<Response>;
|
|
173
172
|
/**
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
173
|
+
* Callback for sending verification codes to users via email.
|
|
174
|
+
* Implement this to integrate with your email service provider.
|
|
175
|
+
*
|
|
176
|
+
* The context parameter indicates why the code is being sent:
|
|
177
|
+
* - "register": User is registering for the first time
|
|
178
|
+
* - "register:resend": User requested to resend registration code
|
|
179
|
+
* - "reset": User is resetting their password
|
|
180
|
+
* - "reset:resend": User requested to resend password reset code
|
|
181
|
+
*
|
|
182
|
+
* @param email - The recipient's email address
|
|
183
|
+
* @param code - The verification code to send
|
|
184
|
+
* @param context - The context of why the code is being sent
|
|
185
|
+
* @returns Promise that resolves when email is sent
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```ts
|
|
189
|
+
* sendCode: async (email, code, context) => {
|
|
190
|
+
* const templates = {
|
|
191
|
+
* "register": {
|
|
192
|
+
* subject: "Welcome! Verify your email",
|
|
193
|
+
* body: `Welcome! Your verification code is: ${code}`
|
|
194
|
+
* },
|
|
195
|
+
* "register:resend": {
|
|
196
|
+
* subject: "Your verification code (resent)",
|
|
197
|
+
* body: `Here's your code again: ${code}`
|
|
198
|
+
* },
|
|
199
|
+
* "reset": {
|
|
200
|
+
* subject: "Reset your password",
|
|
201
|
+
* body: `Your password reset code is: ${code}`
|
|
202
|
+
* },
|
|
203
|
+
* "reset:resend": {
|
|
204
|
+
* subject: "Password reset code (resent)",
|
|
205
|
+
* body: `Here's your reset code again: ${code}`
|
|
206
|
+
* }
|
|
207
|
+
* }
|
|
208
|
+
*
|
|
209
|
+
* const template = templates[context]
|
|
210
|
+
* await emailService.send({
|
|
211
|
+
* to: email,
|
|
212
|
+
* subject: template.subject,
|
|
213
|
+
* text: template.body
|
|
214
|
+
* })
|
|
215
|
+
* }
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
219
218
|
sendCode: (email: string, code: string, context: "register" | "register:resend" | "reset" | "reset:resend") => Promise<void>;
|
|
220
219
|
/**
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
220
|
+
* Optional password validation function or schema.
|
|
221
|
+
* Can be either a validation function or a standard-schema validator.
|
|
222
|
+
*
|
|
223
|
+
* @param password - The password to validate
|
|
224
|
+
* @returns Error message if invalid, undefined if valid
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```ts
|
|
228
|
+
* // Function-based validation
|
|
229
|
+
* validatePassword: (password) => {
|
|
230
|
+
* if (password.length < 8) return "Password must be at least 8 characters"
|
|
231
|
+
* if (!/[A-Z]/.test(password)) return "Password must contain uppercase letter"
|
|
232
|
+
* return undefined
|
|
233
|
+
* }
|
|
234
|
+
*
|
|
235
|
+
* // Schema-based validation
|
|
236
|
+
* validatePassword: pipe(
|
|
237
|
+
* string(),
|
|
238
|
+
* minLength(8, "Password must be at least 8 characters"),
|
|
239
|
+
* regex(/[A-Z]/, "Password must contain uppercase letter")
|
|
240
|
+
* )
|
|
241
|
+
* ```
|
|
242
|
+
*/
|
|
244
243
|
readonly validatePassword?: StandardSchemaV1 | ((password: string) => Promise<string | undefined> | string | undefined);
|
|
245
244
|
}
|
|
246
245
|
/**
|
|
@@ -248,95 +247,68 @@ interface PasswordConfig {
|
|
|
248
247
|
* The registration process moves through these states sequentially.
|
|
249
248
|
*/
|
|
250
249
|
type PasswordRegisterState = {
|
|
251
|
-
/** Initial state: user enters email and password */
|
|
252
|
-
readonly type: "start";
|
|
250
|
+
/** Initial state: user enters email and password */readonly type: "start";
|
|
253
251
|
} | {
|
|
254
|
-
/** Code verification state: user enters emailed verification code */
|
|
255
|
-
readonly
|
|
256
|
-
/** The
|
|
257
|
-
readonly code: string;
|
|
258
|
-
/** The user's email address */
|
|
259
|
-
readonly email: string;
|
|
260
|
-
/** The hashed password (ready for storage) */
|
|
252
|
+
/** Code verification state: user enters emailed verification code */readonly type: "code"; /** The verification code sent to the user */
|
|
253
|
+
readonly code: string; /** The user's email address */
|
|
254
|
+
readonly email: string; /** The hashed password (ready for storage) */
|
|
261
255
|
readonly password: unknown;
|
|
262
256
|
};
|
|
263
257
|
/**
|
|
264
258
|
* Possible errors during user registration.
|
|
265
259
|
*/
|
|
266
260
|
type PasswordRegisterError = {
|
|
267
|
-
/** The verification code entered is incorrect */
|
|
268
|
-
readonly type: "invalid_code";
|
|
261
|
+
/** The verification code entered is incorrect */readonly type: "invalid_code";
|
|
269
262
|
} | {
|
|
270
|
-
/** The email address is already registered */
|
|
271
|
-
readonly type: "email_taken";
|
|
263
|
+
/** The email address is already registered */readonly type: "email_taken";
|
|
272
264
|
} | {
|
|
273
|
-
/** The email address format is invalid */
|
|
274
|
-
readonly type: "invalid_email";
|
|
265
|
+
/** The email address format is invalid */readonly type: "invalid_email";
|
|
275
266
|
} | {
|
|
276
|
-
/** The password does not meet requirements */
|
|
277
|
-
readonly type: "invalid_password";
|
|
267
|
+
/** The password does not meet requirements */readonly type: "invalid_password";
|
|
278
268
|
} | {
|
|
279
|
-
/** Password and confirmation password don't match */
|
|
280
|
-
readonly type: "password_mismatch";
|
|
269
|
+
/** Password and confirmation password don't match */readonly type: "password_mismatch";
|
|
281
270
|
} | {
|
|
282
|
-
/** Custom validation error from validatePassword callback */
|
|
283
|
-
readonly type: "validation_error";
|
|
271
|
+
/** Custom validation error from validatePassword callback */readonly type: "validation_error";
|
|
284
272
|
readonly message?: string;
|
|
285
273
|
};
|
|
286
274
|
/**
|
|
287
275
|
* Password change flow states that determine which UI to show.
|
|
288
276
|
*/
|
|
289
277
|
type PasswordChangeState = {
|
|
290
|
-
/** Initial state: user enters their email address */
|
|
291
|
-
readonly type: "start";
|
|
292
|
-
/** URL to redirect to after successful password change */
|
|
278
|
+
/** Initial state: user enters their email address */readonly type: "start"; /** URL to redirect to after successful password change */
|
|
293
279
|
readonly redirect: string;
|
|
294
280
|
} | {
|
|
295
|
-
/** Code verification state: user enters emailed verification code */
|
|
296
|
-
readonly
|
|
297
|
-
|
|
298
|
-
readonly code: string;
|
|
299
|
-
/** The user's email address */
|
|
300
|
-
readonly email: string;
|
|
301
|
-
/** URL to redirect to after completion */
|
|
281
|
+
/** Code verification state: user enters emailed verification code */readonly type: "code"; /** The verification code sent to the user */
|
|
282
|
+
readonly code: string; /** The user's email address */
|
|
283
|
+
readonly email: string; /** URL to redirect to after completion */
|
|
302
284
|
readonly redirect: string;
|
|
303
285
|
} | {
|
|
304
|
-
/** Password update state: user enters new password */
|
|
305
|
-
readonly
|
|
306
|
-
/** URL to redirect to after completion */
|
|
307
|
-
readonly redirect: string;
|
|
308
|
-
/** The verified email address */
|
|
286
|
+
/** Password update state: user enters new password */readonly type: "update"; /** URL to redirect to after completion */
|
|
287
|
+
readonly redirect: string; /** The verified email address */
|
|
309
288
|
readonly email: string;
|
|
310
289
|
};
|
|
311
290
|
/**
|
|
312
291
|
* Possible errors during password changes.
|
|
313
292
|
*/
|
|
314
293
|
type PasswordChangeError = {
|
|
315
|
-
/** The email address format is invalid */
|
|
316
|
-
readonly type: "invalid_email";
|
|
294
|
+
/** The email address format is invalid */readonly type: "invalid_email";
|
|
317
295
|
} | {
|
|
318
|
-
/** The verification code entered is incorrect */
|
|
319
|
-
readonly type: "invalid_code";
|
|
296
|
+
/** The verification code entered is incorrect */readonly type: "invalid_code";
|
|
320
297
|
} | {
|
|
321
|
-
/** The new password does not meet requirements */
|
|
322
|
-
readonly type: "invalid_password";
|
|
298
|
+
/** The new password does not meet requirements */readonly type: "invalid_password";
|
|
323
299
|
} | {
|
|
324
|
-
/** New password and confirmation don't match */
|
|
325
|
-
readonly type: "password_mismatch";
|
|
300
|
+
/** New password and confirmation don't match */readonly type: "password_mismatch";
|
|
326
301
|
} | {
|
|
327
|
-
/** Custom validation error from validatePassword callback */
|
|
328
|
-
readonly type: "validation_error";
|
|
302
|
+
/** Custom validation error from validatePassword callback */readonly type: "validation_error";
|
|
329
303
|
readonly message: string;
|
|
330
304
|
};
|
|
331
305
|
/**
|
|
332
306
|
* Possible errors during login attempts.
|
|
333
307
|
*/
|
|
334
308
|
type PasswordLoginError = {
|
|
335
|
-
/** The email address format is invalid */
|
|
336
|
-
readonly type: "invalid_email";
|
|
309
|
+
/** The email address format is invalid */readonly type: "invalid_email";
|
|
337
310
|
} | {
|
|
338
|
-
/** The password is incorrect or email not found */
|
|
339
|
-
readonly type: "invalid_password";
|
|
311
|
+
/** The password is incorrect or email not found */readonly type: "invalid_password";
|
|
340
312
|
};
|
|
341
313
|
/**
|
|
342
314
|
* User data returned by successful password authentication.
|