@gauts/auth 0.6.4 → 0.7.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/README.md +471 -198
- package/SECURITY.md +10 -2
- package/dist/adapters/hono/index.d.ts +28 -4
- package/dist/adapters/hono/index.d.ts.map +1 -1
- package/dist/adapters/hono/index.js +40 -7
- package/dist/adapters/hono/index.js.map +1 -1
- package/dist/adapters/hono/social.d.ts +34 -0
- package/dist/adapters/hono/social.d.ts.map +1 -0
- package/dist/adapters/hono/social.js +202 -0
- package/dist/adapters/hono/social.js.map +1 -0
- package/dist/adapters/prisma/config.d.ts.map +1 -1
- package/dist/adapters/prisma/config.js +85 -71
- package/dist/adapters/prisma/config.js.map +1 -1
- package/dist/adapters/prisma/index.d.ts +1 -1
- package/dist/adapters/prisma/index.d.ts.map +1 -1
- package/dist/adapters/prisma/index.js +90 -13
- package/dist/adapters/prisma/index.js.map +1 -1
- package/dist/adapters/prisma/model.d.ts +40 -7
- package/dist/adapters/prisma/model.d.ts.map +1 -1
- package/dist/adapters/prisma/model.js +38 -24
- package/dist/adapters/prisma/model.js.map +1 -1
- package/dist/adapters/prisma/types.d.ts +60 -72
- package/dist/adapters/prisma/types.d.ts.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/providers/common.d.ts +31 -0
- package/dist/providers/common.d.ts.map +1 -0
- package/dist/providers/common.js +89 -0
- package/dist/providers/common.js.map +1 -0
- package/dist/providers/github.d.ts +5 -0
- package/dist/providers/github.d.ts.map +1 -0
- package/dist/providers/github.js +93 -0
- package/dist/providers/github.js.map +1 -0
- package/dist/providers/google.d.ts +5 -0
- package/dist/providers/google.d.ts.map +1 -0
- package/dist/providers/google.js +58 -0
- package/dist/providers/google.js.map +1 -0
- package/dist/providers/index.d.ts +7 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +4 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/x.d.ts +5 -0
- package/dist/providers/x.d.ts.map +1 -0
- package/dist/providers/x.js +63 -0
- package/dist/providers/x.js.map +1 -0
- package/dist/session/cookie.d.ts +4 -0
- package/dist/session/cookie.d.ts.map +1 -1
- package/dist/session/cookie.js +9 -3
- package/dist/session/cookie.js.map +1 -1
- package/dist/session/guards.d.ts.map +1 -1
- package/dist/session/guards.js +7 -1
- package/dist/session/guards.js.map +1 -1
- package/dist/session/types.d.ts +6 -0
- package/dist/session/types.d.ts.map +1 -1
- package/dist/social/config.d.ts +3 -0
- package/dist/social/config.d.ts.map +1 -0
- package/dist/social/config.js +115 -0
- package/dist/social/config.js.map +1 -0
- package/dist/social/service.d.ts +18 -0
- package/dist/social/service.d.ts.map +1 -0
- package/dist/social/service.js +78 -0
- package/dist/social/service.js.map +1 -0
- package/dist/social/state.d.ts +46 -0
- package/dist/social/state.d.ts.map +1 -0
- package/dist/social/state.js +139 -0
- package/dist/social/state.js.map +1 -0
- package/dist/social/types.d.ts +75 -0
- package/dist/social/types.d.ts.map +1 -0
- package/dist/social/types.js +2 -0
- package/dist/social/types.js.map +1 -0
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -1,72 +1,144 @@
|
|
|
1
1
|
# `@gauts/auth`
|
|
2
2
|
|
|
3
|
-
Database-backed password authentication
|
|
3
|
+
Database-backed password authentication, opaque browser sessions, and optional social authentication for Node.js applications.
|
|
4
4
|
|
|
5
|
-
`@gauts/auth` provides
|
|
5
|
+
`@gauts/auth` provides password hashing, session lifecycle, secure cookies, database validation, optional short caching, Prisma persistence, Hono integration, and Google/GitHub/X OAuth. The application keeps control of credential lookup, business-specific registration data, authorization, responses, and UI.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
| Capability
|
|
10
|
-
|
|
|
11
|
-
| Argon2id password hashing
|
|
12
|
-
| bcrypt password hashing
|
|
13
|
-
| Opaque server-side sessions
|
|
14
|
-
| Database-backed validation
|
|
15
|
-
| Sliding session renewal
|
|
16
|
-
| Absolute session lifetime
|
|
17
|
-
| Signed browser cache
|
|
18
|
-
| Full User-Agent validation
|
|
19
|
-
| IP validation
|
|
20
|
-
| Platform validation
|
|
21
|
-
| Hono adapter
|
|
22
|
-
| Prisma adapter
|
|
23
|
-
| Next.js renewal adapter
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
9
|
+
| Capability | Support | Default |
|
|
10
|
+
| ------------------------------ | :-----: | ------------------- |
|
|
11
|
+
| Argon2id password hashing | ✅ | Enabled |
|
|
12
|
+
| bcrypt password hashing | ✅ | Opt-in |
|
|
13
|
+
| Opaque server-side sessions | ✅ | Enabled |
|
|
14
|
+
| Database-backed validation | ✅ | Enabled |
|
|
15
|
+
| Sliding session renewal | ✅ | Every 24 hours |
|
|
16
|
+
| Absolute session lifetime | ✅ | 30 days |
|
|
17
|
+
| Signed browser cache | ✅ | Disabled |
|
|
18
|
+
| Full User-Agent validation | ✅ | Enabled |
|
|
19
|
+
| IP validation | ✅ | Disabled |
|
|
20
|
+
| Platform validation | ✅ | Disabled |
|
|
21
|
+
| Hono adapter | ✅ | Available |
|
|
22
|
+
| Prisma adapter | ✅ | Available |
|
|
23
|
+
| Next.js renewal adapter | ✅ | Available |
|
|
24
|
+
| Google social authentication | ✅ | Opt-in |
|
|
25
|
+
| GitHub social authentication | ✅ | Opt-in |
|
|
26
|
+
| X social authentication | ✅ | Opt-in |
|
|
27
|
+
| Social account registration | ✅ | Disabled |
|
|
28
|
+
| Session listing and revocation | ✅ | Available |
|
|
29
|
+
| Token rotation | ❌ | Stable opaque token |
|
|
30
|
+
| JWT sessions | ❌ | Not used |
|
|
31
|
+
| Redis requirement | ❌ | Not required |
|
|
32
|
+
| OTP and transactional email | ❌ | Application-owned |
|
|
33
|
+
| Route roles and permissions | ❌ | Application-owned |
|
|
30
34
|
|
|
31
35
|
“Session renewal” extends the existing session expiry when activity continues. It is not a refresh-token flow and does not rotate the opaque browser token.
|
|
32
36
|
|
|
33
|
-
##
|
|
34
|
-
|
|
35
|
-
### Requirements
|
|
37
|
+
## Quick start
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
- A database adapter.
|
|
39
|
-
- Hono 4 when using the Hono adapter.
|
|
40
|
-
- Next.js 15 or newer when using the Next.js adapter.
|
|
39
|
+
The application defines its credential login, renewal, logout, protected endpoints, and social route paths. Optional social authentication exposes the Hono handler used inside the application-owned route.
|
|
41
40
|
|
|
42
|
-
###
|
|
41
|
+
### 1. Install the package
|
|
43
42
|
|
|
44
43
|
```bash
|
|
45
|
-
npm install @gauts/auth
|
|
44
|
+
npm install @gauts/auth
|
|
46
45
|
```
|
|
47
46
|
|
|
48
|
-
|
|
47
|
+
Install the peer dependencies used by each application if they are not already present:
|
|
49
48
|
|
|
50
49
|
```bash
|
|
51
|
-
npm install @
|
|
50
|
+
npm install hono @prisma/client
|
|
51
|
+
npm install next
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
### 2. Add the Prisma schema
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
The default adapter uses this fixed relationship tree:
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
```text
|
|
59
|
+
users
|
|
60
|
+
└── user_accounts
|
|
61
|
+
├── account_sessions
|
|
62
|
+
└── social_accounts (optional)
|
|
63
|
+
```
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
Add the three required models to the API schema. `password_hash` may be nullable when the same account model also supports social authentication.
|
|
66
|
+
|
|
67
|
+
```prisma
|
|
68
|
+
model users {
|
|
69
|
+
id String @id @default(uuid()) @db.VarChar(255)
|
|
70
|
+
name String @db.VarChar(255)
|
|
71
|
+
|
|
72
|
+
accounts user_accounts[]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
model user_accounts {
|
|
76
|
+
id String @id @default(uuid()) @db.VarChar(255)
|
|
77
|
+
user_id String @db.VarChar(255)
|
|
78
|
+
email String @unique @db.VarChar(255)
|
|
79
|
+
password_hash String? @db.VarChar(255)
|
|
80
|
+
|
|
81
|
+
user users @relation(fields: [user_id], references: [id], onDelete: Cascade)
|
|
82
|
+
sessions account_sessions[]
|
|
83
|
+
|
|
84
|
+
@@index([user_id])
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
model account_sessions {
|
|
88
|
+
id String @id @default(uuid()) @db.VarChar(255)
|
|
89
|
+
account_id String @db.VarChar(255)
|
|
90
|
+
token_hash String @unique @db.VarChar(64)
|
|
91
|
+
ip String? @db.VarChar(45)
|
|
92
|
+
country String? @db.VarChar(2)
|
|
93
|
+
platform String? @db.VarChar(255)
|
|
94
|
+
agent String? @db.Text
|
|
95
|
+
expires_at DateTime @db.Timestamp(0)
|
|
96
|
+
revoked_at DateTime? @db.Timestamp(0)
|
|
97
|
+
created_at DateTime @default(now()) @db.Timestamp(0)
|
|
98
|
+
updated_at DateTime? @db.Timestamp(0)
|
|
99
|
+
|
|
100
|
+
account user_accounts @relation(fields: [account_id], references: [id], onDelete: Cascade)
|
|
66
101
|
|
|
67
|
-
|
|
102
|
+
@@index([account_id])
|
|
103
|
+
@@index([expires_at])
|
|
104
|
+
@@index([revoked_at])
|
|
105
|
+
}
|
|
106
|
+
```
|
|
68
107
|
|
|
69
|
-
|
|
108
|
+
When social authentication is enabled, add this relation inside `user_accounts`:
|
|
109
|
+
|
|
110
|
+
```prisma
|
|
111
|
+
socials social_accounts[]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Then add the optional model:
|
|
115
|
+
|
|
116
|
+
```prisma
|
|
117
|
+
model social_accounts {
|
|
118
|
+
id String @id @default(uuid()) @db.VarChar(255)
|
|
119
|
+
account_id String @db.VarChar(255)
|
|
120
|
+
provider String @db.VarChar(32)
|
|
121
|
+
provider_id String @db.VarChar(255)
|
|
122
|
+
created_at DateTime @default(now()) @db.Timestamp(0)
|
|
123
|
+
|
|
124
|
+
account user_accounts @relation(fields: [account_id], references: [id], onDelete: Cascade)
|
|
125
|
+
|
|
126
|
+
@@unique([provider, provider_id])
|
|
127
|
+
@@unique([account_id, provider])
|
|
128
|
+
@@index([account_id])
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Create the migration through the application's Prisma workflow, then regenerate its client:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npx prisma migrate dev --name add_auth
|
|
136
|
+
npx prisma generate
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 3. Create the auth instance and routes
|
|
140
|
+
|
|
141
|
+
Create the API auth instance:
|
|
70
142
|
|
|
71
143
|
```ts
|
|
72
144
|
import { createHonoAuth } from "@gauts/auth/hono";
|
|
@@ -93,7 +165,7 @@ cache disabled
|
|
|
93
165
|
cookies __ses, __cac, __ren
|
|
94
166
|
```
|
|
95
167
|
|
|
96
|
-
|
|
168
|
+
Mount the application-owned login routes and the package middleware:
|
|
97
169
|
|
|
98
170
|
```ts
|
|
99
171
|
import { Hono } from "hono";
|
|
@@ -145,41 +217,82 @@ app.get("/account", auth.requireSession, (c) => {
|
|
|
145
217
|
return c.json({
|
|
146
218
|
account: c.get("account"),
|
|
147
219
|
session: c.get("session"),
|
|
220
|
+
user: c.get("user"),
|
|
148
221
|
});
|
|
149
222
|
});
|
|
150
223
|
```
|
|
151
224
|
|
|
152
225
|
When `storedHash` is missing, the package performs password work with the configured algorithm and always returns `false`. Applications do not need a dummy hash. Keep the response identical for unknown accounts and incorrect passwords.
|
|
153
226
|
|
|
154
|
-
###
|
|
227
|
+
### 4. Connect the Next.js frontend
|
|
228
|
+
|
|
229
|
+
Create the renewal adapter with the API's private URL:
|
|
155
230
|
|
|
156
231
|
```ts
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
db,
|
|
162
|
-
secret: requiredEnv("AUTH_SECRET"),
|
|
232
|
+
import { createNextAuth } from "@gauts/auth/next";
|
|
233
|
+
|
|
234
|
+
export const nextAuth = createNextAuth({
|
|
235
|
+
renewUrl: `${process.env.NEXT_PRIVATE_API_URL}/auth/renew`,
|
|
163
236
|
});
|
|
164
237
|
```
|
|
165
238
|
|
|
166
|
-
|
|
239
|
+
Call it from the Next.js middleware (proxy.ts) on protected routes:
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
import type { NextRequest } from "next/server";
|
|
243
|
+
import { NextResponse } from "next/server";
|
|
244
|
+
|
|
245
|
+
import { nextAuth } from "./lib/auth.js";
|
|
167
246
|
|
|
168
|
-
|
|
247
|
+
export const proxy = async (request: NextRequest) => {
|
|
248
|
+
const response = NextResponse.next();
|
|
249
|
+
const renewal = await nextAuth.renew({ request, response });
|
|
250
|
+
|
|
251
|
+
if (renewal.status === 401) {
|
|
252
|
+
return NextResponse.redirect(new URL("/auth/login", request.url));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (renewal.status !== null && renewal.status >= 500) {
|
|
256
|
+
return NextResponse.redirect(new URL("/maintenance", request.url));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return renewal.response;
|
|
260
|
+
};
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
The frontend does not receive `AUTH_SECRET`. The API remains responsible for session validation and every `Set-Cookie` response.
|
|
264
|
+
|
|
265
|
+
## Requirements and package entry points
|
|
266
|
+
|
|
267
|
+
- Node.js 22 or newer.
|
|
268
|
+
- A database adapter.
|
|
269
|
+
- Hono 4 when using the Hono adapter.
|
|
270
|
+
- Next.js 15 or newer when using the Next.js adapter.
|
|
271
|
+
|
|
272
|
+
`hono` and `next` are optional peer dependencies. The Prisma adapter receives the application's generated Prisma client and does not import Prisma at runtime.
|
|
273
|
+
|
|
274
|
+
| Import | Purpose |
|
|
275
|
+
| ----------------------- | --------------------------------------------------------- |
|
|
276
|
+
| `@gauts/auth` | Password service, session core, errors, and public types. |
|
|
277
|
+
| `@gauts/auth/prisma` | Prisma database adapter. |
|
|
278
|
+
| `@gauts/auth/hono` | Hono cookies, methods, and middleware. |
|
|
279
|
+
| `@gauts/auth/next` | Next.js renewal scheduling and `Set-Cookie` forwarding. |
|
|
280
|
+
| `@gauts/auth/providers` | Google, GitHub, and X OAuth providers. |
|
|
169
281
|
|
|
170
282
|
## Configuration reference
|
|
171
283
|
|
|
172
284
|
### `createHonoAuth()`
|
|
173
285
|
|
|
174
|
-
| Property | Type / allowed values |
|
|
175
|
-
| ---------- | --------------------- |
|
|
176
|
-
| `db` | `DbAdapter` |
|
|
177
|
-
| `getIp` | `HonoGetIp` | Only with IP validation
|
|
178
|
-
| `password` | `PasswordConfig` |
|
|
179
|
-
| `session` | `SessionConfig` |
|
|
180
|
-
| `cookie` | `HonoCookieConfig` |
|
|
181
|
-
| `cache` | `{ ttl: number }` |
|
|
182
|
-
| `secret` | `string` |
|
|
286
|
+
| Property | Type / allowed values | Required | Default | Description |
|
|
287
|
+
| ---------- | --------------------- | :----------------------: | ----------------- | --------------------------------------------------------------------------------------------------- |
|
|
288
|
+
| `db` | `DbAdapter` | ✅ | — | Authoritative session persistence and account loading. |
|
|
289
|
+
| `getIp` | `HonoGetIp` | Only with IP validation | Omitted | Returns the client IP from a source trusted by the application. May be synchronous or asynchronous. |
|
|
290
|
+
| `password` | `PasswordConfig` | ❌ | Argon2id defaults | Password hashing and verification configuration. |
|
|
291
|
+
| `session` | `SessionConfig` | ❌ | Session defaults | Expiry, renewal, and client validation configuration. |
|
|
292
|
+
| `cookie` | `HonoCookieConfig` | ❌ | Cookie defaults | Names, domain, path, SameSite, and Secure settings. |
|
|
293
|
+
| `cache` | `{ ttl: number }` | ❌ | Disabled | Enables the short signed browser cache. |
|
|
294
|
+
| `secret` | `string` | With `cache` or `social` | — | HMAC secret for signed authentication data. Minimum 32 UTF-8 bytes. |
|
|
295
|
+
| `social` | `SocialConfig` | ❌ | Disabled | Enables configured social providers, redirects, and optional registration. |
|
|
183
296
|
|
|
184
297
|
```ts
|
|
185
298
|
type HonoGetIp = (c: Context) => Promise<string | null | undefined> | string | null | undefined;
|
|
@@ -351,7 +464,22 @@ The cache is signed but not encrypted. Do not place passwords, password hashes,
|
|
|
351
464
|
|
|
352
465
|
### Prisma adapter
|
|
353
466
|
|
|
354
|
-
The
|
|
467
|
+
The Prisma adapter uses one fixed, predictable session relationship tree and one optional social relation:
|
|
468
|
+
|
|
469
|
+
```text
|
|
470
|
+
users
|
|
471
|
+
└── user_accounts
|
|
472
|
+
├── account_sessions
|
|
473
|
+
└── social_accounts (optional)
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
The required default delegate names are `prisma.users`, `prisma.user_accounts`, and `prisma.account_sessions`. When `prisma.social_accounts` exists, the adapter adds social persistence automatically. The fixed Prisma relation fields are:
|
|
477
|
+
|
|
478
|
+
- `user_accounts.user`;
|
|
479
|
+
- `account_sessions.account`;
|
|
480
|
+
- `social_accounts.account` when social persistence is present.
|
|
481
|
+
|
|
482
|
+
There is no relation mapping configuration. Applications may rename delegates with `table`, add payload fields with `select`, and define access conditions with `access`. Omitting the social model removes the social methods from the adapter without affecting password or session authentication.
|
|
355
483
|
|
|
356
484
|
#### Default models
|
|
357
485
|
|
|
@@ -367,6 +495,10 @@ The resolved account payload is:
|
|
|
367
495
|
{
|
|
368
496
|
email: "owner@example.com",
|
|
369
497
|
id: "account-id",
|
|
498
|
+
user: {
|
|
499
|
+
id: "user-id",
|
|
500
|
+
name: "Company name",
|
|
501
|
+
},
|
|
370
502
|
}
|
|
371
503
|
```
|
|
372
504
|
|
|
@@ -378,84 +510,65 @@ The resolved account payload is:
|
|
|
378
510
|
const db = createPrismaAdapter({
|
|
379
511
|
client: prisma,
|
|
380
512
|
models: {
|
|
381
|
-
|
|
382
|
-
select: ["
|
|
513
|
+
accounts: {
|
|
514
|
+
select: ["name", "role", "status", "timezone"],
|
|
383
515
|
access: {
|
|
384
516
|
role: ["OWNER", "ADMIN"],
|
|
385
517
|
status: ["ACTIVE"],
|
|
386
518
|
},
|
|
387
519
|
},
|
|
520
|
+
users: {
|
|
521
|
+
select: ["role", "status"],
|
|
522
|
+
access: {
|
|
523
|
+
status: ["ACTIVE", "PENDING"],
|
|
524
|
+
},
|
|
525
|
+
},
|
|
388
526
|
},
|
|
389
527
|
});
|
|
390
528
|
```
|
|
391
529
|
|
|
392
|
-
`
|
|
530
|
+
Fields used by `access` are selected internally. They enter the returned/cached payload only when they are also present in `select`.
|
|
393
531
|
|
|
394
|
-
#### Custom
|
|
532
|
+
#### Custom delegate names
|
|
395
533
|
|
|
396
|
-
Use `
|
|
534
|
+
Use `table` only when a Prisma delegate differs from the default:
|
|
397
535
|
|
|
398
536
|
```ts
|
|
399
537
|
const db = createPrismaAdapter({
|
|
400
538
|
client: prisma,
|
|
401
539
|
models: {
|
|
540
|
+
accounts: {
|
|
541
|
+
table: "admin_accounts",
|
|
542
|
+
select: ["name", "role", "status"],
|
|
543
|
+
},
|
|
402
544
|
sessions: {
|
|
403
|
-
|
|
545
|
+
table: "admin_sessions",
|
|
404
546
|
},
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
access: {
|
|
409
|
-
role: ["OWNER", "ADMIN"],
|
|
410
|
-
status: ["ACTIVE"],
|
|
411
|
-
},
|
|
412
|
-
relations: {
|
|
413
|
-
user: {
|
|
414
|
-
name: "users",
|
|
415
|
-
select: ["id", "role", "status"],
|
|
416
|
-
access: {
|
|
417
|
-
status: ["ACTIVE"],
|
|
418
|
-
},
|
|
419
|
-
},
|
|
420
|
-
},
|
|
547
|
+
// Only when social authentication is enabled.
|
|
548
|
+
socials: {
|
|
549
|
+
table: "admin_social_accounts",
|
|
421
550
|
},
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
This configuration uses `prisma.admin_sessions`, `prisma.user_accounts`, and `prisma.users`. The root relation on `admin_sessions` must still be named `account`.
|
|
427
|
-
|
|
428
|
-
When a relation name differs from its target model, the object key identifies the relation and `name` identifies the delegate:
|
|
429
|
-
|
|
430
|
-
```ts
|
|
431
|
-
relations: {
|
|
432
|
-
owner: {
|
|
433
|
-
name: "users",
|
|
434
|
-
select: ["id", "email", "status"],
|
|
435
|
-
access: {
|
|
436
|
-
status: ["ACTIVE"],
|
|
551
|
+
users: {
|
|
552
|
+
table: "admin_users",
|
|
553
|
+
select: ["role", "status"],
|
|
437
554
|
},
|
|
438
555
|
},
|
|
439
|
-
}
|
|
556
|
+
});
|
|
440
557
|
```
|
|
441
558
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
|
445
|
-
|
|
|
446
|
-
| `
|
|
447
|
-
| `models.
|
|
448
|
-
| `models.
|
|
449
|
-
| `models.
|
|
450
|
-
| `models.
|
|
451
|
-
| `models.
|
|
452
|
-
| `models.
|
|
453
|
-
| `models.account.relations[key].select`| Unique scalar field array | ❌ | `["id"]` | Fields exposed under that relation. `id` is always included. |
|
|
454
|
-
| `models.account.relations[key].access`| Scalar equality or allowed-value arrays | ❌ | `{}` | Conditions required on the related record. |
|
|
559
|
+
| Property | Type / allowed values | Required | Default | Description |
|
|
560
|
+
| ------------------------ | --------------------------------------- | :------: | -------------------- | ---------------------------------------------------------- |
|
|
561
|
+
| `client` | Generated Prisma client | ✅ | — | Prisma client containing the three required auth models. |
|
|
562
|
+
| `models.users.table` | Compatible user delegate name | ❌ | `"users"` | Overrides the user delegate. |
|
|
563
|
+
| `models.users.select` | Unique scalar field array | ❌ | `[]` | Adds payload fields; `id` and `name` are always included. |
|
|
564
|
+
| `models.users.access` | Scalar equality or allowed-value arrays | ❌ | `{}` | Conditions required on the owning user/entity. |
|
|
565
|
+
| `models.accounts.table` | Compatible account delegate name | ❌ | `"user_accounts"` | Overrides the account delegate. |
|
|
566
|
+
| `models.accounts.select` | Unique scalar field array | ❌ | `[]` | Adds payload fields; `id` and `email` are always included. |
|
|
567
|
+
| `models.accounts.access` | Scalar equality or allowed-value arrays | ❌ | `{}` | Conditions required on the authenticating account. |
|
|
568
|
+
| `models.sessions.table` | Compatible session delegate name | ❌ | `"account_sessions"` | Overrides authoritative session persistence. |
|
|
569
|
+
| `models.socials.table` | Compatible social delegate name | ❌ | `"social_accounts"` | Overrides optional provider association persistence. |
|
|
455
570
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
`select` accepts only JSON-safe scalar fields. It controls the public account payload and receives autocomplete from the generated Prisma client. Treat it as an explicit allowlist: never select passwords, password hashes, tokens, or other secrets because the cache is signed, not encrypted. Fields used only by `access` are selected for validation and removed before the account is exposed or cached.
|
|
571
|
+
`select` accepts JSON-safe scalar fields and receives autocomplete from the generated Prisma client. `password`, `hash`, `password_hash`, and `passwordHash` are rejected by both TypeScript and runtime validation. Never select tokens or other secrets because the optional cache is signed, not encrypted.
|
|
459
572
|
|
|
460
573
|
Each `access` condition is either an exact scalar value or an array of accepted values:
|
|
461
574
|
|
|
@@ -466,7 +579,7 @@ access: {
|
|
|
466
579
|
}
|
|
467
580
|
```
|
|
468
581
|
|
|
469
|
-
|
|
582
|
+
Every configured account and user condition must match. Omitting `access` applies no application-specific account restriction.
|
|
470
583
|
|
|
471
584
|
### Next.js adapter
|
|
472
585
|
|
|
@@ -492,8 +605,8 @@ export const nextAuth = createNextAuth({
|
|
|
492
605
|
credentials accepted
|
|
493
606
|
-> generate 256-bit opaque token
|
|
494
607
|
-> store SHA-256 token hash in DB
|
|
495
|
-
-> load
|
|
496
|
-
-> apply configured access rules
|
|
608
|
+
-> load current account and owning user
|
|
609
|
+
-> apply configured account/user access rules
|
|
497
610
|
-> write __ses
|
|
498
611
|
-> write __ren
|
|
499
612
|
-> optionally write __cac
|
|
@@ -516,7 +629,7 @@ session token
|
|
|
516
629
|
session token
|
|
517
630
|
-> SHA-256 hash
|
|
518
631
|
-> indexed DB lookup
|
|
519
|
-
-> validate expiry, revocation, account access,
|
|
632
|
+
-> validate expiry, revocation, account/user access, and client
|
|
520
633
|
-> clear short cache
|
|
521
634
|
-> continue
|
|
522
635
|
```
|
|
@@ -534,61 +647,6 @@ Next reads __ren
|
|
|
534
647
|
|
|
535
648
|
`auth.session.resolve()` is always DB-backed and read-only. Only explicit renewal updates database expiry.
|
|
536
649
|
|
|
537
|
-
## Prisma schema
|
|
538
|
-
|
|
539
|
-
The default Prisma adapter resolves:
|
|
540
|
-
|
|
541
|
-
```text
|
|
542
|
-
sessions -> account
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
The following is a complete MySQL/MariaDB example for email/password authentication. The account model requires `id`, `email`, and `password_hash`; the password hash is used only by the application's login query and never enters the session payload.
|
|
546
|
-
|
|
547
|
-
```prisma
|
|
548
|
-
model account {
|
|
549
|
-
id String @id @default(uuid()) @db.VarChar(255)
|
|
550
|
-
email String @unique @db.VarChar(255)
|
|
551
|
-
password_hash String @db.VarChar(255)
|
|
552
|
-
|
|
553
|
-
sessions sessions[]
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
model sessions {
|
|
557
|
-
id String @id @default(uuid()) @db.VarChar(255)
|
|
558
|
-
account_id String @db.VarChar(255)
|
|
559
|
-
token_hash String @unique @db.VarChar(64)
|
|
560
|
-
ip String? @db.VarChar(45)
|
|
561
|
-
country String? @db.VarChar(2)
|
|
562
|
-
platform String? @db.VarChar(255)
|
|
563
|
-
agent String? @db.Text
|
|
564
|
-
expires_at DateTime @db.Timestamp(0)
|
|
565
|
-
revoked_at DateTime? @db.Timestamp(0)
|
|
566
|
-
created_at DateTime @default(now()) @db.Timestamp(0)
|
|
567
|
-
updated_at DateTime? @db.Timestamp(0)
|
|
568
|
-
|
|
569
|
-
account account @relation(fields: [account_id], references: [id], onDelete: Cascade)
|
|
570
|
-
|
|
571
|
-
@@index([account_id])
|
|
572
|
-
@@index([expires_at])
|
|
573
|
-
@@index([revoked_at])
|
|
574
|
-
}
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
Required fields and relation names:
|
|
578
|
-
|
|
579
|
-
| Path | Required fields |
|
|
580
|
-
| ------------------ | ------------------------------------------------------------------------------------------------------------------- |
|
|
581
|
-
| Session model | `id`, `account_id`, `token_hash`, `ip`, `platform`, `agent`, `expires_at`, `revoked_at`, `created_at`, `updated_at` |
|
|
582
|
-
| `account` relation | String `id`, `email`, and `password_hash` |
|
|
583
|
-
|
|
584
|
-
The session relation must be named `account`. Additional account fields and nested relations are selected through `models.account`. Application models may add fields, indexes, defaults, mappings, and relations. Access fields may use Prisma enums, strings, booleans, numbers, or null.
|
|
585
|
-
|
|
586
|
-
Keep `agent` large enough for the complete User-Agent. Use provider-compatible native annotations when the database is not MySQL/MariaDB.
|
|
587
|
-
|
|
588
|
-
`models.sessions.name` and every model `name` are Prisma client delegate names. Physical SQL table names remain an application concern and can use Prisma `@@map` without changing the adapter configuration.
|
|
589
|
-
|
|
590
|
-
Create and run migrations through the application's Prisma workflow. The package never manages migrations.
|
|
591
|
-
|
|
592
650
|
## Hono adapter
|
|
593
651
|
|
|
594
652
|
### Request values
|
|
@@ -598,11 +656,17 @@ Create and run migrations through the application's Prisma workflow. The package
|
|
|
598
656
|
```ts
|
|
599
657
|
const account = c.get("account");
|
|
600
658
|
const session = c.get("session");
|
|
659
|
+
const user = c.get("user");
|
|
601
660
|
```
|
|
602
661
|
|
|
603
662
|
```ts
|
|
604
663
|
type AuthAccount = {
|
|
664
|
+
email: string;
|
|
605
665
|
id: string;
|
|
666
|
+
user: {
|
|
667
|
+
id: string;
|
|
668
|
+
name: string;
|
|
669
|
+
};
|
|
606
670
|
};
|
|
607
671
|
|
|
608
672
|
type Session = {
|
|
@@ -619,9 +683,9 @@ type Session = {
|
|
|
619
683
|
};
|
|
620
684
|
```
|
|
621
685
|
|
|
622
|
-
The Prisma adapter refines `
|
|
686
|
+
The Prisma adapter refines `account` and `user` with the exact additional scalar fields declared in their respective `select` arrays.
|
|
623
687
|
|
|
624
|
-
Only `account_id` is copied
|
|
688
|
+
Only `account_id` is copied into the session row. Current account/user data is loaded through the fixed database relations and is never duplicated in the session table.
|
|
625
689
|
|
|
626
690
|
### Login country metadata
|
|
627
691
|
|
|
@@ -639,18 +703,199 @@ await auth.createSession({
|
|
|
639
703
|
|
|
640
704
|
### Methods
|
|
641
705
|
|
|
642
|
-
| Method
|
|
643
|
-
|
|
|
706
|
+
| Method | Purpose |
|
|
707
|
+
| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
644
708
|
| `auth.createSession({ account_id, context, country? })` | Creates the DB session and writes the browser cookies. `country` is optional login-time metadata. |
|
|
645
|
-
| `auth.resolveSession(context)`
|
|
646
|
-
| `auth.renewSession(context)`
|
|
647
|
-
| `auth.revokeSession(context)`
|
|
648
|
-
| `auth.clearSession(context)`
|
|
649
|
-
| `auth.getToken(context)`
|
|
650
|
-
| `auth.requireSession`
|
|
709
|
+
| `auth.resolveSession(context)` | Resolves a request and returns the selected account and session. |
|
|
710
|
+
| `auth.renewSession(context)` | Performs DB validation, renews when due, and writes authoritative cookies. |
|
|
711
|
+
| `auth.revokeSession(context)` | Revokes the current DB session and clears cookies. |
|
|
712
|
+
| `auth.clearSession(context)` | Clears browser cookies without revoking the DB session. |
|
|
713
|
+
| `auth.getToken(context)` | Returns the validated opaque token from the request cookie. |
|
|
714
|
+
| `auth.requireSession` | Hono middleware that authenticates and populates the context. |
|
|
651
715
|
|
|
652
716
|
`requireSession` authenticates only. Application-specific route permissions remain the application's responsibility.
|
|
653
717
|
|
|
718
|
+
## Social authentication
|
|
719
|
+
|
|
720
|
+
Social authentication is disabled unless `social` is configured. Import providers separately so applications only include the providers they use:
|
|
721
|
+
|
|
722
|
+
Social authentication requires the optional `social_accounts` model documented in the schema setup. Without that model, `createPrismaAdapter()` remains a session-only adapter and configuring `social` fails during application startup.
|
|
723
|
+
|
|
724
|
+
```ts
|
|
725
|
+
import { createHonoAuth } from "@gauts/auth/hono";
|
|
726
|
+
import { createPrismaAdapter } from "@gauts/auth/prisma";
|
|
727
|
+
import { github, google, x } from "@gauts/auth/providers";
|
|
728
|
+
|
|
729
|
+
export const auth = createHonoAuth({
|
|
730
|
+
db: createPrismaAdapter({ client: prisma }),
|
|
731
|
+
secret: requiredEnv("AUTH_SECRET"),
|
|
732
|
+
social: {
|
|
733
|
+
errorUrl: "https://app.example.com/auth/login",
|
|
734
|
+
providers: [
|
|
735
|
+
google({
|
|
736
|
+
callbackUrl: "https://app.example.com/proxy/auth/social/google/callback",
|
|
737
|
+
clientId: requiredEnv("GOOGLE_CLIENT_ID"),
|
|
738
|
+
clientSecret: requiredEnv("GOOGLE_CLIENT_SECRET"),
|
|
739
|
+
}),
|
|
740
|
+
github({
|
|
741
|
+
callbackUrl: "https://app.example.com/proxy/auth/social/github/callback",
|
|
742
|
+
clientId: requiredEnv("GITHUB_CLIENT_ID"),
|
|
743
|
+
clientSecret: requiredEnv("GITHUB_CLIENT_SECRET"),
|
|
744
|
+
}),
|
|
745
|
+
x({
|
|
746
|
+
callbackUrl: "https://app.example.com/proxy/auth/social/x/callback",
|
|
747
|
+
clientId: requiredEnv("X_CLIENT_ID"),
|
|
748
|
+
clientSecret: requiredEnv("X_CLIENT_SECRET"),
|
|
749
|
+
}),
|
|
750
|
+
],
|
|
751
|
+
successUrl: "https://app.example.com/dashboard",
|
|
752
|
+
},
|
|
753
|
+
});
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
Declare one application route for every configured provider and supported action:
|
|
757
|
+
|
|
758
|
+
```ts
|
|
759
|
+
app.get("/auth/social/:provider/:action", async (c) => {
|
|
760
|
+
return auth.social.handle(c);
|
|
761
|
+
});
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
The same route handles the explicit `start` and `callback` paths:
|
|
765
|
+
|
|
766
|
+
```text
|
|
767
|
+
GET /proxy/auth/social/google/start?intent=login
|
|
768
|
+
GET /proxy/auth/social/google/start?intent=register
|
|
769
|
+
GET /proxy/auth/social/google/callback
|
|
770
|
+
GET /proxy/auth/social/github/start?intent=login
|
|
771
|
+
GET /proxy/auth/social/github/callback
|
|
772
|
+
GET /proxy/auth/social/x/start?intent=login
|
|
773
|
+
GET /proxy/auth/social/x/callback
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
`intent` defaults to `login`. Only `start` and `callback` are accepted as actions. The public `/proxy` path in this example is expected to forward to the Hono `/auth` path. Provider callback URLs must exactly match the public callback paths registered with each provider.
|
|
777
|
+
|
|
778
|
+
The package does not create or mount routes. Applications may wrap `auth.social.handle(c)` with their own logging, metrics, rate limiting, or other route-level behavior.
|
|
779
|
+
|
|
780
|
+
### Social configuration
|
|
781
|
+
|
|
782
|
+
| Property | Type / allowed values | Required | Default | Description |
|
|
783
|
+
| ---------------------------- | ------------------------------------ | :---------: | --------- | -------------------------------------------------------------------------- |
|
|
784
|
+
| `social.providers` | `SocialProvider[]` | ✅ | — | Configured Google, GitHub, or X providers. |
|
|
785
|
+
| `social.successUrl` | Absolute HTTP(S) URL | ✅ | — | Fixed redirect after session creation. |
|
|
786
|
+
| `social.errorUrl` | Absolute HTTP(S) URL | ✅ | — | Fixed redirect for expected provider/authentication failures. |
|
|
787
|
+
| `social.cookieName` | Valid cookie name | ❌ | `"__soc"` | Signed temporary OAuth/registration transaction cookie. |
|
|
788
|
+
| `social.registration` | `SocialRegistrationConfig` | ❌ | Disabled | Enables default or application-specific social registration. |
|
|
789
|
+
| `registration.registerUrl` | Absolute HTTP(S) URL | ❌ | Direct | Defers account creation to an application form. |
|
|
790
|
+
| `registration.createAccount` | Async callback returning `accountId` | Conditional | Built-in | Creates required business data when the default structure is insufficient. |
|
|
791
|
+
|
|
792
|
+
Provider configuration:
|
|
793
|
+
|
|
794
|
+
| Property | Type | Required | Description |
|
|
795
|
+
| -------------- | -------------------- | :------: | ------------------------------------------------------- |
|
|
796
|
+
| `clientId` | Non-empty string | ✅ | Public OAuth client identifier. |
|
|
797
|
+
| `clientSecret` | Non-empty string | ✅ | Server-only OAuth client secret. |
|
|
798
|
+
| `callbackUrl` | Absolute HTTP(S) URL | ✅ | Exact public callback URL registered with the provider. |
|
|
799
|
+
|
|
800
|
+
The normalized verified identity is:
|
|
801
|
+
|
|
802
|
+
```ts
|
|
803
|
+
type SocialIdentity = {
|
|
804
|
+
avatarUrl: string | null;
|
|
805
|
+
email: string;
|
|
806
|
+
name: string;
|
|
807
|
+
provider: "google" | "github" | "x";
|
|
808
|
+
providerId: string;
|
|
809
|
+
username: string | null;
|
|
810
|
+
};
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
Provider access/refresh tokens and raw provider profiles are never exposed to application callbacks or persisted.
|
|
814
|
+
|
|
815
|
+
### Registration modes
|
|
816
|
+
|
|
817
|
+
Login only (default):
|
|
818
|
+
|
|
819
|
+
```ts
|
|
820
|
+
social: {
|
|
821
|
+
errorUrl,
|
|
822
|
+
providers: [googleProvider],
|
|
823
|
+
successUrl,
|
|
824
|
+
}
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
Default registration creates `users { id, name }`, `user_accounts { id, email, user_id }`, the provider link, and the session:
|
|
828
|
+
|
|
829
|
+
```ts
|
|
830
|
+
social: {
|
|
831
|
+
errorUrl,
|
|
832
|
+
providers: [googleProvider],
|
|
833
|
+
registration: {},
|
|
834
|
+
successUrl,
|
|
835
|
+
}
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
This requires every additional application column on `users` and `user_accounts` to be nullable or have a database default.
|
|
839
|
+
|
|
840
|
+
For additional required form data, configure a registration URL and callback:
|
|
841
|
+
|
|
842
|
+
```ts
|
|
843
|
+
import type { SocialRegistrationInput } from "@gauts/auth";
|
|
844
|
+
|
|
845
|
+
type RegisterData = {
|
|
846
|
+
companyNumber: string;
|
|
847
|
+
};
|
|
848
|
+
|
|
849
|
+
social: {
|
|
850
|
+
errorUrl,
|
|
851
|
+
providers: [googleProvider],
|
|
852
|
+
registration: {
|
|
853
|
+
registerUrl: "https://app.example.com/auth/register/social",
|
|
854
|
+
createAccount: async ({ data, identity }: SocialRegistrationInput<RegisterData>) => {
|
|
855
|
+
const account = await createApplicationAccount({
|
|
856
|
+
companyNumber: data.companyNumber,
|
|
857
|
+
email: identity.email,
|
|
858
|
+
name: identity.name,
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
return { accountId: account.id };
|
|
862
|
+
},
|
|
863
|
+
},
|
|
864
|
+
successUrl,
|
|
865
|
+
}
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
The application endpoint reads the verified identity and completes registration:
|
|
869
|
+
|
|
870
|
+
```ts
|
|
871
|
+
app.get("/auth/register/social", (c) => {
|
|
872
|
+
return c.json(auth.social.getRegistration(c));
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
app.post("/auth/register/social", async (c) => {
|
|
876
|
+
const data = await c.req.json<{ companyNumber: string }>();
|
|
877
|
+
|
|
878
|
+
await auth.social.completeRegistration({
|
|
879
|
+
context: c,
|
|
880
|
+
data,
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
return c.json({ registered: true });
|
|
884
|
+
});
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
`registerUrl` requires `createAccount`; otherwise submitted application data would have no owner. `createAccount` must create the fixed `users`/`user_accounts` relation and return the created account ID. The package then creates `social_accounts` and the authenticated session.
|
|
888
|
+
|
|
889
|
+
### OAuth security
|
|
890
|
+
|
|
891
|
+
- Authorization Code flow with PKCE `S256` is used for every provider.
|
|
892
|
+
- State and the PKCE verifier live in the signed, HttpOnly `__soc` cookie for at most 10 minutes.
|
|
893
|
+
- The temporary cookie is cleared after success or expected failure.
|
|
894
|
+
- Only provider-verified email addresses are accepted.
|
|
895
|
+
- Provider IDs, not email addresses, are the stable social link identifiers.
|
|
896
|
+
- Redirect URLs come only from startup configuration; request query parameters cannot choose them.
|
|
897
|
+
- Social OAuth requires `SameSite=Lax` or `SameSite=None`; `Strict` fails during startup.
|
|
898
|
+
|
|
654
899
|
### Core and adapter composition
|
|
655
900
|
|
|
656
901
|
`createHonoAuth()` is the normal entry point. Use separate composition only when the same core instance is required outside Hono:
|
|
@@ -759,11 +1004,27 @@ const db = {
|
|
|
759
1004
|
|
|
760
1005
|
`findToken` receives only the SHA-256 token hash. It must return the current selected account plus an `allowed` result. Raw tokens must never be persisted.
|
|
761
1006
|
|
|
1007
|
+
Custom adapters used with `social` must additionally implement `SocialDbAdapter`:
|
|
1008
|
+
|
|
1009
|
+
```ts
|
|
1010
|
+
import type { SocialDbAdapter } from "@gauts/auth";
|
|
1011
|
+
|
|
1012
|
+
const socialDb = {
|
|
1013
|
+
createAccount: async ({ email, name }) => accountId,
|
|
1014
|
+
createSocial: async (record) => {},
|
|
1015
|
+
findAccount: async (account_id) => null,
|
|
1016
|
+
findEmail: async (email) => null,
|
|
1017
|
+
findSocial: async ({ provider, provider_id }) => null,
|
|
1018
|
+
} satisfies SocialDbAdapter;
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
The Prisma adapter already implements both `DbAdapter` and `SocialDbAdapter`.
|
|
1022
|
+
|
|
762
1023
|
## Performance
|
|
763
1024
|
|
|
764
1025
|
The package contains no Redis or in-process cache.
|
|
765
1026
|
|
|
766
|
-
Without the optional browser cache, each `requireSession` performs an indexed database lookup through `
|
|
1027
|
+
Without the optional browser cache, each `requireSession` performs an indexed database lookup through `account_sessions.token_hash`.
|
|
767
1028
|
|
|
768
1029
|
With a valid cache, `GET` and `HEAD` skip the lookup until `cache.ttl` expires. Unsafe methods always use current database state.
|
|
769
1030
|
|
|
@@ -774,6 +1035,12 @@ The tradeoff is explicit: revocation and selected account or relation changes ma
|
|
|
774
1035
|
```ts
|
|
775
1036
|
type AuthErrorCode =
|
|
776
1037
|
| "AUTH_CONFIG_INVALID"
|
|
1038
|
+
| "SOCIAL_ACCOUNT_INVALID"
|
|
1039
|
+
| "SOCIAL_ACCOUNT_NOT_FOUND"
|
|
1040
|
+
| "SOCIAL_EMAIL_INVALID"
|
|
1041
|
+
| "SOCIAL_PROVIDER_ERROR"
|
|
1042
|
+
| "SOCIAL_REGISTRATION_INVALID"
|
|
1043
|
+
| "SOCIAL_STATE_INVALID"
|
|
777
1044
|
| "PASSWORD_INPUT_INVALID"
|
|
778
1045
|
| "SESSION_CLIENT_MISMATCH"
|
|
779
1046
|
| "SESSION_DATA_INVALID"
|
|
@@ -784,15 +1051,21 @@ type AuthErrorCode =
|
|
|
784
1051
|
|
|
785
1052
|
Use `isAuthError(error)` before reading `error.code`.
|
|
786
1053
|
|
|
787
|
-
| Code
|
|
788
|
-
|
|
|
789
|
-
| `AUTH_CONFIG_INVALID`
|
|
790
|
-
| `
|
|
791
|
-
| `
|
|
792
|
-
| `
|
|
793
|
-
| `
|
|
794
|
-
| `
|
|
795
|
-
| `
|
|
1054
|
+
| Code | Suggested HTTP status | Meaning |
|
|
1055
|
+
| ----------------------------- | --------------------: | ----------------------------------------------------------------- |
|
|
1056
|
+
| `AUTH_CONFIG_INVALID` | `500` | Invalid startup configuration. |
|
|
1057
|
+
| `SOCIAL_ACCOUNT_INVALID` | `403` | Linked account or owning user failed configured access rules. |
|
|
1058
|
+
| `SOCIAL_ACCOUNT_NOT_FOUND` | `401` | Provider identity is not linked and registration is unavailable. |
|
|
1059
|
+
| `SOCIAL_EMAIL_INVALID` | `400` | Provider did not return a verified usable email address. |
|
|
1060
|
+
| `SOCIAL_PROVIDER_ERROR` | `401` | Provider denied or failed the OAuth exchange. |
|
|
1061
|
+
| `SOCIAL_REGISTRATION_INVALID` | `400` | Custom registration did not return a valid account ID. |
|
|
1062
|
+
| `SOCIAL_STATE_INVALID` | `400` | OAuth/registration state is missing, altered, or expired. |
|
|
1063
|
+
| `PASSWORD_INPUT_INVALID` | `400` | Password input violates configured limits. |
|
|
1064
|
+
| `SESSION_CLIENT_MISMATCH` | `403` | A configured client field does not match; the session is revoked. |
|
|
1065
|
+
| `SESSION_DATA_INVALID` | `400` | Invalid session or renewal data. |
|
|
1066
|
+
| `SESSION_INVALID` | `401` | Missing, expired, revoked, or unknown session. |
|
|
1067
|
+
| `SESSION_NOT_FOUND` | `404` | Requested session does not exist for the account. |
|
|
1068
|
+
| `DB_UNAVAILABLE` | `503` | Database operation failed. Authentication fails closed. |
|
|
796
1069
|
|
|
797
1070
|
The package throws typed errors but does not choose application HTTP responses.
|
|
798
1071
|
|