@dotpress/auth 1.3.2 → 2.0.0-alpha.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/index.d.ts CHANGED
@@ -1,11 +1,682 @@
1
- import type { Plugin } from 'dotpress';
2
- import { AuthSettings, DataProvider, KeyPairSettings } from './types/types.js';
3
- import { generateAccessToken, generateTokensForUser, generateRefreshToken } from './lib/tokens.js';
4
- import { setAuthCookies } from './lib/cookies.js';
5
- type Settings = Partial<AuthSettings> & {
1
+ import * as _dotpress_core from '@dotpress/core';
2
+ import { DotpressRequest, HttpError, PluginCors, DotpressResponse } from '@dotpress/core';
3
+ import { JWK } from 'jose';
4
+
5
+ /** State of a rate limit counter. */
6
+ interface RateLimitState {
7
+ /** Number of hits in the current window, including the last one. */
8
+ count: number;
9
+ /** End of the current window. */
10
+ resetAt: Date;
11
+ }
12
+ /**
13
+ * Storage of rate limit counters (fixed windows). Use a shared store
14
+ * (Redis, database) when the app runs on several instances.
15
+ */
16
+ interface RateLimitStore {
17
+ /**
18
+ * Atomically increments the counter of a key and returns its state. A
19
+ * missing or expired counter starts a new window of `windowSeconds`.
20
+ */
21
+ increment(key: string, windowSeconds: number): Promise<RateLimitState>;
22
+ /** Deletes the counter of a key. */
23
+ reset(key: string): Promise<void>;
24
+ }
25
+ interface MemoryRateLimitStoreOptions {
26
+ /**
27
+ * Maximum number of counters kept in memory: expired counters are purged
28
+ * first, then the oldest ones. Defaults to 10 000.
29
+ */
30
+ maxKeys?: number;
31
+ }
32
+ /**
33
+ * In-memory rate limit store, for a single instance. With several
34
+ * instances, each one counts on its own: use a shared store.
35
+ */
36
+ declare const memoryRateLimitStore: (options?: MemoryRateLimitStoreOptions) => RateLimitStore;
37
+
38
+ /**
39
+ * Authenticated user, as returned by `DataProvider.findUserById()`.
40
+ * Declare its shape through module augmentation:
41
+ *
42
+ * @example
43
+ * declare module '@dotpress/auth' {
44
+ * interface AuthUser {
45
+ * id: string
46
+ * roles: string[]
47
+ * }
48
+ * }
49
+ */
50
+ interface AuthUser {
51
+ }
52
+ /**
53
+ * Data of the access token of the current request.
54
+ */
55
+ interface AuthSession {
56
+ readonly userId: string;
57
+ /** `username` claim, with the `usernameClaim` setting only. */
58
+ readonly username: string | undefined;
59
+ /** Token identifier (`jti`), shared with its refresh token. */
60
+ readonly tokenId: string;
61
+ /**
62
+ * Session of the token (`sid` claim): shared by the tokens rotated from
63
+ * the same login. `undefined` for tokens issued without refresh token
64
+ * (`generateAccessToken()`).
65
+ */
66
+ readonly sessionId: string | undefined;
67
+ /** Client the token was issued for (`client_id` claim). */
68
+ readonly clientId: string | undefined;
69
+ /** Roles of the user, used by `requireRole()`. */
70
+ readonly roles: readonly string[];
71
+ /** Decoded token claims. */
72
+ readonly claims: Readonly<Record<string, unknown>>;
73
+ }
74
+ interface KeyPairSettings {
75
+ /** RSA private key, PEM encoded in PKCS#8 (`BEGIN PRIVATE KEY`). */
76
+ secretKey: string;
77
+ /** RSA public key, PEM encoded in SPKI (`BEGIN PUBLIC KEY`). */
78
+ publicKey: string;
79
+ /**
80
+ * Key identifier (`kid` header of the tokens). Defaults to the RFC 7638
81
+ * thumbprint of the public key.
82
+ */
83
+ kid?: string;
84
+ /**
85
+ * Other public keys accepted to verify tokens (SPKI PEM, with an optional
86
+ * `kid`, defaulting to their thumbprint): the previous key after a
87
+ * rotation, or the next key before it, during rolling deployments.
88
+ */
89
+ additionalPublicKeys?: readonly (string | AdditionalPublicKey)[];
90
+ }
91
+ interface AdditionalPublicKey {
92
+ publicKey: string;
93
+ kid?: string;
94
+ }
95
+ interface CookieSettings {
96
+ /** Enables cookie mode. Defaults to `false`. */
97
+ enabled?: boolean;
98
+ /** Defaults to `true`. */
99
+ httpOnly?: boolean;
100
+ /** Defaults to `true` (browsers accept secure cookies on `localhost`). */
101
+ secure?: boolean;
102
+ /** Defaults to `lax`. */
103
+ sameSite?: 'strict' | 'lax' | 'none';
104
+ /**
105
+ * `SameSite` attribute of the refresh token cookie, which never needs to
106
+ * accompany a navigation from another site. Defaults to `strict`, or to
107
+ * `none` when `sameSite` is `none`.
108
+ */
109
+ refreshTokenSameSite?: 'strict' | 'lax' | 'none';
110
+ /**
111
+ * Origins allowed to send requests authenticated by the cookies, besides
112
+ * the API origin: for example `https://app.example.com` when the app is
113
+ * served from another subdomain. Other cross-origin requests do not use
114
+ * the cookies (protection against cross-site request forgery).
115
+ *
116
+ * Defaults to the origins allowed by the `cors` option of the app, when
117
+ * it allows credentials (`authPlugin()`), else to none.
118
+ */
119
+ trustedOrigins?: readonly string[];
120
+ domain?: string;
121
+ /** Path of the access token cookie. Defaults to `/`. */
122
+ path?: string;
123
+ /**
124
+ * Path of the refresh token cookie: the browser path of the auth routes,
125
+ * the only ones that need it. With `authPlugin()`, defaults to the path
126
+ * of its routes, including the `basePath` or `publicBasePath` of the app
127
+ * (for example `/my-app/api/auth`). Defaults to `/auth` otherwise.
128
+ */
129
+ refreshTokenPath?: string;
130
+ /**
131
+ * Defaults to `dotpress_access_token`, prefixed with `__Host-` when the
132
+ * cookie is secure, without domain and with path `/`, or with
133
+ * `__Secure-` when it is only secure.
134
+ */
135
+ accessTokenName?: string;
136
+ /**
137
+ * Defaults to `dotpress_refresh_token`, prefixed like the access token
138
+ * cookie (`__Secure-` with the default path).
139
+ */
140
+ refreshTokenName?: string;
141
+ /** Also stores the access token in a cookie. Defaults to `false`. */
142
+ accessTokenInCookie?: boolean;
143
+ }
144
+ type ResolvedCookieSettings = Required<Omit<CookieSettings, 'domain'>> & {
145
+ domain: string | undefined;
146
+ };
147
+ interface LoginFailedEvent {
148
+ username: string;
149
+ errorType: 'invalid_username' | 'invalid_password' | 'invalid_role'
150
+ /** Blocked by the login throttle, before checking the password. */
151
+ | 'too_many_attempts';
152
+ /** `clientId` sent at login; empty when missing. */
153
+ clientId: string;
154
+ ipAddress: string;
155
+ }
156
+ /** Login attempt, before the password is checked. */
157
+ interface BeforeLoginEvent {
158
+ username: string;
159
+ /** `clientId` sent at login; empty when missing. */
160
+ clientId: string;
161
+ ipAddress: string;
162
+ req: DotpressRequest;
163
+ }
164
+ /** At most `max` failed logins per `window` seconds. */
165
+ interface RateLimit {
166
+ max: number;
167
+ window: number;
168
+ }
169
+ /**
170
+ * Login throttle: failed logins are counted per username and IP address,
171
+ * and per username, whether the account exists or not.
172
+ */
173
+ interface LoginThrottleSettings {
174
+ /**
175
+ * Counters storage. Defaults to an in-memory store, for a single
176
+ * instance: use a shared store (Redis, database) with several instances.
177
+ */
178
+ store?: RateLimitStore;
179
+ limits?: {
180
+ /**
181
+ * Against brute force from one client, without letting others block
182
+ * the account. Defaults to 5 per minute; `false` disables it.
183
+ */
184
+ usernameAndIp?: RateLimit | false;
185
+ /**
186
+ * Against distributed brute force on an account. Defaults to 100 per
187
+ * hour (NIST 800-63B); `false` disables it.
188
+ */
189
+ username?: RateLimit | false;
190
+ };
191
+ }
192
+ interface ResolvedLoginThrottleSettings {
193
+ store: RateLimitStore;
194
+ limits: {
195
+ usernameAndIp: RateLimit | undefined;
196
+ username: RateLimit | undefined;
197
+ };
198
+ }
199
+ interface RefreshTokenReuseEvent {
200
+ userId: string;
201
+ /** Revoked session; undefined for tokens stored without family. */
202
+ familyId: string | undefined;
203
+ /** `jti` of the access token the reused refresh token was issued with. */
204
+ jwtId: string;
205
+ }
206
+ interface LoginSuccessEvent {
207
+ userId: string;
208
+ username: string;
209
+ /** `clientId` sent at login; empty when missing. */
210
+ clientId: string;
211
+ ipAddress: string;
212
+ }
213
+ interface StoreRefreshTokenDto {
214
+ token: string;
215
+ tokenHash: string;
216
+ userId: string;
217
+ jwtId: string;
218
+ /**
219
+ * Session the token belongs to: created at login, kept by every
220
+ * rotation. Used to revoke the whole session when a token is reused.
221
+ */
222
+ familyId: string;
223
+ expiresAt: Date;
224
+ }
225
+ /**
226
+ * Sessions revoked by a logout: the current session, or every session of
227
+ * the user.
228
+ */
229
+ type LogoutScope = 'current' | 'all';
230
+ /** Session revoked by `revokeSession()`. */
231
+ interface RevokedSession {
232
+ userId: string;
233
+ /** `undefined` for tokens stored without family (before V2). */
234
+ sessionId: string | undefined;
235
+ }
236
+ interface RefreshTokenData {
237
+ userId: string;
238
+ jwtId: string;
239
+ /** `null` or missing for tokens stored before families existed. */
240
+ familyId?: string | null;
241
+ expiresAt: Date;
242
+ isUsed: boolean;
243
+ /** When the token was used (see `refreshTokenReuseInterval`). */
244
+ usedAt?: Date | null;
245
+ revokedAt?: Date | null;
246
+ }
247
+ interface UserIdentifiers {
248
+ userId: string;
249
+ /**
250
+ * bcrypt hash of the password. `null` (or an empty string) for accounts
251
+ * without password (SSO, pending invitation): their login fails like a
252
+ * wrong password, in the same time.
253
+ */
254
+ passwordHash: string | null;
255
+ role?: string;
256
+ }
257
+ /**
258
+ * Storage of users and refresh tokens, implemented by the application
259
+ * (Prisma, Drizzle, SQL…).
260
+ */
261
+ interface DataProvider {
262
+ storeRefreshToken(payload: StoreRefreshTokenDto): Promise<void>;
263
+ findRefreshToken(tokenHash: string): Promise<RefreshTokenData | undefined>;
264
+ /** Marks a token as used, storing `usedAt`. */
265
+ markRefreshTokenAsUsed(tokenHash: string, usedAt: Date): Promise<void>;
266
+ /** Revokes every refresh token of a user. */
267
+ revokeTokens(userId: string): Promise<void>;
268
+ /** Revokes every refresh token of a session (`familyId`). */
269
+ revokeTokenFamily(familyId: string): Promise<void>;
270
+ /**
271
+ * User of a token, on every authenticated request and every refresh.
272
+ * Return `undefined` for a deleted, disabled or banned account: its
273
+ * requests are rejected at once, and its sessions end at their next
274
+ * refresh.
275
+ */
276
+ findUserById(userId: string): Promise<AuthUser | undefined>;
277
+ findUserIdentifiers(username: string): Promise<UserIdentifiers | undefined>;
278
+ /**
279
+ * Optional. Whether a session (`familyId`, the `sid` claim) was revoked.
280
+ * When defined, it is checked on every authenticated request, so that
281
+ * the access tokens of a revoked session (logout, refresh token reuse,
282
+ * password change) stop working at once, instead of at their expiry.
283
+ * It costs one lookup per request: see the README for implementations.
284
+ */
285
+ isSessionRevoked?(sessionId: string): Promise<boolean>;
286
+ /**
287
+ * Optional. Stores a new hash of the password of a user, computed at
288
+ * login when the stored hash has another cost than `passwordHashRounds`
289
+ * (for example after raising it). Errors are logged, and do not fail
290
+ * the login.
291
+ */
292
+ updatePasswordHash?(userId: string, passwordHash: string): Promise<void>;
293
+ }
294
+ /**
295
+ * Client applications (`client_id` claim), such as `app` or `backoffice`.
296
+ *
297
+ * The client identifier is declared by the caller at login: it does not
298
+ * prove which application sends a request. It means that the token was
299
+ * issued under the policy of that client (see `isUserRoleAllowed`).
300
+ */
301
+ interface ClientSettings {
302
+ /** Accepted client identifiers. Others are rejected at login (`400`). */
303
+ allowed: readonly string[];
304
+ /** Requires a `clientId` at login. Defaults to `false`. */
305
+ required?: boolean;
306
+ /** Checks at login that a user may get a token for a client. */
307
+ isUserRoleAllowed?: (role: string, clientId: string) => boolean;
308
+ /**
309
+ * Session lifetimes of some clients, overriding the `session` setting
310
+ * (an explicit `absoluteTimeout: undefined` removes the global limit).
311
+ *
312
+ * @example
313
+ * sessions: { backoffice: { idleTimeout: 86400, absoluteTimeout: 86400 } }
314
+ */
315
+ sessions?: Readonly<Record<string, SessionSettings>>;
316
+ }
317
+ /**
318
+ * Session lifetimes, in seconds. A session starts at login and is extended
319
+ * by each refresh.
320
+ */
321
+ interface SessionSettings {
322
+ /**
323
+ * Inactivity timeout: the session ends when it is not refreshed during
324
+ * this delay (lifetime of each refresh token). Defaults to 14 days.
325
+ */
326
+ idleTimeout?: number;
327
+ /**
328
+ * Absolute timeout: the session ends this delay after login, even when
329
+ * it is active. Defaults to none: active sessions never end.
330
+ */
331
+ absoluteTimeout?: number | undefined;
332
+ }
333
+ interface ResolvedSessionSettings {
334
+ idleTimeout: number;
335
+ absoluteTimeout: number | undefined;
336
+ }
337
+ interface AuthSettings {
6
338
  keys: KeyPairSettings;
7
339
  dataProvider: DataProvider;
340
+ /**
341
+ * Access token lifetime, in seconds. Defaults to 900 (15 minutes): the
342
+ * longest delay during which the token of a revoked session still
343
+ * works, without `DataProvider.isSessionRevoked()`.
344
+ */
345
+ accessTokenTTL?: number;
346
+ /** Session lifetimes (inactivity and absolute timeouts). */
347
+ session?: SessionSettings;
348
+ /**
349
+ * `iss` claim of the access tokens, checked on every request: for
350
+ * example the URL of the API. Use a different value per environment
351
+ * and application, especially when they share a key pair.
352
+ */
353
+ issuer: string;
354
+ /**
355
+ * `aud` claim of the access tokens: the API that accepts them, checked
356
+ * on every request. Defaults to `issuer`.
357
+ */
358
+ audience?: string;
359
+ cookies?: CookieSettings;
360
+ clients?: ClientSettings;
361
+ onLoginFailed?: (event: LoginFailedEvent) => Promise<void>;
362
+ onLoginSuccess?: (event: LoginSuccessEvent) => Promise<void>;
363
+ /**
364
+ * Login throttle (brute force protection), enabled by default. `false`
365
+ * disables it, when the infrastructure (gateway, WAF) already limits
366
+ * `/auth/login`.
367
+ */
368
+ loginThrottle?: LoginThrottleSettings | false;
369
+ /**
370
+ * Called before checking the password, after the login throttle: return
371
+ * an error (`tooManyRequestsError()`, `forbiddenError()`…) to reject the
372
+ * attempt, for example to require a CAPTCHA or to block an IP address.
373
+ */
374
+ beforeLogin?: (event: BeforeLoginEvent) => Promise<HttpError | void> | HttpError | void;
375
+ /**
376
+ * bcrypt cost of `hashPassword()`, and of the dummy hash compared when
377
+ * the user is unknown or has no password, so that every login takes the
378
+ * same time. Hashes with another cost are upgraded at login when
379
+ * `DataProvider.updatePasswordHash()` is defined. Defaults to 12.
380
+ */
381
+ passwordHashRounds?: number;
382
+ /**
383
+ * Writes the `username` claim in the access tokens. Defaults to `false`:
384
+ * a username is often an email address, readable by anyone holding the
385
+ * token (it is only base64 encoded); `sub` identifies the user.
386
+ */
387
+ usernameClaim?: boolean;
388
+ /**
389
+ * Delay, in seconds after its first use, during which a refresh token can
390
+ * be presented again without being treated as stolen: concurrent
391
+ * refreshes (several tabs) and retries after a lost response get a new
392
+ * token pair in the same session. Defaults to 10; 0 disables it.
393
+ */
394
+ refreshTokenReuseInterval?: number;
395
+ /**
396
+ * Called when a used refresh token is presented again after the reuse
397
+ * interval: the session has been revoked, as the token was probably
398
+ * stolen. Errors thrown by the hook are ignored.
399
+ */
400
+ onRefreshTokenReuse?: (event: RefreshTokenReuseEvent) => Promise<void>;
401
+ /**
402
+ * Roles of a user, checked by `requireRole()`. Defaults to its `roles`
403
+ * array, or to its `role` string.
404
+ */
405
+ getUserRoles?: (user: AuthUser) => readonly string[];
406
+ }
407
+ /** Options of `auth.init()`. */
408
+ interface AuthInitOptions {
409
+ /**
410
+ * Browser path of the auth routes, used as the default path of the
411
+ * refresh token cookie. An auth service serves a single path.
412
+ */
413
+ routesPath?: string;
414
+ /**
415
+ * CORS settings of the app (`api.cors`). When `cookies.trustedOrigins`
416
+ * is not set and CORS allows credentials, the origins allowed by CORS
417
+ * are trusted.
418
+ */
419
+ cors?: PluginCors;
420
+ }
421
+ interface ResolvedAuthSettings extends Required<Omit<AuthSettings, 'cookies' | 'clients' | 'session' | 'onLoginFailed' | 'onLoginSuccess' | 'loginThrottle' | 'beforeLogin' | 'onRefreshTokenReuse'>> {
422
+ cookies: ResolvedCookieSettings;
423
+ clients: ClientSettings | undefined;
424
+ session: ResolvedSessionSettings;
425
+ onLoginFailed: ((event: LoginFailedEvent) => Promise<void>) | undefined;
426
+ onLoginSuccess: ((event: LoginSuccessEvent) => Promise<void>) | undefined;
427
+ loginThrottle: ResolvedLoginThrottleSettings | undefined;
428
+ beforeLogin: AuthSettings['beforeLogin'];
429
+ onRefreshTokenReuse: ((event: RefreshTokenReuseEvent) => Promise<void>) | undefined;
430
+ }
431
+ interface TokenPair {
432
+ accessToken: string;
433
+ refreshToken: string;
434
+ }
435
+ interface TokenSubject {
436
+ userId: string;
437
+ /** Written in the token with the `usernameClaim` setting only. */
438
+ username?: string;
439
+ /** `client_id` claim. Must be allowed by the `clients` setting. */
440
+ clientId?: string;
441
+ }
442
+ /** Token pair issued at login or by a refresh. */
443
+ interface IssuedTokens extends TokenPair {
444
+ accessTokenExpiresAt: Date;
445
+ refreshTokenExpiresAt: Date;
446
+ /** End of the session, when an absolute timeout applies. */
447
+ sessionExpiresAt: Date | undefined;
448
+ }
449
+ interface CreatedAccessToken {
450
+ jwtId: string;
451
+ accessToken: string;
452
+ expiresAt: Date;
453
+ }
454
+ interface CreatedRefreshToken {
455
+ token: string;
456
+ tokenHash: string;
457
+ }
458
+
459
+ declare module '@dotpress/core' {
460
+ interface RequestContext {
461
+ /** Authenticated user (`authPlugin`). Use `requireAuth` to require it. */
462
+ user: AuthUser | undefined;
463
+ /** Access token data of the authenticated user (`authPlugin`). */
464
+ auth: AuthSession | undefined;
465
+ }
466
+ }
467
+
468
+ type AuthErrorCode = 'INVALID_ACCESS_TOKEN' | 'INVALID_REFRESH_TOKEN' | 'EXPIRED_REFRESH_TOKEN'
469
+ /** A used refresh token was presented again: its session is revoked. */
470
+ | 'REFRESH_TOKEN_REUSED'
471
+ /** Valid access token of a revoked session (`isSessionRevoked()`). */
472
+ | 'SESSION_REVOKED';
473
+ /**
474
+ * Error raised by the token helpers.
475
+ */
476
+ declare class AuthError extends Error {
477
+ readonly code: AuthErrorCode;
478
+ constructor(code: AuthErrorCode, options?: ErrorOptions);
479
+ }
480
+ interface Auth {
481
+ readonly settings: ResolvedAuthSettings;
482
+ /**
483
+ * Imports the keys. Fails with an explicit message on invalid keys.
484
+ * `authPlugin()` calls it at startup, with the browser path of its routes.
485
+ */
486
+ init(options?: AuthInitOptions): Promise<void>;
487
+ generateAccessToken(subject: TokenSubject): Promise<CreatedAccessToken>;
488
+ generateRefreshToken(): Promise<CreatedRefreshToken>;
489
+ /** Generates and stores a token pair, starting a session. */
490
+ generateTokensForUser(subject: TokenSubject): Promise<IssuedTokens>;
491
+ /**
492
+ * Rotates a token pair. The access token may be expired, but must be
493
+ * signed by the key pair and match the refresh token.
494
+ *
495
+ * A refresh token can only be used once. Within
496
+ * `refreshTokenReuseInterval` after its first use, it may be presented
497
+ * again (concurrent refreshes, retries after a lost response): a new pair
498
+ * is issued in the same session. After, presenting it again means it was
499
+ * probably stolen: the whole session (token family) is revoked, and
500
+ * `REFRESH_TOKEN_REUSED` is thrown.
501
+ *
502
+ * Throws `EXPIRED_REFRESH_TOKEN` when the session reached its inactivity
503
+ * or absolute timeout, and `INVALID_REFRESH_TOKEN` when
504
+ * `findUserById()` no longer returns the user (the session is revoked).
505
+ */
506
+ refreshTokens(tokens: TokenPair): Promise<IssuedTokens & {
507
+ userId: string;
508
+ user: AuthUser;
509
+ }>;
510
+ /**
511
+ * Verifies a valid (not expired) access token. With
512
+ * `DataProvider.isSessionRevoked()`, throws `SESSION_REVOKED` for the
513
+ * tokens of a revoked session.
514
+ */
515
+ verifyAccessToken(accessToken: string): Promise<AuthSession>;
516
+ /**
517
+ * Revokes the session of a refresh token (logout). The token may be
518
+ * used, revoked or expired: it still designates its session. Tokens
519
+ * stored without family (before V2) revoke every session of the user.
520
+ *
521
+ * With `scope: 'all'`, revokes every session of the user, on every
522
+ * device. The token must then be active (neither used, revoked nor
523
+ * expired).
524
+ *
525
+ * Returns `undefined`, without revoking anything, for an unknown token,
526
+ * or an inactive token with `scope: 'all'`.
527
+ */
528
+ revokeSession(refreshToken: string, options?: {
529
+ scope?: LogoutScope;
530
+ }): Promise<RevokedSession | undefined>;
531
+ /**
532
+ * End of a session, when an absolute timeout applies to it (computed
533
+ * from the `auth_time` claim).
534
+ */
535
+ getSessionExpiresAt(session: AuthSession): Date | undefined;
536
+ /**
537
+ * Public keys as a JSON Web Key Set (RFC 7517): the active key first,
538
+ * then the additional keys. Other services can verify the tokens with
539
+ * it (`createRemoteJWKSet()` of jose).
540
+ */
541
+ getJwks(): Promise<{
542
+ keys: JWK[];
543
+ }>;
544
+ /** Roles of a user, from the `getUserRoles` setting. */
545
+ getUserRoles(user: AuthUser): readonly string[];
546
+ /**
547
+ * Sets the token cookies (cookie mode only). They expire with the
548
+ * refresh token (`refreshTokenExpiresAt` of issued tokens), or after the
549
+ * inactivity timeout.
550
+ */
551
+ setAuthCookies(res: DotpressResponse, tokens: TokenPair & {
552
+ refreshTokenExpiresAt?: Date;
553
+ }, expiresInSeconds?: number): void;
554
+ clearAuthCookies(res: DotpressResponse): void;
555
+ /**
556
+ * Whether a request may be authenticated by the cookies: protection
557
+ * against cross-site request forgery (see `cookies.trustedOrigins`).
558
+ * `authPlugin()` checks it on its routes and before reading the access
559
+ * token cookie.
560
+ */
561
+ isCookieAuthenticationAllowed(req: DotpressRequest): Promise<boolean>;
562
+ /**
563
+ * Hashes a password with bcrypt. Defaults to `passwordHashRounds`.
564
+ * Throws a `RangeError` for passwords longer than 72 bytes (see
565
+ * `isPasswordTooLong()`).
566
+ */
567
+ hashPassword(password: string, saltRounds?: number): Promise<string>;
568
+ /** Whether a bcrypt hash has another cost than `passwordHashRounds`. */
569
+ passwordNeedsRehash(passwordHash: string): boolean;
570
+ /**
571
+ * Hashes a verified password with `passwordHashRounds`, to upgrade its
572
+ * stored hash. Unlike `hashPassword()`, it accepts passwords longer than
573
+ * 72 bytes, created before the check.
574
+ */
575
+ rehashPassword(password: string): Promise<string>;
576
+ /**
577
+ * Verifies a password against its bcrypt hash. Without valid hash
578
+ * (unknown user, account without password), it compares the password
579
+ * with a dummy hash and returns `false`: the answer takes the same time,
580
+ * and does not reveal whether the account exists.
581
+ */
582
+ verifyPassword(password: string, passwordHash: string | null | undefined): Promise<boolean>;
583
+ }
584
+ /**
585
+ * Creates the authentication service. It works without an app (scripts,
586
+ * workers), and is plugged into an app with `authPlugin()`.
587
+ *
588
+ * @example
589
+ * const auth = createAuth({ keys, dataProvider })
590
+ * await createApp({ adapter, plugins: [authPlugin(auth)] })
591
+ */
592
+ declare const createAuth: (input: AuthSettings) => Auth;
593
+ /** Longest password bcrypt reads, in UTF-8 bytes. */
594
+ declare const MAX_PASSWORD_BYTES = 72;
595
+ /**
596
+ * Whether a password exceeds the 72 bytes (UTF-8) read by bcrypt. Check
597
+ * it in the sign-up and password change schemas: `hashPassword()` rejects
598
+ * these passwords instead of silently truncating them.
599
+ *
600
+ * @example
601
+ * password: z.string().min(12).refine((p) => !isPasswordTooLong(p), 'Password too long')
602
+ */
603
+ declare const isPasswordTooLong: (password: string) => boolean;
604
+ /**
605
+ * Hashes a password with bcrypt (cost 12 by default). Throws a
606
+ * `RangeError` for passwords longer than 72 bytes (see
607
+ * `isPasswordTooLong()`).
608
+ */
609
+ declare const generatePasswordHash: (password: string, saltRounds?: number) => Promise<string>;
610
+
611
+ /** Context guaranteed by `requireAuth`, `requireRole()` and `requireClient()`. */
612
+ interface AuthenticatedContext {
613
+ user: AuthUser;
614
+ auth: AuthSession;
615
+ }
616
+ /**
617
+ * Requires an authenticated user (`401 UNAUTHORIZED` otherwise). In the
618
+ * handlers of the routes using it, `ctx.user` and `ctx.auth` are defined.
619
+ */
620
+ declare const requireAuth: _dotpress_core.Middleware<AuthenticatedContext>;
621
+ /**
622
+ * Requires an authenticated user with at least one of the roles
623
+ * (`401 UNAUTHORIZED` without user, `403 FORBIDDEN` without role). Roles
624
+ * come from the `getUserRoles` setting of `createAuth()`.
625
+ */
626
+ declare const requireRole: (...roles: [string, ...string[]]) => _dotpress_core.Middleware<AuthenticatedContext>;
627
+ /**
628
+ * Requires an access token issued for one of the clients (`401
629
+ * UNAUTHORIZED` without user, `403 FORBIDDEN` for another client). For
630
+ * example, keeps the tokens of a mobile app away from the backoffice
631
+ * routes. It does not prove which application sends the request: combine
632
+ * it with `requireRole()`.
633
+ */
634
+ declare const requireClient: (...clientIds: [string, ...string[]]) => _dotpress_core.Middleware<AuthenticatedContext>;
635
+
636
+ interface AuthPluginOptions {
637
+ /** Prefix of the authentication routes. Defaults to `/auth`. */
638
+ prefix?: string;
639
+ /** Registers the authentication routes. Defaults to `true`. */
640
+ routes?: boolean;
641
+ /**
642
+ * Publishes the public keys at `GET /.well-known/jwks.json`, for other
643
+ * services verifying the tokens. Defaults to `false`.
644
+ */
645
+ jwks?: boolean;
646
+ }
647
+ /** Security schemes of the plugin, for documentation generators. */
648
+ declare const AUTH_SECURITY_SCHEMES: {
649
+ readonly bearerAuth: {
650
+ readonly type: "http";
651
+ readonly scheme: "bearer";
652
+ readonly bearerFormat: "JWT";
653
+ };
654
+ };
655
+ /**
656
+ * Access token of a request: `Authorization: Bearer` header, then cookie
657
+ * (when enabled). The request body is never read.
658
+ */
659
+ declare const extractAccessToken: (req: DotpressRequest, cookies: ResolvedCookieSettings) => string | undefined;
660
+ /**
661
+ * Plugs an authentication service into an app:
662
+ *
663
+ * - reads the access token of every request, and sets `ctx.user` and
664
+ * `ctx.auth` when it is valid;
665
+ * - registers the `POST /auth/login`, `POST /auth/refresh`,
666
+ * `GET /auth/me`, `POST /auth/restore` and `POST /auth/logout` routes.
667
+ */
668
+ declare const authPlugin: (auth: Auth, options?: AuthPluginOptions) => {
669
+ readonly name: "auth";
670
+ readonly auth: Auth;
671
+ readonly securitySchemes: {
672
+ readonly bearerAuth: {
673
+ readonly type: "http";
674
+ readonly scheme: "bearer";
675
+ readonly bearerFormat: "JWT";
676
+ };
677
+ };
678
+ readonly setup: (api: _dotpress_core.PluginAPI) => Promise<void>;
8
679
  };
9
- export declare const configurePlugin: (settings: Settings) => Plugin;
10
- export declare const generatePasswordHash: (password: string, salt?: number) => Promise<string>;
11
- export { generateAccessToken, generateRefreshToken, generateTokensForUser, setAuthCookies, };
680
+
681
+ export { AUTH_SECURITY_SCHEMES, AuthError, MAX_PASSWORD_BYTES, authPlugin, createAuth, extractAccessToken, generatePasswordHash, isPasswordTooLong, memoryRateLimitStore, requireAuth, requireClient, requireRole };
682
+ export type { AdditionalPublicKey, Auth, AuthErrorCode, AuthInitOptions, AuthPluginOptions, AuthSession, AuthSettings, AuthUser, AuthenticatedContext, BeforeLoginEvent, ClientSettings, CookieSettings, CreatedAccessToken, CreatedRefreshToken, DataProvider, IssuedTokens, KeyPairSettings, LoginFailedEvent, LoginSuccessEvent, LoginThrottleSettings, LogoutScope, MemoryRateLimitStoreOptions, RateLimit, RateLimitState, RateLimitStore, RefreshTokenData, RefreshTokenReuseEvent, ResolvedAuthSettings, ResolvedCookieSettings, ResolvedLoginThrottleSettings, ResolvedSessionSettings, RevokedSession, SessionSettings, StoreRefreshTokenDto, TokenPair, TokenSubject, UserIdentifiers };