@lenne.tech/nuxt-extensions 1.15.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +90 -3
- package/README.md +49 -2
- package/dist/module.d.mts +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/composables/auth/use-lt-auth.js +15 -3
- package/dist/runtime/lib/auth-client.d.ts +164 -41
- package/dist/runtime/lib/auth-client.js +48 -15
- package/dist/runtime/plugins/auth-interceptor.client.js +0 -1
- package/dist/runtime/types/auth.d.ts +93 -6
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -25,8 +25,8 @@ src/
|
|
|
25
25
|
|
|
26
26
|
| Composable | Purpose |
|
|
27
27
|
|-----------|---------|
|
|
28
|
-
| `useLtAuth()` | Authentication state, login, logout, session management |
|
|
29
|
-
| `useLtAuthClient()` | Raw Better Auth client access |
|
|
28
|
+
| `useLtAuth()` | Authentication state, login, logout, session management, password change + reset |
|
|
29
|
+
| `useLtAuthClient()` | Raw Better Auth client access (`twoFactor.*`, `admin.*`, `passkey`) |
|
|
30
30
|
| `useSystemSetup()` | First-admin setup flow |
|
|
31
31
|
| `useLtTusUpload()` | TUS resumable file uploads |
|
|
32
32
|
| `useLtFile()` | File utilities (size formatting, URLs) |
|
|
@@ -179,6 +179,90 @@ export default defineNuxtRouteMiddleware((to) => {
|
|
|
179
179
|
});
|
|
180
180
|
```
|
|
181
181
|
|
|
182
|
+
## Password Handling: Reset, Change, and Client-Side Hashing
|
|
183
|
+
|
|
184
|
+
**Every password leaves the browser SHA256-hashed.** `auth-client.ts` wraps each Better-Auth
|
|
185
|
+
method that carries a credential and substitutes that one field with its digest;
|
|
186
|
+
`@lenne.tech/nest-server` hashes the received value again on its side. The two ends agree only
|
|
187
|
+
because both do this — which is why a hand-rolled call is not a style choice but a way to lock
|
|
188
|
+
a user out of their own account.
|
|
189
|
+
|
|
190
|
+
### Use the composable. Never hand-roll the flow.
|
|
191
|
+
|
|
192
|
+
`useLtAuth()` exposes the whole set — `changePassword`, `requestPasswordReset`, `resetPassword`
|
|
193
|
+
(since 1.16.0) — alongside `signIn` / `signUp`. Reach for `useLtAuthClient()` only for surfaces
|
|
194
|
+
the composable does not carry (`twoFactor.*`, `admin.*`, `passkey`); that client hashes too.
|
|
195
|
+
|
|
196
|
+
What went wrong when a project did otherwise: `useLtAuth()` offered `changePassword` but neither
|
|
197
|
+
reset method, so a reset page was built directly against `$fetch`, the client-side hashing was
|
|
198
|
+
forgotten, and the backend stored `scrypt(plaintext)` while sign-in verified
|
|
199
|
+
`scrypt(sha256(plaintext))`. The password the user had just chosen did not work, and nothing in
|
|
200
|
+
either log said why. The gap in this composable is what created that workaround.
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
const { requestPasswordReset, resetPassword } = useLtAuth();
|
|
204
|
+
|
|
205
|
+
// Step 1 — send the mail. `redirectTo` MUST be absolute (see below).
|
|
206
|
+
await requestPasswordReset({
|
|
207
|
+
email,
|
|
208
|
+
redirectTo: new URL('/auth/reset-password', config.public.siteUrl).toString(),
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Step 2 — on the reset page, with the token from the query string.
|
|
212
|
+
await resetPassword({ newPassword, token });
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### `redirectTo` must be absolute, and must never come from user input
|
|
216
|
+
|
|
217
|
+
Better Auth resolves it against the **API** origin (`new URL(callbackURL, ctx.baseURL)`). A
|
|
218
|
+
relative value therefore lands on the API host, where the route does not exist: 403
|
|
219
|
+
`INVALID_REDIRECT_URL`, no mail sent, nothing visible in the browser. It fails silently.
|
|
220
|
+
|
|
221
|
+
**It also carries a live single-use reset token.** An attacker-chosen value is account takeover,
|
|
222
|
+
not a phishing redirect — so never build it from `route.query`, a referrer, or a form field.
|
|
223
|
+
The only thing holding it back is the server's `trustedOrigins` allowlist; a project that
|
|
224
|
+
widens that (or sets a wildcard) removes the last barrier. In lt starter projects `appUrl()`
|
|
225
|
+
(from the starter's `app/utils/app-origin.ts`, not from this package) builds one and throws
|
|
226
|
+
rather than returning something relative; elsewhere use `new URL(path, siteUrl).toString()`.
|
|
227
|
+
|
|
228
|
+
### Password length is enforced by YOUR form — the server cannot do it
|
|
229
|
+
|
|
230
|
+
better-auth checks `minPasswordLength` against the value it **receives**
|
|
231
|
+
(`api/routes/password.mjs`). Because this library hashes first, that value is always a 64-character
|
|
232
|
+
hex string, so the check passes trivially on every path. nest-server 11.38.0 adds a raw-value
|
|
233
|
+
length check for plaintext clients, and it deliberately skips anything matching `^[a-f0-9]{64}$`
|
|
234
|
+
— i.e. skips us.
|
|
235
|
+
|
|
236
|
+
This is a consequence of client-side hashing, not a bug in either backend, and there is nothing to
|
|
237
|
+
"fix" server-side. **A minimum length must live in your form schema** (the starters use
|
|
238
|
+
`v.minLength(8)` on register, setup and reset). If someone later removes that rule reasoning that
|
|
239
|
+
"the server validates it", nothing will catch it.
|
|
240
|
+
|
|
241
|
+
### `admin.*` is passed through UNHASHED — an armed trap, not a live one
|
|
242
|
+
|
|
243
|
+
`admin.createUser` (`password`) and `admin.setUserPassword` (`newPassword`) are the only
|
|
244
|
+
credential-carrying methods this library does **not** hash.
|
|
245
|
+
|
|
246
|
+
Today that is harmless: `@lenne.tech/nest-server` does not register better-auth's `admin()`
|
|
247
|
+
plugin and has no option to enable it, so those routes 404. But `auth.enableAdmin` defaults to
|
|
248
|
+
`true`, meaning the admin *client* plugin is registered in every consuming project — the trap is
|
|
249
|
+
armed and waiting for a server that answers.
|
|
250
|
+
|
|
251
|
+
**Enabling `admin()` server-side is therefore a lock-step change across both repos in one
|
|
252
|
+
release:** the two routes join nest-server's password-normalisation table, and these two methods
|
|
253
|
+
get `ltSha256` wrappers here. Doing only the server half creates accounts whose password nobody
|
|
254
|
+
can ever log in with, with no error anywhere. Hashing here pre-emptively is equally wrong — it
|
|
255
|
+
would build a client expectation the server does not answer.
|
|
256
|
+
|
|
257
|
+
### If you add a wrapper that hashes something
|
|
258
|
+
|
|
259
|
+
Add it to both guards. `test/auth-client-hashing.test.ts` asserts the actual request payload;
|
|
260
|
+
`test/auth-client-param-forwarding.test.ts` keeps the source-level half. A hashing wrapper
|
|
261
|
+
replaces ONE field and forwards the rest — `{ ...params, password: hashed }`. Never
|
|
262
|
+
`{ password: hashed }` (drops `revokeOtherSessions`, `redirectTo`, `method`, `issuer` — all
|
|
263
|
+
shipped defects), and never `{ password: hashed, ...params }`, which looks almost identical and
|
|
264
|
+
puts the **plaintext** back on the wire.
|
|
265
|
+
|
|
182
266
|
## Form Label Repair
|
|
183
267
|
|
|
184
268
|
**The defect:** Nuxt UI's `FormField` derives the label's `for` and the control's `id` from one
|
|
@@ -257,7 +341,10 @@ fields at `app:beforeMount` and restores them at `app:mounted`, dispatching a sy
|
|
|
257
341
|
## Development Rules
|
|
258
342
|
|
|
259
343
|
1. **ALWAYS read source code** in `dist/runtime/` to understand available composables and components
|
|
260
|
-
2. **Use `useLtAuth()`** for authentication — never implement auth manually
|
|
344
|
+
2. **Use `useLtAuth()`** for authentication — never implement auth manually. This includes the
|
|
345
|
+
password-reset flow: passwords are SHA256-hashed client-side, and a hand-rolled call that skips
|
|
346
|
+
that step desynchronises the two credential stores and locks the user out (see "Password
|
|
347
|
+
Handling" below).
|
|
261
348
|
3. **Check existing composables** before creating new ones — this module may already provide what you need
|
|
262
349
|
4. **Peer dependencies** (`better-auth`, `@better-auth/passkey`, `tus-js-client`) must be installed in the consuming project.
|
|
263
350
|
`better-auth` and `@better-auth/passkey` are pinned to **`>=1.7.1 <1.8.0`** — deliberately not a caret.
|
package/README.md
CHANGED
|
@@ -363,6 +363,9 @@ const {
|
|
|
363
363
|
authenticateWithPasskey,
|
|
364
364
|
registerPasskey,
|
|
365
365
|
twoFactor,
|
|
366
|
+
changePassword,
|
|
367
|
+
requestPasswordReset,
|
|
368
|
+
resetPassword,
|
|
366
369
|
} = useLtAuth();
|
|
367
370
|
|
|
368
371
|
// Login with email/password
|
|
@@ -426,6 +429,50 @@ const { hasRole, hasAnyRole } = useLtAuth();
|
|
|
426
429
|
</template>
|
|
427
430
|
```
|
|
428
431
|
|
|
432
|
+
### Password Change and Reset
|
|
433
|
+
|
|
434
|
+
All three methods hash the password with SHA256 before it leaves the browser, because
|
|
435
|
+
`@lenne.tech/nest-server` verifies that shape. **Use them rather than calling the endpoints
|
|
436
|
+
yourself** — a hand-rolled reset that skips the hashing stores a credential the login can never
|
|
437
|
+
match, and the user is locked out of the account they just set a password for. That is not
|
|
438
|
+
hypothetical; it is why these two methods were added in 1.16.0.
|
|
439
|
+
|
|
440
|
+
```vue
|
|
441
|
+
<script setup lang="ts">
|
|
442
|
+
const { changePassword, requestPasswordReset, resetPassword } = useLtAuth();
|
|
443
|
+
const config = useRuntimeConfig();
|
|
444
|
+
|
|
445
|
+
// Signed-in user changes their own password.
|
|
446
|
+
// `revokeOtherSessions` ends every OTHER session — set it after a suspected compromise.
|
|
447
|
+
await changePassword({ currentPassword, newPassword, revokeOtherSessions: true });
|
|
448
|
+
|
|
449
|
+
// Step 1: send the reset mail.
|
|
450
|
+
await requestPasswordReset({
|
|
451
|
+
email,
|
|
452
|
+
redirectTo: new URL('/auth/reset-password', config.public.siteUrl).toString(),
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
// Step 2: on the reset page, with the token from the query string.
|
|
456
|
+
await resetPassword({ newPassword, token: route.query.token as string });
|
|
457
|
+
</script>
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
**`redirectTo` must be an absolute URL.** Better Auth resolves it against the **API** origin, so
|
|
461
|
+
a relative value lands on the API host where the route does not exist — a 403
|
|
462
|
+
`INVALID_REDIRECT_URL`, no mail sent, and nothing visible in the browser. In an lt starter
|
|
463
|
+
project the auto-imported `appUrl()` helper builds one and throws rather than returning something
|
|
464
|
+
relative; that helper lives in the starter (`app/utils/app-origin.ts`), not in this package, so
|
|
465
|
+
outside a starter use `new URL(path, siteUrl).toString()` as above.
|
|
466
|
+
|
|
467
|
+
**Never build `redirectTo` from user input** (`route.query`, a referrer, a form field). The link
|
|
468
|
+
carries a live single-use reset token, so an attacker-chosen destination is account takeover
|
|
469
|
+
rather than a phishing redirect. The server's `trustedOrigins` allowlist is what stops it.
|
|
470
|
+
|
|
471
|
+
**Enforce a minimum password length in your own form.** better-auth checks `minPasswordLength`
|
|
472
|
+
against the value it receives, and because this library hashes first, that value is always a
|
|
473
|
+
64-character hex string — the check passes on every path. The form is the only place the rule can
|
|
474
|
+
live.
|
|
475
|
+
|
|
429
476
|
### Custom Better Auth Plugins
|
|
430
477
|
|
|
431
478
|
You can extend the auth client with additional [Better Auth plugins](https://www.better-auth.com/docs/plugins):
|
|
@@ -655,8 +702,8 @@ The package works **with or without** `@nuxtjs/i18n`:
|
|
|
655
702
|
|
|
656
703
|
| Composable | Description |
|
|
657
704
|
|------------|-------------|
|
|
658
|
-
| `useLtAuth()` | Better-Auth integration with session, passkey, 2FA |
|
|
659
|
-
| `useLtAuthClient()` | Direct access to the Better-Auth client singleton |
|
|
705
|
+
| `useLtAuth()` | Better-Auth integration with session, passkey, 2FA, password change + reset |
|
|
706
|
+
| `useLtAuthClient()` | Direct access to the Better-Auth client singleton (`twoFactor.*`, `admin.*`, `passkey`) |
|
|
660
707
|
| `useLtErrorTranslation()` | Translate backend error codes to user-friendly messages |
|
|
661
708
|
| `useLtTusUpload()` | TUS protocol file uploads with pause/resume |
|
|
662
709
|
| `useLtFile()` | File utilities (size formatting, URLs) |
|
package/dist/module.d.mts
CHANGED
|
@@ -3,7 +3,7 @@ import { LtExtensionsModuleOptions } from '../dist/runtime/types/index.js';
|
|
|
3
3
|
export * from '../dist/runtime/types/index.js';
|
|
4
4
|
|
|
5
5
|
declare const name = "@lenne.tech/nuxt-extensions";
|
|
6
|
-
declare const version = "1.
|
|
6
|
+
declare const version = "1.16.0";
|
|
7
7
|
declare const configKey = "ltExtensions";
|
|
8
8
|
declare const DEFAULT_LT_AUTH_STATE_COOKIE = "lt-auth-state";
|
|
9
9
|
declare const DEFAULT_LT_JWT_TOKEN_COOKIE = "lt-jwt-token";
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { defineNuxtModule, createResolver, tryResolveModule, addImports, addComp
|
|
|
2
2
|
import { pathToFileURL } from 'node:url';
|
|
3
3
|
|
|
4
4
|
const name = "@lenne.tech/nuxt-extensions";
|
|
5
|
-
const version = "1.
|
|
5
|
+
const version = "1.16.0";
|
|
6
6
|
const configKey = "ltExtensions";
|
|
7
7
|
const DEFAULT_LT_AUTH_STATE_COOKIE = "lt-auth-state";
|
|
8
8
|
const DEFAULT_LT_JWT_TOKEN_COOKIE = "lt-jwt-token";
|
|
@@ -201,7 +201,6 @@ export function useLtAuth() {
|
|
|
201
201
|
}
|
|
202
202
|
const signIn = {
|
|
203
203
|
...authClient.signIn,
|
|
204
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
205
204
|
email: async (params, options) => {
|
|
206
205
|
isLoading.value = true;
|
|
207
206
|
try {
|
|
@@ -227,7 +226,6 @@ export function useLtAuth() {
|
|
|
227
226
|
};
|
|
228
227
|
const signUp = {
|
|
229
228
|
...authClient.signUp,
|
|
230
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
231
229
|
email: async (params, options) => {
|
|
232
230
|
isLoading.value = true;
|
|
233
231
|
try {
|
|
@@ -382,7 +380,6 @@ export function useLtAuth() {
|
|
|
382
380
|
timeout: options.timeout,
|
|
383
381
|
attestation: options.attestation,
|
|
384
382
|
authenticatorSelection: options.authenticatorSelection,
|
|
385
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
386
383
|
excludeCredentials: (options.excludeCredentials || []).map((cred) => ({
|
|
387
384
|
...cred,
|
|
388
385
|
id: ltBase64UrlToUint8Array(cred.id).buffer
|
|
@@ -479,6 +476,21 @@ export function useLtAuth() {
|
|
|
479
476
|
changePassword: authClient.changePassword,
|
|
480
477
|
clearUser,
|
|
481
478
|
registerPasskey,
|
|
479
|
+
// The reset pair, exposed so no project has to reach for `useLtAuthClient()` or hand-roll
|
|
480
|
+
// the flow — the one that did forgot the client-side hashing and desynchronised the two
|
|
481
|
+
// credential stores. The full contract, including why `redirectTo` must be absolute and
|
|
482
|
+
// must never come from user input, lives on `UseLtAuthReturn` in `runtime/types/auth.ts`:
|
|
483
|
+
// that is the surface consumers read, and one copy cannot drift from itself.
|
|
484
|
+
//
|
|
485
|
+
// `params` is forwarded WHOLE and stays fully type-checked. Only `options` is cast, and
|
|
486
|
+
// only because `UseLtAuthReturn` declares it `unknown` while Better-Auth wants its own
|
|
487
|
+
// `ClientFetchOption`. Casting the METHOD instead — which is what this used to do — would
|
|
488
|
+
// throw away the checking on `params` as collateral, on the one argument that carries a
|
|
489
|
+
// credential. The `Parameters<>` here reads the client's option type INSIDE the
|
|
490
|
+
// implementation; it never reaches the published declarations, so it does not recreate
|
|
491
|
+
// the better-auth coupling that 1.15.1 had to revert.
|
|
492
|
+
requestPasswordReset: (params, options) => authClient.requestPasswordReset(params, options),
|
|
493
|
+
resetPassword: (params, options) => authClient.resetPassword(params, options),
|
|
482
494
|
setUser,
|
|
483
495
|
signIn,
|
|
484
496
|
signOut,
|
|
@@ -6,6 +6,34 @@
|
|
|
6
6
|
*
|
|
7
7
|
* SECURITY: Passwords are hashed with SHA256 client-side to prevent
|
|
8
8
|
* plain text password transmission over the network.
|
|
9
|
+
*
|
|
10
|
+
* ---
|
|
11
|
+
*
|
|
12
|
+
* A HASHING WRAPPER REPLACES ONE FIELD — IT IS NEVER A WHITELIST.
|
|
13
|
+
*
|
|
14
|
+
* This is the invariant every wrapper below shares, and it has been broken twice, so it
|
|
15
|
+
* lives here rather than beside one method that a refactor could delete.
|
|
16
|
+
*
|
|
17
|
+
* Each wrapper exists to substitute ONE field with its SHA256 digest. It must forward
|
|
18
|
+
* everything else the caller passed, untouched:
|
|
19
|
+
*
|
|
20
|
+
* { ...params, password: hashedPassword } // correct
|
|
21
|
+
* { password: hashedPassword } // WRONG — drops every other option
|
|
22
|
+
* { password: hashedPassword, ...params } // WRONG — puts the PLAINTEXT back
|
|
23
|
+
*
|
|
24
|
+
* The first mistake is silent data loss: `changePassword` dropped `revokeOtherSessions`,
|
|
25
|
+
* so callers who set it after a suspected compromise got a successful password change and
|
|
26
|
+
* every other session left open. `twoFactor.enable` dropped `method` and `issuer`, so
|
|
27
|
+
* `method: 'otp'` quietly enabled TOTP instead.
|
|
28
|
+
*
|
|
29
|
+
* The second is worse and looks almost identical: `params` still carries the raw value, so
|
|
30
|
+
* a trailing spread overwrites the digest and the plaintext password goes on the wire.
|
|
31
|
+
* Trailing-spread is the more common JS idiom, which is exactly why this warning is here.
|
|
32
|
+
*
|
|
33
|
+
* Both are pinned by tests: `test/auth-client-hashing.test.ts` asserts the real request
|
|
34
|
+
* payload (it catches both mistakes), `test/auth-client-param-forwarding.test.ts` keeps the
|
|
35
|
+
* source-level half so a whitelist rebuild is visible in review. If you add a wrapper that
|
|
36
|
+
* hashes something, add it to both.
|
|
9
37
|
*/
|
|
10
38
|
import type { LtAuthClientConfig } from '../types/index.js';
|
|
11
39
|
/**
|
|
@@ -184,8 +212,28 @@ export declare function createLtAuthClient(config?: LtAuthClientConfig): {
|
|
|
184
212
|
}>;
|
|
185
213
|
}>;
|
|
186
214
|
};
|
|
187
|
-
passkey:
|
|
188
|
-
|
|
215
|
+
passkey: any;
|
|
216
|
+
/**
|
|
217
|
+
* Better-Auth admin plugin, passed through UNWRAPPED — and therefore UNHASHED.
|
|
218
|
+
*
|
|
219
|
+
* `admin.createUser` (`password`) and `admin.setUserPassword` (`newPassword`) carry
|
|
220
|
+
* credentials that every other method in this file hashes. These two do not.
|
|
221
|
+
*
|
|
222
|
+
* Why that is deliberate rather than an oversight: `@lenne.tech/nest-server` does not
|
|
223
|
+
* register better-auth's `admin()` plugin and offers no option to — the two routes do
|
|
224
|
+
* not exist server-side, so a call 404s today. Hashing here would build a client
|
|
225
|
+
* expectation the server does not answer, and if the plugin were later enabled without
|
|
226
|
+
* normalising those routes, this wrapper would CREATE the credential-store desync it
|
|
227
|
+
* was meant to prevent.
|
|
228
|
+
*
|
|
229
|
+
* What that costs, stated plainly: `auth.enableAdmin` defaults to `true`, so the admin
|
|
230
|
+
* CLIENT plugin is registered in every consuming project. The trap is armed and waiting
|
|
231
|
+
* for a server that answers. Enabling `admin()` server-side is therefore a LOCKSTEP
|
|
232
|
+
* change — the routes join nest-server's password-normalisation table and these two
|
|
233
|
+
* methods get `ltSha256` wrappers here, in one release. Half of it produces accounts
|
|
234
|
+
* whose password nobody can log in with, and nothing warns.
|
|
235
|
+
*/
|
|
236
|
+
admin: any;
|
|
189
237
|
$Infer: {
|
|
190
238
|
Session: {
|
|
191
239
|
user: import("better-auth").StripEmptyObjects<{
|
|
@@ -297,12 +345,44 @@ export declare function createLtAuthClient(config?: LtAuthClientConfig): {
|
|
|
297
345
|
message?: string | undefined;
|
|
298
346
|
}, FetchOptions["throw"] extends true ? true : false>>;
|
|
299
347
|
/**
|
|
300
|
-
* Change password for an authenticated user (both passwords are hashed)
|
|
348
|
+
* Change password for an authenticated user (both passwords are hashed).
|
|
349
|
+
*
|
|
350
|
+
* `revokeOtherSessions` is the option that made the whitelist bug matter — see the
|
|
351
|
+
* file header.
|
|
301
352
|
*/
|
|
302
|
-
changePassword:
|
|
353
|
+
changePassword: <T extends {
|
|
303
354
|
currentPassword: string;
|
|
304
355
|
newPassword: string;
|
|
305
|
-
}, options?:
|
|
356
|
+
}>(params: T, options?: any) => Promise<(Omit<{
|
|
357
|
+
token: string | null;
|
|
358
|
+
user: {
|
|
359
|
+
id: string;
|
|
360
|
+
createdAt: Date;
|
|
361
|
+
updatedAt: Date;
|
|
362
|
+
email: string;
|
|
363
|
+
emailVerified: boolean;
|
|
364
|
+
name: string;
|
|
365
|
+
image?: string | null | undefined;
|
|
366
|
+
} & Record<string, any> & {
|
|
367
|
+
id: string;
|
|
368
|
+
createdAt: Date;
|
|
369
|
+
updatedAt: Date;
|
|
370
|
+
email: string;
|
|
371
|
+
emailVerified: boolean;
|
|
372
|
+
name: string;
|
|
373
|
+
image?: string | null | undefined;
|
|
374
|
+
};
|
|
375
|
+
}, "user"> & {
|
|
376
|
+
user: import("better-auth").StripEmptyObjects<{
|
|
377
|
+
id: string;
|
|
378
|
+
createdAt: Date;
|
|
379
|
+
updatedAt: Date;
|
|
380
|
+
email: string;
|
|
381
|
+
emailVerified: boolean;
|
|
382
|
+
name: string;
|
|
383
|
+
image?: string | null | undefined;
|
|
384
|
+
}>;
|
|
385
|
+
}) | {
|
|
306
386
|
data: Omit<{
|
|
307
387
|
token: string | null;
|
|
308
388
|
user: {
|
|
@@ -344,12 +424,17 @@ export declare function createLtAuthClient(config?: LtAuthClientConfig): {
|
|
|
344
424
|
};
|
|
345
425
|
}>;
|
|
346
426
|
/**
|
|
347
|
-
* Reset password with token (new password is hashed before sending)
|
|
427
|
+
* Reset password with token (new password is hashed before sending).
|
|
428
|
+
*
|
|
429
|
+
* Spread first, then overwrite the one field this wrapper exists for — see the file
|
|
430
|
+
* header for why the order is not a style question.
|
|
348
431
|
*/
|
|
349
|
-
resetPassword:
|
|
432
|
+
resetPassword: <T extends {
|
|
350
433
|
newPassword: string;
|
|
351
434
|
token: string;
|
|
352
|
-
}, options?:
|
|
435
|
+
}>(params: T, options?: any) => Promise<{
|
|
436
|
+
status: boolean;
|
|
437
|
+
} | {
|
|
353
438
|
data: {
|
|
354
439
|
status: boolean;
|
|
355
440
|
};
|
|
@@ -367,11 +452,34 @@ export declare function createLtAuthClient(config?: LtAuthClientConfig): {
|
|
|
367
452
|
/**
|
|
368
453
|
* Sign in with email and password (password is hashed before sending)
|
|
369
454
|
*/
|
|
370
|
-
email:
|
|
455
|
+
email: <T extends {
|
|
371
456
|
email: string;
|
|
372
457
|
password: string;
|
|
373
458
|
rememberMe?: boolean;
|
|
374
|
-
}, options?:
|
|
459
|
+
}>(params: T, options?: any) => Promise<(Omit<{
|
|
460
|
+
redirect: boolean;
|
|
461
|
+
token: string;
|
|
462
|
+
url?: string | undefined;
|
|
463
|
+
user: {
|
|
464
|
+
id: string;
|
|
465
|
+
createdAt: Date;
|
|
466
|
+
updatedAt: Date;
|
|
467
|
+
email: string;
|
|
468
|
+
emailVerified: boolean;
|
|
469
|
+
name: string;
|
|
470
|
+
image?: string | null | undefined | undefined;
|
|
471
|
+
};
|
|
472
|
+
}, "user"> & {
|
|
473
|
+
user: import("better-auth").StripEmptyObjects<{
|
|
474
|
+
id: string;
|
|
475
|
+
createdAt: Date;
|
|
476
|
+
updatedAt: Date;
|
|
477
|
+
email: string;
|
|
478
|
+
emailVerified: boolean;
|
|
479
|
+
name: string;
|
|
480
|
+
image?: string | null | undefined;
|
|
481
|
+
}>;
|
|
482
|
+
}) | {
|
|
375
483
|
data: Omit<{
|
|
376
484
|
redirect: boolean;
|
|
377
485
|
token: string;
|
|
@@ -410,7 +518,7 @@ export declare function createLtAuthClient(config?: LtAuthClientConfig): {
|
|
|
410
518
|
* Sign in with passkey (pass through to base client - provided by passkeyClient plugin)
|
|
411
519
|
* @see https://www.better-auth.com/docs/plugins/passkey
|
|
412
520
|
*/
|
|
413
|
-
passkey:
|
|
521
|
+
passkey: any;
|
|
414
522
|
social: <FetchOptions extends import("better-auth").ClientFetchOption<Partial<{
|
|
415
523
|
provider: (string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat";
|
|
416
524
|
callbackURL?: string | undefined;
|
|
@@ -519,11 +627,53 @@ export declare function createLtAuthClient(config?: LtAuthClientConfig): {
|
|
|
519
627
|
/**
|
|
520
628
|
* Sign up with email and password (password is hashed before sending)
|
|
521
629
|
*/
|
|
522
|
-
email:
|
|
630
|
+
email: <T extends {
|
|
523
631
|
email: string;
|
|
524
632
|
name: string;
|
|
525
633
|
password: string;
|
|
526
|
-
}
|
|
634
|
+
}>(params: T, options?: any) => Promise<(Omit<{
|
|
635
|
+
token: null;
|
|
636
|
+
user: {
|
|
637
|
+
id: string;
|
|
638
|
+
createdAt: Date;
|
|
639
|
+
updatedAt: Date;
|
|
640
|
+
email: string;
|
|
641
|
+
emailVerified: boolean;
|
|
642
|
+
name: string;
|
|
643
|
+
image?: string | null | undefined | undefined;
|
|
644
|
+
};
|
|
645
|
+
}, "user"> & {
|
|
646
|
+
user: import("better-auth").StripEmptyObjects<{
|
|
647
|
+
id: string;
|
|
648
|
+
createdAt: Date;
|
|
649
|
+
updatedAt: Date;
|
|
650
|
+
email: string;
|
|
651
|
+
emailVerified: boolean;
|
|
652
|
+
name: string;
|
|
653
|
+
image?: string | null | undefined;
|
|
654
|
+
}>;
|
|
655
|
+
}) | (Omit<{
|
|
656
|
+
token: string;
|
|
657
|
+
user: {
|
|
658
|
+
id: string;
|
|
659
|
+
createdAt: Date;
|
|
660
|
+
updatedAt: Date;
|
|
661
|
+
email: string;
|
|
662
|
+
emailVerified: boolean;
|
|
663
|
+
name: string;
|
|
664
|
+
image?: string | null | undefined | undefined;
|
|
665
|
+
};
|
|
666
|
+
}, "user"> & {
|
|
667
|
+
user: import("better-auth").StripEmptyObjects<{
|
|
668
|
+
id: string;
|
|
669
|
+
createdAt: Date;
|
|
670
|
+
updatedAt: Date;
|
|
671
|
+
email: string;
|
|
672
|
+
emailVerified: boolean;
|
|
673
|
+
name: string;
|
|
674
|
+
image?: string | null | undefined;
|
|
675
|
+
}>;
|
|
676
|
+
}) | {
|
|
527
677
|
data: (Omit<{
|
|
528
678
|
token: null;
|
|
529
679
|
user: {
|
|
@@ -578,34 +728,7 @@ export declare function createLtAuthClient(config?: LtAuthClientConfig): {
|
|
|
578
728
|
};
|
|
579
729
|
}>;
|
|
580
730
|
};
|
|
581
|
-
twoFactor:
|
|
582
|
-
/**
|
|
583
|
-
* Disable 2FA (password is hashed before sending)
|
|
584
|
-
*/
|
|
585
|
-
disable: (params: {
|
|
586
|
-
password: string;
|
|
587
|
-
}, options?: unknown) => Promise<unknown>;
|
|
588
|
-
/**
|
|
589
|
-
* Enable 2FA (password is hashed before sending)
|
|
590
|
-
*/
|
|
591
|
-
enable: (params: {
|
|
592
|
-
password: string;
|
|
593
|
-
}, options?: unknown) => Promise<unknown>;
|
|
594
|
-
/**
|
|
595
|
-
* Generate backup codes (password is hashed before sending)
|
|
596
|
-
*/
|
|
597
|
-
generateBackupCodes: (params: {
|
|
598
|
-
password: string;
|
|
599
|
-
}, options?: unknown) => Promise<unknown>;
|
|
600
|
-
/**
|
|
601
|
-
* Verify TOTP code (pass through to base client)
|
|
602
|
-
*/
|
|
603
|
-
verifyTotp: ((...args: unknown[]) => Promise<unknown>) | undefined;
|
|
604
|
-
/**
|
|
605
|
-
* Verify backup code (pass through to base client)
|
|
606
|
-
*/
|
|
607
|
-
verifyBackupCode: ((...args: unknown[]) => Promise<unknown>) | undefined;
|
|
608
|
-
};
|
|
731
|
+
twoFactor: any;
|
|
609
732
|
verifyEmail: <FetchOptions extends import("better-auth").ClientFetchOption<never, Partial<{
|
|
610
733
|
token: string;
|
|
611
734
|
callbackURL?: string | undefined;
|
|
@@ -78,32 +78,65 @@ export function createLtAuthClient(config = {}) {
|
|
|
78
78
|
},
|
|
79
79
|
plugins
|
|
80
80
|
});
|
|
81
|
-
const withPlugins = baseClient;
|
|
82
|
-
const signInWithPlugins = baseClient.signIn;
|
|
83
81
|
return {
|
|
84
82
|
// Spread all base client properties and methods
|
|
85
83
|
...baseClient,
|
|
86
84
|
// Explicitly pass through methods not captured by spread operator
|
|
87
85
|
useSession: baseClient.useSession,
|
|
88
|
-
passkey:
|
|
89
|
-
|
|
86
|
+
passkey: baseClient.passkey,
|
|
87
|
+
/**
|
|
88
|
+
* Better-Auth admin plugin, passed through UNWRAPPED — and therefore UNHASHED.
|
|
89
|
+
*
|
|
90
|
+
* `admin.createUser` (`password`) and `admin.setUserPassword` (`newPassword`) carry
|
|
91
|
+
* credentials that every other method in this file hashes. These two do not.
|
|
92
|
+
*
|
|
93
|
+
* Why that is deliberate rather than an oversight: `@lenne.tech/nest-server` does not
|
|
94
|
+
* register better-auth's `admin()` plugin and offers no option to — the two routes do
|
|
95
|
+
* not exist server-side, so a call 404s today. Hashing here would build a client
|
|
96
|
+
* expectation the server does not answer, and if the plugin were later enabled without
|
|
97
|
+
* normalising those routes, this wrapper would CREATE the credential-store desync it
|
|
98
|
+
* was meant to prevent.
|
|
99
|
+
*
|
|
100
|
+
* What that costs, stated plainly: `auth.enableAdmin` defaults to `true`, so the admin
|
|
101
|
+
* CLIENT plugin is registered in every consuming project. The trap is armed and waiting
|
|
102
|
+
* for a server that answers. Enabling `admin()` server-side is therefore a LOCKSTEP
|
|
103
|
+
* change — the routes join nest-server's password-normalisation table and these two
|
|
104
|
+
* methods get `ltSha256` wrappers here, in one release. Half of it produces accounts
|
|
105
|
+
* whose password nobody can log in with, and nothing warns.
|
|
106
|
+
*/
|
|
107
|
+
admin: baseClient.admin,
|
|
90
108
|
$Infer: baseClient.$Infer,
|
|
91
109
|
$fetch: baseClient.$fetch,
|
|
92
110
|
$store: baseClient.$store,
|
|
111
|
+
// Deliberately a bare passthrough: it takes an email address, never a password, so
|
|
112
|
+
// there is nothing to hash — and passing it through untouched is what keeps
|
|
113
|
+
// `redirectTo` intact (see the note above `resetPassword`).
|
|
93
114
|
requestPasswordReset: baseClient.requestPasswordReset,
|
|
94
115
|
/**
|
|
95
|
-
* Change password for an authenticated user (both passwords are hashed)
|
|
116
|
+
* Change password for an authenticated user (both passwords are hashed).
|
|
117
|
+
*
|
|
118
|
+
* `revokeOtherSessions` is the option that made the whitelist bug matter — see the
|
|
119
|
+
* file header.
|
|
96
120
|
*/
|
|
97
121
|
changePassword: async (params, options) => {
|
|
98
122
|
const [hashedCurrent, hashedNew] = await Promise.all([ltSha256(params.currentPassword), ltSha256(params.newPassword)]);
|
|
99
|
-
|
|
123
|
+
if (!baseClient.changePassword) {
|
|
124
|
+
throw new Error("[lt-auth] changePassword is unavailable on the Better-Auth client \u2014 the password was NOT changed.");
|
|
125
|
+
}
|
|
126
|
+
return baseClient.changePassword({ ...params, currentPassword: hashedCurrent, newPassword: hashedNew }, options);
|
|
100
127
|
},
|
|
101
128
|
/**
|
|
102
|
-
* Reset password with token (new password is hashed before sending)
|
|
129
|
+
* Reset password with token (new password is hashed before sending).
|
|
130
|
+
*
|
|
131
|
+
* Spread first, then overwrite the one field this wrapper exists for — see the file
|
|
132
|
+
* header for why the order is not a style question.
|
|
103
133
|
*/
|
|
104
134
|
resetPassword: async (params, options) => {
|
|
105
135
|
const hashedPassword = await ltSha256(params.newPassword);
|
|
106
|
-
|
|
136
|
+
if (!baseClient.resetPassword) {
|
|
137
|
+
throw new Error("[lt-auth] resetPassword is unavailable on the Better-Auth client \u2014 the password was NOT changed.");
|
|
138
|
+
}
|
|
139
|
+
return baseClient.resetPassword({ ...params, newPassword: hashedPassword }, options);
|
|
107
140
|
},
|
|
108
141
|
// Override signIn to hash password (keep passkey method from plugin)
|
|
109
142
|
signIn: {
|
|
@@ -119,7 +152,7 @@ export function createLtAuthClient(config = {}) {
|
|
|
119
152
|
* Sign in with passkey (pass through to base client - provided by passkeyClient plugin)
|
|
120
153
|
* @see https://www.better-auth.com/docs/plugins/passkey
|
|
121
154
|
*/
|
|
122
|
-
passkey:
|
|
155
|
+
passkey: baseClient.signIn.passkey
|
|
123
156
|
},
|
|
124
157
|
// Explicitly pass through signOut (not captured by spread operator)
|
|
125
158
|
signOut: baseClient.signOut,
|
|
@@ -136,36 +169,36 @@ export function createLtAuthClient(config = {}) {
|
|
|
136
169
|
},
|
|
137
170
|
// Override twoFactor to hash passwords (provided by twoFactorClient plugin)
|
|
138
171
|
twoFactor: {
|
|
139
|
-
...
|
|
172
|
+
...baseClient.twoFactor,
|
|
140
173
|
/**
|
|
141
174
|
* Disable 2FA (password is hashed before sending)
|
|
142
175
|
*/
|
|
143
176
|
disable: async (params, options) => {
|
|
144
177
|
const hashedPassword = await ltSha256(params.password);
|
|
145
|
-
return
|
|
178
|
+
return baseClient.twoFactor.disable({ ...params, password: hashedPassword }, options);
|
|
146
179
|
},
|
|
147
180
|
/**
|
|
148
181
|
* Enable 2FA (password is hashed before sending)
|
|
149
182
|
*/
|
|
150
183
|
enable: async (params, options) => {
|
|
151
184
|
const hashedPassword = await ltSha256(params.password);
|
|
152
|
-
return
|
|
185
|
+
return baseClient.twoFactor.enable({ ...params, password: hashedPassword }, options);
|
|
153
186
|
},
|
|
154
187
|
/**
|
|
155
188
|
* Generate backup codes (password is hashed before sending)
|
|
156
189
|
*/
|
|
157
190
|
generateBackupCodes: async (params, options) => {
|
|
158
191
|
const hashedPassword = await ltSha256(params.password);
|
|
159
|
-
return
|
|
192
|
+
return baseClient.twoFactor.generateBackupCodes({ ...params, password: hashedPassword }, options);
|
|
160
193
|
},
|
|
161
194
|
/**
|
|
162
195
|
* Verify TOTP code (pass through to base client)
|
|
163
196
|
*/
|
|
164
|
-
verifyTotp:
|
|
197
|
+
verifyTotp: baseClient.twoFactor.verifyTotp,
|
|
165
198
|
/**
|
|
166
199
|
* Verify backup code (pass through to base client)
|
|
167
200
|
*/
|
|
168
|
-
verifyBackupCode:
|
|
201
|
+
verifyBackupCode: baseClient.twoFactor.verifyBackupCode
|
|
169
202
|
}
|
|
170
203
|
};
|
|
171
204
|
}
|
|
@@ -133,7 +133,6 @@ export default (nuxtApp) => {
|
|
|
133
133
|
globalThis.$fetch = ((url, options) => {
|
|
134
134
|
return originalFetch(url, {
|
|
135
135
|
...options,
|
|
136
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
137
136
|
onResponseError: (context) => {
|
|
138
137
|
if (options?.onResponseError) {
|
|
139
138
|
options.onResponseError(context);
|
|
@@ -102,6 +102,31 @@ export interface LtPasskeyRegisterResult {
|
|
|
102
102
|
/**
|
|
103
103
|
* Return type for useLtAuth composable
|
|
104
104
|
*/
|
|
105
|
+
/**
|
|
106
|
+
* Accepts any caller-supplied shape while refusing a stray plaintext credential field.
|
|
107
|
+
*
|
|
108
|
+
* WHY A GENERIC AND NOT `& Record<string, unknown>`
|
|
109
|
+
*
|
|
110
|
+
* TypeScript gives a `type` alias an implicit index signature and an `interface` deliberately none
|
|
111
|
+
* (declaration merging could add anything later). So an intersection with `Record<string, unknown>`
|
|
112
|
+
* REJECTS an interface-typed variable — and `app/interfaces/*.interface.ts` plus `Ref<Form>.value`
|
|
113
|
+
* are the dominant shapes in this stack. That is how 1.15.0 broke consuming projects; the same
|
|
114
|
+
* mistake in a different costume is not worth repeating.
|
|
115
|
+
*
|
|
116
|
+
* A generic parameter carries every shape and keeps the excess-property check, which matters:
|
|
117
|
+
* widening these signatures to forward caller options also removed the compiler's objection to
|
|
118
|
+
* `changePassword({ …, password: plaintext })` — a plaintext credential that would land in the
|
|
119
|
+
* request body and from there in proxy logs, APM body capture and error reporters. Naming the
|
|
120
|
+
* foreign credential keys `never` puts that objection back without closing the passthrough.
|
|
121
|
+
*
|
|
122
|
+
* @typeParam T - whatever the caller actually passes
|
|
123
|
+
* @typeParam Own - the credential keys this method legitimately carries
|
|
124
|
+
*/
|
|
125
|
+
type NoStrayCredential<T, Own extends string> = Omit<{
|
|
126
|
+
currentPassword?: never;
|
|
127
|
+
newPassword?: never;
|
|
128
|
+
password?: never;
|
|
129
|
+
}, Own> & T;
|
|
105
130
|
export interface UseLtAuthReturn {
|
|
106
131
|
authMode: ComputedRef<LtAuthMode>;
|
|
107
132
|
isAuthenticated: ComputedRef<boolean>;
|
|
@@ -157,33 +182,95 @@ export interface UseLtAuthReturn {
|
|
|
157
182
|
features: ComputedRef<Record<string, boolean | number | string[]>>;
|
|
158
183
|
fetchFeatures: () => Promise<Record<string, boolean | number | string[]>>;
|
|
159
184
|
authenticateWithPasskey: () => Promise<LtPasskeyAuthResult>;
|
|
160
|
-
|
|
185
|
+
/**
|
|
186
|
+
* Change the password of the signed-in user. Both passwords are hashed client-side.
|
|
187
|
+
*
|
|
188
|
+
* Pass `revokeOtherSessions: true` after a suspected compromise — Better Auth reads it on the
|
|
189
|
+
* `update-user` route and ends every other session. Until 1.16.0 the wrapper silently dropped
|
|
190
|
+
* it, so a caller believed foreign sessions were ended when they were not.
|
|
191
|
+
*/
|
|
192
|
+
changePassword: <T extends {
|
|
161
193
|
currentPassword: string;
|
|
162
194
|
newPassword: string;
|
|
163
|
-
}, options?: unknown) => Promise<unknown>;
|
|
195
|
+
}>(params: NoStrayCredential<T, 'currentPassword' | 'newPassword'>, options?: unknown) => Promise<unknown>;
|
|
164
196
|
clearUser: () => void;
|
|
165
197
|
fetchWithAuth: (url: string, options?: RequestInit) => Promise<Response>;
|
|
166
198
|
refreshJwtToken: () => Promise<boolean>;
|
|
167
199
|
registerPasskey: (name?: string) => Promise<LtPasskeyRegisterResult>;
|
|
200
|
+
/**
|
|
201
|
+
* Ask for a password-reset mail.
|
|
202
|
+
*
|
|
203
|
+
* `redirectTo` MUST be an absolute app URL — Better Auth resolves it against the API
|
|
204
|
+
* origin, so a relative value lands on the API host, the route does not exist, and the
|
|
205
|
+
* answer is a 403 with no mail sent. In an lt starter project `appUrl()` builds one; elsewhere
|
|
206
|
+
* construct it yourself, e.g. `new URL('/auth/reset-password', config.public.siteUrl).toString()`.
|
|
207
|
+
*
|
|
208
|
+
* **SECURITY: never build `redirectTo` from `route.query`, a referrer, or a form field.** The
|
|
209
|
+
* reset redirect carries a live single-use token, so an attacker-controlled value is account
|
|
210
|
+
* takeover rather than phishing. What holds it back is `trustedOrigins` rejecting foreign
|
|
211
|
+
* origins — a project that widens that removes the last barrier.
|
|
212
|
+
*
|
|
213
|
+
* From `@lenne.tech/nest-server` 11.38.0 the reset mail links straight to the app, so
|
|
214
|
+
* `redirectTo` is no longer consulted on the default path. It stays load-bearing where a project
|
|
215
|
+
* sets `betterAuth.emailVerification.passwordResetLink: false` to keep Better Auth's own link.
|
|
216
|
+
*
|
|
217
|
+
* Generic rather than a closed shape: this is a passthrough, and a closed shape would push
|
|
218
|
+
* callers back to the raw client for any option the library has not enumerated yet — which is
|
|
219
|
+
* how hand-rolled reset flows start.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* const { requestPasswordReset } = useLtAuth();
|
|
223
|
+
* await requestPasswordReset({
|
|
224
|
+
* email,
|
|
225
|
+
* redirectTo: new URL('/auth/reset-password', config.public.siteUrl).toString(),
|
|
226
|
+
* });
|
|
227
|
+
* @see {@link UseLtAuthReturn.resetPassword} — step two, with the token from the query string.
|
|
228
|
+
*/
|
|
229
|
+
requestPasswordReset: <T extends {
|
|
230
|
+
email: string;
|
|
231
|
+
}>(params: NoStrayCredential<T, never>, options?: unknown) => Promise<unknown>;
|
|
232
|
+
/**
|
|
233
|
+
* Set a new password from a reset token. The password is hashed client-side.
|
|
234
|
+
*
|
|
235
|
+
* **The minimum-length rule has to live in your form.** Better Auth checks `minPasswordLength`
|
|
236
|
+
* against the value it RECEIVES, and this client hashes first — so the server sees 64 characters
|
|
237
|
+
* whatever the user typed, on every version. That is a consequence of client-side hashing, not
|
|
238
|
+
* of any particular server release. The lt starter's `reset-password.vue`, `register.vue` and
|
|
239
|
+
* `setup.vue` declare `v.minLength(8)`; a project using this package without those forms has to
|
|
240
|
+
* add it.
|
|
241
|
+
*
|
|
242
|
+
* Takes `{ newPassword, token }` and nothing else — better-auth's `/reset-password` body schema
|
|
243
|
+
* carries no `redirectTo`, so the absolute-URL trap applies one step earlier, on
|
|
244
|
+
* {@link UseLtAuthReturn.requestPasswordReset}.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* const { resetPassword } = useLtAuth();
|
|
248
|
+
* await resetPassword({ newPassword, token: route.query.token as string });
|
|
249
|
+
*/
|
|
250
|
+
resetPassword: <T extends {
|
|
251
|
+
newPassword: string;
|
|
252
|
+
token: string;
|
|
253
|
+
}>(params: NoStrayCredential<T, 'newPassword'>, options?: unknown) => Promise<unknown>;
|
|
168
254
|
setUser: (userData: LtUser | null, mode?: LtAuthMode) => void;
|
|
169
255
|
signIn: {
|
|
170
|
-
email:
|
|
256
|
+
email: <T extends {
|
|
171
257
|
email: string;
|
|
172
258
|
password: string;
|
|
173
259
|
rememberMe?: boolean;
|
|
174
|
-
}, options?: unknown) => Promise<unknown>;
|
|
260
|
+
}>(params: NoStrayCredential<T, 'password'>, options?: unknown) => Promise<unknown>;
|
|
175
261
|
passkey?: (options?: unknown) => Promise<unknown>;
|
|
176
262
|
};
|
|
177
263
|
signOut: (options?: unknown) => Promise<unknown>;
|
|
178
264
|
signUp: {
|
|
179
|
-
email:
|
|
265
|
+
email: <T extends {
|
|
180
266
|
email: string;
|
|
181
267
|
name: string;
|
|
182
268
|
password: string;
|
|
183
|
-
}
|
|
269
|
+
}>(params: NoStrayCredential<T, 'password'>, options?: unknown) => Promise<unknown>;
|
|
184
270
|
};
|
|
185
271
|
switchToJwtMode: () => Promise<boolean>;
|
|
186
272
|
validateSession: () => Promise<boolean>;
|
|
187
273
|
passkey?: unknown;
|
|
188
274
|
twoFactor?: unknown;
|
|
189
275
|
}
|
|
276
|
+
export {};
|