@auth/drizzle-adapter 1.0.1 → 1.2.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/src/lib/pg.ts CHANGED
@@ -1,9 +1,10 @@
1
- import { and, eq, getTableColumns } from "drizzle-orm"
1
+ import { InferInsertModel, and, eq, getTableColumns } from "drizzle-orm"
2
2
  import {
3
3
  PgColumn,
4
4
  PgDatabase,
5
5
  PgTableWithColumns,
6
6
  QueryResultHKT,
7
+ boolean,
7
8
  integer,
8
9
  pgTable,
9
10
  primaryKey,
@@ -14,88 +15,132 @@ import {
14
15
  import type {
15
16
  Adapter,
16
17
  AdapterAccount,
18
+ AdapterAccountType,
19
+ AdapterAuthenticator,
17
20
  AdapterSession,
18
21
  AdapterUser,
19
22
  VerificationToken,
20
23
  } from "@auth/core/adapters"
21
24
 
22
- export const postgresUsersTable = pgTable("user", {
23
- id: text("id")
24
- .primaryKey()
25
- .$defaultFn(() => crypto.randomUUID()),
26
- name: text("name"),
27
- email: text("email").notNull(),
28
- emailVerified: timestamp("emailVerified", { mode: "date" }),
29
- image: text("image"),
30
- }) satisfies DefaultPostgresUsersTable
25
+ export function defineTables(
26
+ schema: Partial<DefaultPostgresSchema> = {}
27
+ ): Required<DefaultPostgresSchema> {
28
+ const usersTable =
29
+ schema.usersTable ??
30
+ (pgTable("user", {
31
+ id: text("id")
32
+ .primaryKey()
33
+ .$defaultFn(() => crypto.randomUUID()),
34
+ name: text("name"),
35
+ email: text("email").notNull(),
36
+ emailVerified: timestamp("emailVerified", { mode: "date" }),
37
+ image: text("image"),
38
+ }) satisfies DefaultPostgresUsersTable)
31
39
 
32
- export const postgresAccountsTable = pgTable(
33
- "account",
34
- {
35
- userId: text("userId")
36
- .notNull()
37
- .references(() => postgresUsersTable.id, { onDelete: "cascade" }),
38
- type: text("type").$type<AdapterAccount["type"]>().notNull(),
39
- provider: text("provider").notNull(),
40
- providerAccountId: text("providerAccountId").notNull(),
41
- refresh_token: text("refresh_token"),
42
- access_token: text("access_token"),
43
- expires_at: integer("expires_at"),
44
- token_type: text("token_type"),
45
- scope: text("scope"),
46
- id_token: text("id_token"),
47
- session_state: text("session_state"),
48
- },
49
- (table) => {
50
- return {
51
- compositePk: primaryKey({
52
- columns: [table.provider, table.providerAccountId],
53
- }),
54
- }
55
- }
56
- ) satisfies DefaultPostgresAccountsTable
40
+ const accountsTable =
41
+ schema.accountsTable ??
42
+ (pgTable(
43
+ "account",
44
+ {
45
+ userId: text("userId")
46
+ .notNull()
47
+ .references(() => usersTable.id, { onDelete: "cascade" }),
48
+ type: text("type").$type<AdapterAccountType>().notNull(),
49
+ provider: text("provider").notNull(),
50
+ providerAccountId: text("providerAccountId").notNull(),
51
+ refresh_token: text("refresh_token"),
52
+ access_token: text("access_token"),
53
+ expires_at: integer("expires_at"),
54
+ token_type: text("token_type"),
55
+ scope: text("scope"),
56
+ id_token: text("id_token"),
57
+ session_state: text("session_state"),
58
+ },
59
+ (account) => ({
60
+ compositePk: primaryKey({
61
+ columns: [account.provider, account.providerAccountId],
62
+ }),
63
+ })
64
+ ) satisfies DefaultPostgresAccountsTable)
57
65
 
58
- export const postgresSessionsTable = pgTable("session", {
59
- sessionToken: text("sessionToken").primaryKey(),
60
- userId: text("userId")
61
- .notNull()
62
- .references(() => postgresUsersTable.id, { onDelete: "cascade" }),
63
- expires: timestamp("expires", { mode: "date" }).notNull(),
64
- }) satisfies DefaultPostgresSessionsTable
66
+ const sessionsTable =
67
+ schema.sessionsTable ??
68
+ (pgTable("session", {
69
+ sessionToken: text("sessionToken").primaryKey(),
70
+ userId: text("userId")
71
+ .notNull()
72
+ .references(() => usersTable.id, { onDelete: "cascade" }),
73
+ expires: timestamp("expires", { mode: "date" }).notNull(),
74
+ }) satisfies DefaultPostgresSessionsTable)
65
75
 
66
- export const postgresVerificationTokensTable = pgTable(
67
- "verificationToken",
68
- {
69
- identifier: text("identifier").notNull(),
70
- token: text("token").notNull(),
71
- expires: timestamp("expires", { mode: "date" }).notNull(),
72
- },
73
- (table) => {
74
- return {
75
- compositePk: primaryKey({ columns: [table.identifier, table.token] }),
76
- }
76
+ const verificationTokensTable =
77
+ schema.verificationTokensTable ??
78
+ (pgTable(
79
+ "verificationToken",
80
+ {
81
+ identifier: text("identifier").notNull(),
82
+ token: text("token").notNull(),
83
+ expires: timestamp("expires", { mode: "date" }).notNull(),
84
+ },
85
+ (verficationToken) => ({
86
+ compositePk: primaryKey({
87
+ columns: [verficationToken.identifier, verficationToken.token],
88
+ }),
89
+ })
90
+ ) satisfies DefaultPostgresVerificationTokenTable)
91
+
92
+ const authenticatorsTable =
93
+ schema.authenticatorsTable ??
94
+ (pgTable(
95
+ "authenticator",
96
+ {
97
+ credentialID: text("credentialID").notNull().unique(),
98
+ userId: text("userId")
99
+ .notNull()
100
+ .references(() => usersTable.id, { onDelete: "cascade" }),
101
+ providerAccountId: text("providerAccountId").notNull(),
102
+ credentialPublicKey: text("credentialPublicKey").notNull(),
103
+ counter: integer("counter").notNull(),
104
+ credentialDeviceType: text("credentialDeviceType").notNull(),
105
+ credentialBackedUp: boolean("credentialBackedUp").notNull(),
106
+ transports: text("transports"),
107
+ },
108
+ (authenticator) => ({
109
+ compositePK: primaryKey({
110
+ columns: [authenticator.userId, authenticator.credentialID],
111
+ }),
112
+ })
113
+ ) satisfies DefaultPostgresAuthenticatorTable)
114
+
115
+ return {
116
+ usersTable,
117
+ accountsTable,
118
+ sessionsTable,
119
+ verificationTokensTable,
120
+ authenticatorsTable,
77
121
  }
78
- ) satisfies DefaultPostgresVerificationTokenTable
122
+ }
79
123
 
80
124
  export function PostgresDrizzleAdapter(
81
125
  client: PgDatabase<QueryResultHKT, any>,
82
- schema: DefaultPostgresSchema = {
83
- usersTable: postgresUsersTable,
84
- accountsTable: postgresAccountsTable,
85
- sessionsTable: postgresSessionsTable,
86
- verificationTokensTable: postgresVerificationTokensTable,
87
- }
126
+ schema?: DefaultPostgresSchema
88
127
  ): Adapter {
89
- const { usersTable, accountsTable, sessionsTable, verificationTokensTable } =
90
- schema
128
+ const {
129
+ usersTable,
130
+ accountsTable,
131
+ sessionsTable,
132
+ verificationTokensTable,
133
+ authenticatorsTable,
134
+ } = defineTables(schema)
91
135
 
92
136
  return {
93
137
  async createUser(data: AdapterUser) {
138
+ const { id, ...insertData } = data
94
139
  const hasDefaultId = getTableColumns(usersTable)["id"]["hasDefault"]
95
140
 
96
141
  return client
97
142
  .insert(usersTable)
98
- .values(hasDefaultId ? data : { ...data, id: crypto.randomUUID() })
143
+ .values(hasDefaultId ? insertData : { ...insertData, id })
99
144
  .returning()
100
145
  .then((res) => res[0])
101
146
  },
@@ -224,21 +269,72 @@ export function PostgresDrizzleAdapter(
224
269
  )
225
270
  )
226
271
  },
272
+ async getAccount(providerAccountId: string, provider: string) {
273
+ return client
274
+ .select()
275
+ .from(accountsTable)
276
+ .where(
277
+ and(
278
+ eq(accountsTable.provider, provider),
279
+ eq(accountsTable.providerAccountId, providerAccountId)
280
+ )
281
+ )
282
+ .then((res) => res[0] ?? null) as Promise<AdapterAccount | null>
283
+ },
284
+ async createAuthenticator(data: AdapterAuthenticator) {
285
+ return client
286
+ .insert(authenticatorsTable)
287
+ .values(data)
288
+ .returning()
289
+ .then((res) => res[0] ?? null)
290
+ },
291
+ async getAuthenticator(credentialID: string) {
292
+ return client
293
+ .select()
294
+ .from(authenticatorsTable)
295
+ .where(eq(authenticatorsTable.credentialID, credentialID))
296
+ .then((res) => res[0] ?? null)
297
+ },
298
+ async listAuthenticatorsByUserId(userId: string) {
299
+ return client
300
+ .select()
301
+ .from(authenticatorsTable)
302
+ .where(eq(authenticatorsTable.userId, userId))
303
+ .then((res) => res)
304
+ },
305
+ async updateAuthenticatorCounter(credentialID: string, newCounter: number) {
306
+ const authenticator = await client
307
+ .update(authenticatorsTable)
308
+ .set({ counter: newCounter })
309
+ .where(eq(authenticatorsTable.credentialID, credentialID))
310
+ .returning()
311
+ .then((res) => res[0])
312
+
313
+ if (!authenticator) throw new Error("Authenticator not found.")
314
+
315
+ return authenticator
316
+ },
227
317
  }
228
318
  }
229
319
 
230
320
  type DefaultPostgresColumn<
231
321
  T extends {
232
- data: string | number | Date
233
- dataType: "string" | "number" | "date"
322
+ data: string | number | boolean | Date
323
+ dataType: "string" | "number" | "boolean" | "date"
234
324
  notNull: boolean
235
- columnType: "PgVarchar" | "PgText" | "PgTimestamp" | "PgInteger" | "PgUUID"
325
+ columnType:
326
+ | "PgVarchar"
327
+ | "PgText"
328
+ | "PgBoolean"
329
+ | "PgTimestamp"
330
+ | "PgInteger"
331
+ | "PgUUID"
236
332
  },
237
333
  > = PgColumn<{
238
334
  name: string
239
335
  columnType: T["columnType"]
240
336
  data: T["data"]
241
- driverParam: string | number
337
+ driverParam: string | number | boolean
242
338
  notNull: T["notNull"]
243
339
  hasDefault: boolean
244
340
  enumValues: any
@@ -410,9 +506,66 @@ export type DefaultPostgresVerificationTokenTable = PgTableWithColumns<{
410
506
  schema: string | undefined
411
507
  }>
412
508
 
509
+ export type DefaultPostgresAuthenticatorTable = PgTableWithColumns<{
510
+ name: string
511
+ columns: {
512
+ credentialID: DefaultPostgresColumn<{
513
+ columnType: "PgVarchar" | "PgText"
514
+ data: string
515
+ notNull: true
516
+ dataType: "string"
517
+ }>
518
+ userId: DefaultPostgresColumn<{
519
+ columnType: "PgVarchar" | "PgText"
520
+ data: string
521
+ notNull: true
522
+ dataType: "string"
523
+ }>
524
+ providerAccountId: DefaultPostgresColumn<{
525
+ columnType: "PgVarchar" | "PgText"
526
+ data: string
527
+ notNull: true
528
+ dataType: "string"
529
+ }>
530
+ credentialPublicKey: DefaultPostgresColumn<{
531
+ columnType: "PgVarchar" | "PgText"
532
+ data: string
533
+ notNull: true
534
+ dataType: "string"
535
+ }>
536
+ counter: DefaultPostgresColumn<{
537
+ columnType: "PgInteger"
538
+ data: number
539
+ notNull: true
540
+ dataType: "number"
541
+ }>
542
+ credentialDeviceType: DefaultPostgresColumn<{
543
+ columnType: "PgVarchar" | "PgText"
544
+ data: string
545
+ notNull: true
546
+ dataType: "string"
547
+ }>
548
+ credentialBackedUp: DefaultPostgresColumn<{
549
+ columnType: "PgBoolean"
550
+ data: boolean
551
+ notNull: true
552
+ dataType: "boolean"
553
+ }>
554
+ transports: DefaultPostgresColumn<{
555
+ columnType: "PgVarchar" | "PgText"
556
+ data: string
557
+ notNull: false
558
+ dataType: "string"
559
+ }>
560
+ }
561
+ dialect: "pg"
562
+ schema: string | undefined
563
+ }>
564
+
413
565
  export type DefaultPostgresSchema = {
414
566
  usersTable: DefaultPostgresUsersTable
415
567
  accountsTable: DefaultPostgresAccountsTable
416
- sessionsTable: DefaultPostgresSessionsTable
417
- verificationTokensTable: DefaultPostgresVerificationTokenTable
568
+ sessionsTable?: DefaultPostgresSessionsTable
569
+ verificationTokensTable?: DefaultPostgresVerificationTokenTable
570
+ authenticatorsTable?: DefaultPostgresAuthenticatorTable
418
571
  }