@gauts/auth 0.8.2 → 0.9.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 +292 -66
- package/dist/adapters/drizzle/config.d.ts +11 -0
- package/dist/adapters/drizzle/config.d.ts.map +1 -0
- package/dist/adapters/drizzle/config.js +147 -0
- package/dist/adapters/drizzle/config.js.map +1 -0
- package/dist/adapters/drizzle/index.d.ts +4 -0
- package/dist/adapters/drizzle/index.d.ts.map +1 -0
- package/dist/adapters/drizzle/index.js +156 -0
- package/dist/adapters/drizzle/index.js.map +1 -0
- package/dist/adapters/drizzle/model.d.ts +21 -0
- package/dist/adapters/drizzle/model.d.ts.map +1 -0
- package/dist/adapters/drizzle/model.js +61 -0
- package/dist/adapters/drizzle/model.js.map +1 -0
- package/dist/adapters/drizzle/schema.d.ts +311 -0
- package/dist/adapters/drizzle/schema.d.ts.map +1 -0
- package/dist/adapters/drizzle/schema.js +37 -0
- package/dist/adapters/drizzle/schema.js.map +1 -0
- package/dist/adapters/drizzle/types.d.ts +109 -0
- package/dist/adapters/drizzle/types.d.ts.map +1 -0
- package/dist/adapters/drizzle/types.js +2 -0
- package/dist/adapters/drizzle/types.js.map +1 -0
- package/dist/adapters/express/index.d.ts +83 -0
- package/dist/adapters/express/index.d.ts.map +1 -0
- package/dist/adapters/express/index.js +72 -0
- package/dist/adapters/express/index.js.map +1 -0
- package/dist/adapters/express/social.d.ts +29 -0
- package/dist/adapters/express/social.d.ts.map +1 -0
- package/dist/adapters/express/social.js +44 -0
- package/dist/adapters/express/social.js.map +1 -0
- package/dist/adapters/fastify/index.d.ts +85 -0
- package/dist/adapters/fastify/index.d.ts.map +1 -0
- package/dist/adapters/fastify/index.js +100 -0
- package/dist/adapters/fastify/index.js.map +1 -0
- package/dist/adapters/fastify/social.d.ts +27 -0
- package/dist/adapters/fastify/social.d.ts.map +1 -0
- package/dist/adapters/fastify/social.js +48 -0
- package/dist/adapters/fastify/social.js.map +1 -0
- package/dist/adapters/hono/index.d.ts +4 -11
- package/dist/adapters/hono/index.d.ts.map +1 -1
- package/dist/adapters/hono/index.js +31 -223
- package/dist/adapters/hono/index.js.map +1 -1
- package/dist/adapters/hono/social.d.ts +4 -10
- package/dist/adapters/hono/social.d.ts.map +1 -1
- package/dist/adapters/hono/social.js +25 -221
- package/dist/adapters/hono/social.js.map +1 -1
- package/dist/adapters/model.d.ts +33 -0
- package/dist/adapters/model.d.ts.map +1 -0
- package/dist/adapters/model.js +87 -0
- package/dist/adapters/model.js.map +1 -0
- package/dist/adapters/prisma/config.d.ts.map +1 -1
- package/dist/adapters/prisma/config.js +1 -4
- package/dist/adapters/prisma/config.js.map +1 -1
- package/dist/adapters/prisma/model.d.ts +7 -8
- package/dist/adapters/prisma/model.d.ts.map +1 -1
- package/dist/adapters/prisma/model.js +11 -79
- package/dist/adapters/prisma/model.js.map +1 -1
- package/dist/adapters/utils/cookie.d.ts +17 -0
- package/dist/adapters/utils/cookie.d.ts.map +1 -0
- package/dist/adapters/utils/cookie.js +59 -0
- package/dist/adapters/utils/cookie.js.map +1 -0
- package/dist/adapters/utils/session.d.ts +4 -0
- package/dist/adapters/utils/session.d.ts.map +1 -0
- package/dist/adapters/utils/session.js +216 -0
- package/dist/adapters/utils/session.js.map +1 -0
- package/dist/adapters/utils/social.d.ts +51 -0
- package/dist/adapters/utils/social.d.ts.map +1 -0
- package/dist/adapters/utils/social.js +278 -0
- package/dist/adapters/utils/social.js.map +1 -0
- package/dist/adapters/utils/types.d.ts +67 -0
- package/dist/adapters/utils/types.d.ts.map +1 -0
- package/dist/adapters/utils/types.js +2 -0
- package/dist/adapters/utils/types.js.map +1 -0
- package/package.json +40 -3
- package/src/adapters/drizzle/schema.ts +47 -0
- package/src/adapters/prisma/schema.prisma +38 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Database-backed password authentication, opaque browser sessions, and optional social authentication for Node.js applications.
|
|
4
4
|
|
|
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.
|
|
5
|
+
`@gauts/auth` provides password hashing, session lifecycle, secure cookies, database validation, optional short caching, Prisma or Drizzle persistence, Hono/Express/Fastify 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
|
|
|
@@ -19,7 +19,10 @@ Database-backed password authentication, opaque browser sessions, and optional s
|
|
|
19
19
|
| IP validation | ✅ | Disabled |
|
|
20
20
|
| Platform validation | ✅ | Disabled |
|
|
21
21
|
| Hono adapter | ✅ | Available |
|
|
22
|
+
| Express adapter | ✅ | Available |
|
|
23
|
+
| Fastify adapter | ✅ | Available |
|
|
22
24
|
| Prisma adapter | ✅ | Available |
|
|
25
|
+
| Drizzle MySQL/MariaDB adapter | ✅ | Available |
|
|
23
26
|
| Next.js renewal adapter | ✅ | Available |
|
|
24
27
|
| Google social authentication | ✅ | Opt-in |
|
|
25
28
|
| GitHub social authentication | ✅ | Opt-in |
|
|
@@ -40,25 +43,38 @@ The application defines its credential login, renewal, logout, and protected end
|
|
|
40
43
|
|
|
41
44
|
### 1. Install the package
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
Install the package and the framework used by the API. Only one framework adapter is required:
|
|
44
47
|
|
|
45
48
|
```bash
|
|
46
|
-
npm install @gauts/auth
|
|
47
|
-
npm install
|
|
49
|
+
npm install @gauts/auth hono
|
|
50
|
+
npm install @gauts/auth express
|
|
51
|
+
npm install @gauts/auth fastify
|
|
48
52
|
```
|
|
49
53
|
|
|
50
|
-
When the Next.js frontend is a separate project, run
|
|
54
|
+
When the Next.js frontend is a separate project, run this command inside the frontend project:
|
|
51
55
|
|
|
52
56
|
```bash
|
|
53
|
-
npm install @gauts/auth
|
|
54
|
-
npm install next
|
|
57
|
+
npm install @gauts/auth next
|
|
55
58
|
```
|
|
56
59
|
|
|
57
|
-
`hono`,
|
|
60
|
+
Run only the matching API command. `hono`, `express`, `fastify`, and `next` only need to be installed when the corresponding project does not already provide them.
|
|
58
61
|
|
|
59
|
-
|
|
62
|
+
Express TypeScript projects also install its type declarations:
|
|
60
63
|
|
|
61
|
-
|
|
64
|
+
```bash
|
|
65
|
+
npm install --save-dev @types/express
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 2. Choose the database adapter and schema
|
|
69
|
+
|
|
70
|
+
One database adapter is required. Prisma and Drizzle are equivalent persistence choices; install and configure only one.
|
|
71
|
+
|
|
72
|
+
| Adapter | Database support | Setup |
|
|
73
|
+
| ------- | -------------------------- | ---------------------------------------------- |
|
|
74
|
+
| Prisma | Prisma-supported databases | [Use Prisma](#option-a--prisma) |
|
|
75
|
+
| Drizzle | MySQL and MariaDB | [Use Drizzle](#option-b--drizzle-mysqlmariadb) |
|
|
76
|
+
|
|
77
|
+
Both schemas use the same relationship tree:
|
|
62
78
|
|
|
63
79
|
```text
|
|
64
80
|
users
|
|
@@ -66,70 +82,78 @@ users
|
|
|
66
82
|
└── account_sessions
|
|
67
83
|
```
|
|
68
84
|
|
|
69
|
-
|
|
85
|
+
Social authentication is an optional addon with separate schema instructions later in this README.
|
|
70
86
|
|
|
71
|
-
|
|
72
|
-
model users {
|
|
73
|
-
id String @id @default(uuid()) @db.VarChar(255)
|
|
74
|
-
name String @db.VarChar(255)
|
|
87
|
+
#### Option A — Prisma
|
|
75
88
|
|
|
76
|
-
|
|
77
|
-
}
|
|
89
|
+
Install Prisma inside the API project:
|
|
78
90
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
password_hash String? @db.VarChar(255)
|
|
91
|
+
```bash
|
|
92
|
+
npm install @prisma/client
|
|
93
|
+
npm install --save-dev prisma
|
|
94
|
+
```
|
|
84
95
|
|
|
85
|
-
|
|
86
|
-
sessions account_sessions[]
|
|
96
|
+
[View and copy the Prisma schema](./src/adapters/prisma/schema.prisma) into the application's Prisma schema.
|
|
87
97
|
|
|
88
|
-
|
|
89
|
-
}
|
|
98
|
+
Create the migration through the application's Prisma workflow, then regenerate its client:
|
|
90
99
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
ip String? @db.VarChar(45)
|
|
96
|
-
country String? @db.VarChar(2)
|
|
97
|
-
platform String? @db.VarChar(255)
|
|
98
|
-
agent String? @db.Text
|
|
99
|
-
expires_at DateTime @db.Timestamp(0)
|
|
100
|
-
revoked_at DateTime? @db.Timestamp(0)
|
|
101
|
-
created_at DateTime @default(now()) @db.Timestamp(0)
|
|
102
|
-
updated_at DateTime? @db.Timestamp(0)
|
|
100
|
+
```bash
|
|
101
|
+
npx prisma migrate dev --name add_auth
|
|
102
|
+
npx prisma generate
|
|
103
|
+
```
|
|
103
104
|
|
|
104
|
-
|
|
105
|
+
Create the database adapter:
|
|
105
106
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
107
|
+
```ts
|
|
108
|
+
import { createPrismaAdapter } from "@gauts/auth/prisma";
|
|
109
|
+
|
|
110
|
+
import { prisma } from "./db.js";
|
|
111
|
+
|
|
112
|
+
export const authDb = createPrismaAdapter({
|
|
113
|
+
client: prisma,
|
|
114
|
+
});
|
|
110
115
|
```
|
|
111
116
|
|
|
112
|
-
|
|
117
|
+
#### Option B — Drizzle MySQL/MariaDB
|
|
118
|
+
|
|
119
|
+
Install Drizzle and the application's MySQL driver inside the API project:
|
|
113
120
|
|
|
114
121
|
```bash
|
|
115
|
-
|
|
116
|
-
|
|
122
|
+
npm install drizzle-orm mysql2
|
|
123
|
+
npm install --save-dev drizzle-kit
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
[View and copy the Drizzle MySQL/MariaDB schema](./src/adapters/drizzle/schema.ts) into the application.
|
|
127
|
+
|
|
128
|
+
Create and apply the migration through the application's Drizzle workflow, then create the database adapter:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import { createDrizzleAdapter } from "@gauts/auth/drizzle";
|
|
132
|
+
|
|
133
|
+
import { db } from "./db.js";
|
|
134
|
+
import { accountSessions, userAccounts, users } from "./schema.js";
|
|
135
|
+
|
|
136
|
+
export const authDb = createDrizzleAdapter({
|
|
137
|
+
client: db,
|
|
138
|
+
models: {
|
|
139
|
+
accounts: { table: userAccounts },
|
|
140
|
+
sessions: { table: accountSessions },
|
|
141
|
+
users: { table: users },
|
|
142
|
+
},
|
|
143
|
+
});
|
|
117
144
|
```
|
|
118
145
|
|
|
119
146
|
### 3. Create the auth instance and routes
|
|
120
147
|
|
|
121
|
-
|
|
148
|
+
This Hono example is identical for Prisma and Drizzle:
|
|
122
149
|
|
|
123
150
|
```ts
|
|
124
151
|
import { createHonoAuth } from "@gauts/auth/hono";
|
|
125
|
-
import { createPrismaAdapter } from "@gauts/auth/prisma";
|
|
126
152
|
|
|
127
|
-
import {
|
|
153
|
+
import { authDb } from "./authDb.js";
|
|
128
154
|
|
|
129
155
|
export const auth = createHonoAuth({
|
|
130
|
-
db:
|
|
131
|
-
client: prisma,
|
|
132
|
-
}),
|
|
156
|
+
db: authDb,
|
|
133
157
|
});
|
|
134
158
|
```
|
|
135
159
|
|
|
@@ -202,6 +226,16 @@ app.get("/account", auth.requireSession, (c) => {
|
|
|
202
226
|
});
|
|
203
227
|
```
|
|
204
228
|
|
|
229
|
+
The equivalent framework examples are available here:
|
|
230
|
+
|
|
231
|
+
| Framework | Simple example | Advanced example |
|
|
232
|
+
| --------- | --------------------------------------------------------------- | ------------------------------------------------------------------- |
|
|
233
|
+
| Hono | [`examples/hono/simple`](./examples/hono/simple/index.ts) | [`examples/hono/advanced`](./examples/hono/advanced/index.ts) |
|
|
234
|
+
| Express | [`examples/express/simple`](./examples/express/simple/index.ts) | [`examples/express/advanced`](./examples/express/advanced/index.ts) |
|
|
235
|
+
| Fastify | [`examples/fastify/simple`](./examples/fastify/simple/index.ts) | [`examples/fastify/advanced`](./examples/fastify/advanced/index.ts) |
|
|
236
|
+
|
|
237
|
+
Express exposes authenticated values through `response.locals`. Fastify exposes them through request decorators and requires one `auth.decorate(app)` call before routes are registered. All routes remain application-owned.
|
|
238
|
+
|
|
205
239
|
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.
|
|
206
240
|
|
|
207
241
|
### 4. Connect the Next.js frontend
|
|
@@ -221,7 +255,6 @@ Call it from the Next.js middleware (proxy.ts) on protected routes:
|
|
|
221
255
|
```ts
|
|
222
256
|
import type { NextRequest } from "next/server";
|
|
223
257
|
import { NextResponse } from "next/server";
|
|
224
|
-
|
|
225
258
|
import { nextAuth } from "./lib/auth.js";
|
|
226
259
|
|
|
227
260
|
export const proxy = async (request: NextRequest) => {
|
|
@@ -247,16 +280,22 @@ The application owns the redirect URL. When `unauthorizedUrl` is provided, the a
|
|
|
247
280
|
|
|
248
281
|
- Node.js 22 or newer.
|
|
249
282
|
- A database adapter.
|
|
283
|
+
- Drizzle ORM 0.45.2 or newer when using the Drizzle adapter.
|
|
250
284
|
- Hono 4 when using the Hono adapter.
|
|
285
|
+
- Express 5 when using the Express adapter.
|
|
286
|
+
- Fastify 5 when using the Fastify adapter.
|
|
251
287
|
- Next.js 15 or newer when using the Next.js adapter.
|
|
252
288
|
|
|
253
|
-
`hono` and `next` are optional peer dependencies. The Prisma adapter receives the application's generated Prisma client and does not import Prisma at runtime.
|
|
289
|
+
`drizzle-orm`, `hono`, `express`, `fastify`, and `next` are optional peer dependencies. Installing or importing one adapter does not load the others. The Prisma adapter receives the application's generated Prisma client and does not import Prisma at runtime. The Drizzle adapter imports `drizzle-orm`, while the application owns the MySQL/MariaDB driver.
|
|
254
290
|
|
|
255
291
|
| Import | Purpose |
|
|
256
292
|
| ----------------------- | --------------------------------------------------------- |
|
|
257
293
|
| `@gauts/auth` | Password service, session core, errors, and public types. |
|
|
294
|
+
| `@gauts/auth/drizzle` | Drizzle database adapter for MySQL and MariaDB. |
|
|
258
295
|
| `@gauts/auth/prisma` | Prisma database adapter. |
|
|
259
296
|
| `@gauts/auth/hono` | Hono cookies, methods, and middleware. |
|
|
297
|
+
| `@gauts/auth/express` | Express cookies, methods, and middleware. |
|
|
298
|
+
| `@gauts/auth/fastify` | Fastify cookies, methods, decorators, and hooks. |
|
|
260
299
|
| `@gauts/auth/next` | Next.js renewal scheduling and `Set-Cookie` forwarding. |
|
|
261
300
|
| `@gauts/auth/providers` | Google, GitHub, and X OAuth providers. |
|
|
262
301
|
|
|
@@ -281,6 +320,8 @@ type HonoGetIp = (c: Context) => Promise<string | null | undefined> | string | n
|
|
|
281
320
|
|
|
282
321
|
When `getIp` is omitted, the adapter stores `ip: null` and does not read IP headers automatically. Configuring `session.validation` with `"ip"` requires `getIp` and fails during initialization when it is missing.
|
|
283
322
|
|
|
323
|
+
`createExpressAuth()` and `createFastifyAuth()` accept the same configuration. Their `getIp` callback receives the native Express or Fastify request instead of a Hono context.
|
|
324
|
+
|
|
284
325
|
### Password
|
|
285
326
|
|
|
286
327
|
#### Argon2id
|
|
@@ -453,6 +494,8 @@ users
|
|
|
453
494
|
└── account_sessions
|
|
454
495
|
```
|
|
455
496
|
|
|
497
|
+
[View and copy the required Prisma schema](./src/adapters/prisma/schema.prisma).
|
|
498
|
+
|
|
456
499
|
The required default delegate names are `prisma.users`, `prisma.user_accounts`, and `prisma.account_sessions`. The fixed Prisma relation fields are:
|
|
457
500
|
|
|
458
501
|
- `user_accounts.user`;
|
|
@@ -555,6 +598,76 @@ access: {
|
|
|
555
598
|
|
|
556
599
|
Every configured account and user condition must match. Omitting `access` applies no application-specific account restriction.
|
|
557
600
|
|
|
601
|
+
### Drizzle MySQL/MariaDB adapter
|
|
602
|
+
|
|
603
|
+
The Drizzle adapter supports MySQL and MariaDB. Install it inside the API project with the driver used by the application:
|
|
604
|
+
|
|
605
|
+
```bash
|
|
606
|
+
npm install @gauts/auth drizzle-orm mysql2
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
Unlike Prisma, Drizzle does not expose model delegates that can be discovered by name. Pass the application's table objects explicitly:
|
|
610
|
+
|
|
611
|
+
```ts
|
|
612
|
+
import { createDrizzleAdapter } from "@gauts/auth/drizzle";
|
|
613
|
+
import { createHonoAuth } from "@gauts/auth/hono";
|
|
614
|
+
import { db } from "./db.js";
|
|
615
|
+
import { accountSessions, userAccounts, users } from "./schema.js";
|
|
616
|
+
|
|
617
|
+
export const auth = createHonoAuth({
|
|
618
|
+
db: createDrizzleAdapter({
|
|
619
|
+
client: db,
|
|
620
|
+
models: {
|
|
621
|
+
accounts: { table: userAccounts },
|
|
622
|
+
sessions: { table: accountSessions },
|
|
623
|
+
users: { table: users },
|
|
624
|
+
},
|
|
625
|
+
}),
|
|
626
|
+
});
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
The minimum Drizzle schema uses the same relationship tree and canonical TypeScript field names as the Prisma adapter. [View and copy the required Drizzle MySQL/MariaDB schema](./src/adapters/drizzle/schema.ts).
|
|
630
|
+
|
|
631
|
+
The adapter queries these tables through explicit SQL joins, so Drizzle `relations()` declarations are not required. Physical SQL column names may use aliases, but the TypeScript keys shown above are part of the adapter contract. Date columns must use `{ mode: "date" }`.
|
|
632
|
+
|
|
633
|
+
`select` and `access` work exactly like the Prisma adapter and are inferred from the supplied table:
|
|
634
|
+
|
|
635
|
+
```ts
|
|
636
|
+
const database = createDrizzleAdapter({
|
|
637
|
+
client: db,
|
|
638
|
+
models: {
|
|
639
|
+
accounts: {
|
|
640
|
+
access: {
|
|
641
|
+
role: ["OWNER", "ADMIN"],
|
|
642
|
+
status: ["ACTIVE"],
|
|
643
|
+
},
|
|
644
|
+
select: ["role", "status", "timezone"],
|
|
645
|
+
table: userAccounts,
|
|
646
|
+
},
|
|
647
|
+
sessions: { table: accountSessions },
|
|
648
|
+
users: {
|
|
649
|
+
access: { status: ["ACTIVE", "PENDING"] },
|
|
650
|
+
select: ["status"],
|
|
651
|
+
table: users,
|
|
652
|
+
},
|
|
653
|
+
},
|
|
654
|
+
});
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
| Property | Required | Description |
|
|
658
|
+
| ------------------------ | :------: | ---------------------------------------------------------------------- |
|
|
659
|
+
| `client` | ✅ | Drizzle MySQL client created by the application. |
|
|
660
|
+
| `models.users.table` | ✅ | Table with `id` and `name` columns. |
|
|
661
|
+
| `models.users.select` | ❌ | Additional public scalar fields; `id` and `name` are always included. |
|
|
662
|
+
| `models.users.access` | ❌ | Required values for the owning user/entity. |
|
|
663
|
+
| `models.accounts.table` | ✅ | Table with `id`, `email`, and `user_id` columns. |
|
|
664
|
+
| `models.accounts.select` | ❌ | Additional public scalar fields; `id` and `email` are always included. |
|
|
665
|
+
| `models.accounts.access` | ❌ | Required values for the authenticating account. |
|
|
666
|
+
| `models.sessions.table` | ✅ | Table implementing the complete documented session column contract. |
|
|
667
|
+
| `models.socials.table` | ❌ | Enables social persistence when the optional social table is supplied. |
|
|
668
|
+
|
|
669
|
+
Private password fields are rejected from `select` and `access` by both TypeScript and runtime validation. The adapter does not load or execute the Prisma adapter, and Prisma is not required in a Drizzle application.
|
|
670
|
+
|
|
558
671
|
### Next.js adapter
|
|
559
672
|
|
|
560
673
|
```ts
|
|
@@ -657,7 +770,7 @@ type Session = {
|
|
|
657
770
|
};
|
|
658
771
|
```
|
|
659
772
|
|
|
660
|
-
The Prisma
|
|
773
|
+
The Prisma and Drizzle adapters refine `account` and `user` with the exact additional scalar fields declared in their respective `select` arrays.
|
|
661
774
|
|
|
662
775
|
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.
|
|
663
776
|
|
|
@@ -673,7 +786,7 @@ await auth.createSession({
|
|
|
673
786
|
});
|
|
674
787
|
```
|
|
675
788
|
|
|
676
|
-
`country` is normalized to uppercase and is never used for authentication or client matching. It is not resolved by the package and does not trigger GeoIP work during normal requests. When supplied, the configured session model must provide a nullable `country` column. When omitted, the
|
|
789
|
+
`country` is normalized to uppercase and is never used for authentication or client matching. It is not resolved by the package and does not trigger GeoIP work during normal requests. When supplied, the configured session model must provide a nullable `country` column. When omitted, the database adapter does not send the field.
|
|
677
790
|
|
|
678
791
|
### Methods
|
|
679
792
|
|
|
@@ -689,11 +802,69 @@ await auth.createSession({
|
|
|
689
802
|
|
|
690
803
|
`requireSession` authenticates only. Application-specific route permissions remain the application's responsibility.
|
|
691
804
|
|
|
805
|
+
## Express adapter
|
|
806
|
+
|
|
807
|
+
```ts
|
|
808
|
+
import { createExpressAuth, type ExpressAuthLocals } from "@gauts/auth/express";
|
|
809
|
+
|
|
810
|
+
const auth = createExpressAuth({ db });
|
|
811
|
+
|
|
812
|
+
app.get("/account", auth.requireSession, (_request, response) => {
|
|
813
|
+
const { account, session, user } = response.locals as ExpressAuthLocals;
|
|
814
|
+
response.json({ account, session, user });
|
|
815
|
+
});
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
| Method | Purpose |
|
|
819
|
+
| ----------------------------------------------------------------- | ---------------------------------------------------- |
|
|
820
|
+
| `auth.createSession({ account_id, request, response, country? })` | Creates the DB session and writes cookies. |
|
|
821
|
+
| `auth.resolveSession({ request, response })` | Resolves the selected account and session. |
|
|
822
|
+
| `auth.renewSession({ request, response })` | Renews when due and writes authoritative cookies. |
|
|
823
|
+
| `auth.revokeSession({ request, response })` | Revokes the current session and clears cookies. |
|
|
824
|
+
| `auth.clearSession(response)` | Clears cookies without revoking the DB session. |
|
|
825
|
+
| `auth.getToken(request)` | Reads and validates the opaque session token. |
|
|
826
|
+
| `auth.requireSession` | Express middleware that populates `response.locals`. |
|
|
827
|
+
|
|
828
|
+
Express 5 forwards rejected async middleware promises to the application's error handler. The adapter does not install routes or an error handler.
|
|
829
|
+
|
|
830
|
+
## Fastify adapter
|
|
831
|
+
|
|
832
|
+
Register the request decorators once before declaring routes:
|
|
833
|
+
|
|
834
|
+
```ts
|
|
835
|
+
import { createFastifyAuth } from "@gauts/auth/fastify";
|
|
836
|
+
|
|
837
|
+
const auth = createFastifyAuth({ db });
|
|
838
|
+
|
|
839
|
+
auth.decorate(app);
|
|
840
|
+
|
|
841
|
+
app.get("/account", { preHandler: auth.requireSession }, (request) => ({
|
|
842
|
+
account: request.getDecorator("account"),
|
|
843
|
+
session: request.getDecorator("session"),
|
|
844
|
+
user: request.getDecorator("user"),
|
|
845
|
+
}));
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
| Method | Purpose |
|
|
849
|
+
| -------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
850
|
+
| `auth.decorate(app)` | Declares the native request decorators once at startup. |
|
|
851
|
+
| `auth.createSession({ account_id, request, reply, country? })` | Creates the DB session and writes cookies. |
|
|
852
|
+
| `auth.resolveSession({ request, reply })` | Resolves the selected account and session. |
|
|
853
|
+
| `auth.renewSession({ request, reply })` | Renews when due and writes authoritative cookies. |
|
|
854
|
+
| `auth.revokeSession({ request, reply })` | Revokes the current session and clears cookies. |
|
|
855
|
+
| `auth.clearSession(reply)` | Clears cookies without revoking the DB session. |
|
|
856
|
+
| `auth.getToken(request)` | Reads and validates the opaque session token. |
|
|
857
|
+
| `auth.requireSession` | Fastify `preHandler` that populates native request decorators. |
|
|
858
|
+
|
|
859
|
+
When social authentication is enabled, `auth.decorate(app)` also declares `social`. The adapter does not register a Fastify plugin or create routes.
|
|
860
|
+
|
|
692
861
|
## Social authentication
|
|
693
862
|
|
|
694
863
|
Social authentication is an optional addon and remains disabled unless `social` is configured. Complete these steps only in applications that need Google, GitHub, or X authentication.
|
|
695
864
|
|
|
696
|
-
### 1. Add
|
|
865
|
+
### 1. Add social persistence
|
|
866
|
+
|
|
867
|
+
#### Prisma
|
|
697
868
|
|
|
698
869
|
Add the social relation inside the existing `user_accounts` model in the API schema:
|
|
699
870
|
|
|
@@ -745,6 +916,49 @@ const db = createPrismaAdapter({
|
|
|
745
916
|
});
|
|
746
917
|
```
|
|
747
918
|
|
|
919
|
+
#### Drizzle MySQL/MariaDB
|
|
920
|
+
|
|
921
|
+
Add the provider table to the API schema:
|
|
922
|
+
|
|
923
|
+
```ts
|
|
924
|
+
export const socialAccounts = mysqlTable(
|
|
925
|
+
"social_accounts",
|
|
926
|
+
{
|
|
927
|
+
account_id: varchar("account_id", { length: 255 })
|
|
928
|
+
.notNull()
|
|
929
|
+
.references(() => userAccounts.id, { onDelete: "cascade" }),
|
|
930
|
+
created_at: timestamp("created_at", { mode: "date" }).defaultNow().notNull(),
|
|
931
|
+
id: varchar("id", { length: 255 }).primaryKey(),
|
|
932
|
+
provider: varchar("provider", { length: 32 }).notNull(),
|
|
933
|
+
provider_id: varchar("provider_id", { length: 255 }).notNull(),
|
|
934
|
+
},
|
|
935
|
+
(table) => [
|
|
936
|
+
index("social_accounts_account_id_idx").on(table.account_id),
|
|
937
|
+
uniqueIndex("social_accounts_provider_provider_id_key").on(
|
|
938
|
+
table.provider,
|
|
939
|
+
table.provider_id,
|
|
940
|
+
),
|
|
941
|
+
uniqueIndex("social_accounts_account_id_provider_key").on(table.account_id, table.provider),
|
|
942
|
+
],
|
|
943
|
+
);
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
Then supply it to the adapter:
|
|
947
|
+
|
|
948
|
+
```ts
|
|
949
|
+
const db = createDrizzleAdapter({
|
|
950
|
+
client,
|
|
951
|
+
models: {
|
|
952
|
+
accounts: { table: userAccounts },
|
|
953
|
+
sessions: { table: accountSessions },
|
|
954
|
+
socials: { table: socialAccounts },
|
|
955
|
+
users: { table: users },
|
|
956
|
+
},
|
|
957
|
+
});
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
Omitting `models.socials` keeps the Drizzle adapter session-only. The adapter creates the owning user and account in one transaction during default social registration, so that path requires a Drizzle MySQL driver with transaction support. Additional required user/account columns must have database defaults; otherwise the application must provide `registration.createAccount`.
|
|
961
|
+
|
|
748
962
|
### 2. Configure the providers
|
|
749
963
|
|
|
750
964
|
Import only the providers used by the API:
|
|
@@ -783,7 +997,7 @@ Register every `callbackUrl` shown above in the corresponding provider dashboard
|
|
|
783
997
|
|
|
784
998
|
### 3. Add the API route and frontend start
|
|
785
999
|
|
|
786
|
-
Declare one dynamic API route covering every configured provider. `social.handle`
|
|
1000
|
+
Declare one dynamic API route covering every configured provider. `social.handle` completes `start` and expected error responses itself, then continues to the application handler only after a successful callback.
|
|
787
1001
|
|
|
788
1002
|
```ts
|
|
789
1003
|
app.get("/auth/social/:provider/:action", auth.social.handle, async (c) => {
|
|
@@ -827,16 +1041,28 @@ social.registered;
|
|
|
827
1041
|
social.returnTo;
|
|
828
1042
|
```
|
|
829
1043
|
|
|
1044
|
+
The native equivalents are:
|
|
1045
|
+
|
|
1046
|
+
```ts
|
|
1047
|
+
// Express
|
|
1048
|
+
const social = (response.locals as ExpressSocialLocals).social;
|
|
1049
|
+
|
|
1050
|
+
// Fastify
|
|
1051
|
+
const social = request.getDecorator<SocialAuthenticated>("social");
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
See the advanced framework examples for complete social routes and custom registration.
|
|
1055
|
+
|
|
830
1056
|
The package does not create the application route, session, notifications, logs, or final success response. Those remain explicit in the route handler.
|
|
831
1057
|
|
|
832
1058
|
### Social configuration
|
|
833
1059
|
|
|
834
|
-
| Property | Type / allowed values |
|
|
835
|
-
| ---------------------------- | ------------------------------------ |
|
|
836
|
-
| `social.providers` | `SocialProvider[]` |
|
|
837
|
-
| `social.cookieName` | Valid cookie name |
|
|
838
|
-
| `social.registration` | `SocialRegistrationConfig` |
|
|
839
|
-
| `registration.createAccount` | Async callback returning `accountId` |
|
|
1060
|
+
| Property | Type / allowed values | Required | Default | Description |
|
|
1061
|
+
| ---------------------------- | ------------------------------------ | :------: | --------- | -------------------------------------------------------------------------- |
|
|
1062
|
+
| `social.providers` | `SocialProvider[]` | ✅ | — | Configured Google, GitHub, or X providers. |
|
|
1063
|
+
| `social.cookieName` | Valid cookie name | ❌ | `"__soc"` | Signed temporary OAuth/registration transaction cookie. |
|
|
1064
|
+
| `social.registration` | `SocialRegistrationConfig` | ❌ | Disabled | Enables default or application-specific social registration. |
|
|
1065
|
+
| `registration.createAccount` | Async callback returning `accountId` | ❌ | Built-in | Creates required business data when the default structure is insufficient. |
|
|
840
1066
|
|
|
841
1067
|
Provider configuration:
|
|
842
1068
|
|
|
@@ -952,7 +1178,7 @@ app.post("/auth/register/social", async (c) => {
|
|
|
952
1178
|
|
|
953
1179
|
### Core and adapter composition
|
|
954
1180
|
|
|
955
|
-
`createHonoAuth()`
|
|
1181
|
+
Use the entry point matching the application framework: `createHonoAuth()`, `createExpressAuth()`, or `createFastifyAuth()`. Use separate composition only when the same core instance is required outside the framework adapter:
|
|
956
1182
|
|
|
957
1183
|
```ts
|
|
958
1184
|
import { createAuth } from "@gauts/auth";
|
|
@@ -1074,7 +1300,7 @@ const socialDb = {
|
|
|
1074
1300
|
} satisfies SocialDbAdapter;
|
|
1075
1301
|
```
|
|
1076
1302
|
|
|
1077
|
-
The Prisma
|
|
1303
|
+
The Prisma and Drizzle adapters implement both `DbAdapter` and `SocialDbAdapter` when their optional social model is available.
|
|
1078
1304
|
|
|
1079
1305
|
## Performance
|
|
1080
1306
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AnyMySqlTable } from "drizzle-orm/mysql-core";
|
|
2
|
+
import type { ResolvedDrizzleModels } from "./types.js";
|
|
3
|
+
export declare const DRIZZLE_FIELDS: {
|
|
4
|
+
readonly accounts: readonly ["id", "email", "user_id"];
|
|
5
|
+
readonly sessions: readonly ["account_id", "agent", "country", "created_at", "expires_at", "id", "ip", "platform", "revoked_at", "token_hash", "updated_at"];
|
|
6
|
+
readonly socials: readonly ["account_id", "created_at", "id", "provider", "provider_id"];
|
|
7
|
+
readonly users: readonly ["id", "name"];
|
|
8
|
+
};
|
|
9
|
+
export declare const resolveDrizzleModels: (input: unknown) => ResolvedDrizzleModels;
|
|
10
|
+
export declare const getDrizzleSource: (table: AnyMySqlTable) => string;
|
|
11
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/adapters/drizzle/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAK5D,OAAO,KAAK,EAA4B,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAkClF,eAAO,MAAM,cAAc;;;;;CAiBjB,CAAC;AA+HX,eAAO,MAAM,oBAAoB,GAAI,OAAO,OAAO,KAAG,qBAmCrD,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,OAAO,aAAa,KAAG,MAEvD,CAAC"}
|