File
Blob: src/worker/db/schema/auth.ts
| 1 | import { relations, sql } from "drizzle-orm"; |
| 2 | import { sqliteTable, text, integer, index, uniqueIndex } from "drizzle-orm/sqlite-core"; |
| 3 | |
| 4 | export const users = sqliteTable("users", { |
| 5 | id: text("id").primaryKey(), |
| 6 | name: text("name").notNull(), |
| 7 | email: text("email").notNull().unique(), |
| 8 | emailVerified: integer("email_verified", { mode: "boolean" }).default(false).notNull(), |
| 9 | image: text("image"), |
| 10 | createdAt: integer("created_at", { mode: "timestamp_ms" }) |
| 11 | .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) |
| 12 | .notNull(), |
| 13 | updatedAt: integer("updated_at", { mode: "timestamp_ms" }) |
| 14 | .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) |
| 15 | .$onUpdate(() => /* @__PURE__ */ new Date()) |
| 16 | .notNull(), |
| 17 | role: text("role"), |
| 18 | banned: integer("banned", { mode: "boolean" }).default(false), |
| 19 | banReason: text("ban_reason"), |
| 20 | banExpires: integer("ban_expires", { mode: "timestamp_ms" }), |
| 21 | preferredUsername: text("preferred_username"), |
| 22 | }); |
| 23 | |
| 24 | export const sessions = sqliteTable( |
| 25 | "sessions", |
| 26 | { |
| 27 | id: text("id").primaryKey(), |
| 28 | expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(), |
| 29 | token: text("token").notNull().unique(), |
| 30 | createdAt: integer("created_at", { mode: "timestamp_ms" }) |
| 31 | .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) |
| 32 | .notNull(), |
| 33 | updatedAt: integer("updated_at", { mode: "timestamp_ms" }) |
| 34 | .$onUpdate(() => /* @__PURE__ */ new Date()) |
| 35 | .notNull(), |
| 36 | ipAddress: text("ip_address"), |
| 37 | userAgent: text("user_agent"), |
| 38 | userId: text("user_id") |
| 39 | .notNull() |
| 40 | .references(() => users.id, { onDelete: "cascade" }), |
| 41 | impersonatedBy: text("impersonated_by"), |
| 42 | }, |
| 43 | (table) => [index("sessions_userId_idx").on(table.userId)], |
| 44 | ); |
| 45 | |
| 46 | export const accounts = sqliteTable( |
| 47 | "accounts", |
| 48 | { |
| 49 | id: text("id").primaryKey(), |
| 50 | accountId: text("account_id").notNull(), |
| 51 | providerId: text("provider_id").notNull(), |
| 52 | userId: text("user_id") |
| 53 | .notNull() |
| 54 | .references(() => users.id, { onDelete: "cascade" }), |
| 55 | accessToken: text("access_token"), |
| 56 | refreshToken: text("refresh_token"), |
| 57 | idToken: text("id_token"), |
| 58 | accessTokenExpiresAt: integer("access_token_expires_at", { |
| 59 | mode: "timestamp_ms", |
| 60 | }), |
| 61 | refreshTokenExpiresAt: integer("refresh_token_expires_at", { |
| 62 | mode: "timestamp_ms", |
| 63 | }), |
| 64 | scope: text("scope"), |
| 65 | password: text("password"), |
| 66 | createdAt: integer("created_at", { mode: "timestamp_ms" }) |
| 67 | .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) |
| 68 | .notNull(), |
| 69 | updatedAt: integer("updated_at", { mode: "timestamp_ms" }) |
| 70 | .$onUpdate(() => /* @__PURE__ */ new Date()) |
| 71 | .notNull(), |
| 72 | }, |
| 73 | (table) => [index("accounts_userId_idx").on(table.userId)], |
| 74 | ); |
| 75 | |
| 76 | export const verifications = sqliteTable( |
| 77 | "verifications", |
| 78 | { |
| 79 | id: text("id").primaryKey(), |
| 80 | identifier: text("identifier").notNull(), |
| 81 | value: text("value").notNull(), |
| 82 | expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(), |
| 83 | createdAt: integer("created_at", { mode: "timestamp_ms" }) |
| 84 | .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) |
| 85 | .notNull(), |
| 86 | updatedAt: integer("updated_at", { mode: "timestamp_ms" }) |
| 87 | .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) |
| 88 | .$onUpdate(() => /* @__PURE__ */ new Date()) |
| 89 | .notNull(), |
| 90 | }, |
| 91 | (table) => [index("verifications_identifier_idx").on(table.identifier)], |
| 92 | ); |
| 93 | |
| 94 | export const jwkss = sqliteTable("jwkss", { |
| 95 | id: text("id").primaryKey(), |
| 96 | publicKey: text("public_key").notNull(), |
| 97 | privateKey: text("private_key").notNull(), |
| 98 | createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(), |
| 99 | expiresAt: integer("expires_at", { mode: "timestamp_ms" }), |
| 100 | alg: text("alg"), |
| 101 | crv: text("crv"), |
| 102 | }); |
| 103 | |
| 104 | export const oauthClients = sqliteTable( |
| 105 | "oauth_clients", |
| 106 | { |
| 107 | id: text("id").primaryKey(), |
| 108 | clientId: text("client_id").notNull().unique(), |
| 109 | clientSecret: text("client_secret"), |
| 110 | clientDiscoveryId: text("client_discovery_id"), |
| 111 | disabled: integer("disabled", { mode: "boolean" }).default(false), |
| 112 | skipConsent: integer("skip_consent", { mode: "boolean" }), |
| 113 | enableEndSession: integer("enable_end_session", { mode: "boolean" }), |
| 114 | subjectType: text("subject_type"), |
| 115 | scopes: text("scopes", { mode: "json" }), |
| 116 | clientCredentialsScopes: text("client_credentials_scopes", { |
| 117 | mode: "json", |
| 118 | }).default([]), |
| 119 | userId: text("user_id").references(() => users.id, { onDelete: "cascade" }), |
| 120 | createdAt: integer("created_at", { mode: "timestamp_ms" }), |
| 121 | updatedAt: integer("updated_at", { mode: "timestamp_ms" }), |
| 122 | name: text("name"), |
| 123 | uri: text("uri"), |
| 124 | icon: text("icon"), |
| 125 | contacts: text("contacts", { mode: "json" }), |
| 126 | tos: text("tos"), |
| 127 | policy: text("policy"), |
| 128 | softwareId: text("software_id"), |
| 129 | softwareVersion: text("software_version"), |
| 130 | softwareStatement: text("software_statement"), |
| 131 | redirectUris: text("redirect_uris", { mode: "json" }).notNull(), |
| 132 | postLogoutRedirectUris: text("post_logout_redirect_uris", { mode: "json" }), |
| 133 | backchannelLogoutUri: text("backchannel_logout_uri"), |
| 134 | backchannelLogoutSessionRequired: integer("backchannel_logout_session_required", { mode: "boolean" }), |
| 135 | tokenEndpointAuthMethod: text("token_endpoint_auth_method"), |
| 136 | applicationType: text("application_type"), |
| 137 | jwks: text("jwks"), |
| 138 | jwksUri: text("jwks_uri"), |
| 139 | grantTypes: text("grant_types", { mode: "json" }), |
| 140 | responseTypes: text("response_types", { mode: "json" }), |
| 141 | requirePKCE: integer("require_pkce", { mode: "boolean" }), |
| 142 | dpopBoundAccessTokens: integer("dpop_bound_access_tokens", { |
| 143 | mode: "boolean", |
| 144 | }).default(false), |
| 145 | referenceId: text("reference_id"), |
| 146 | metadata: text("metadata", { mode: "json" }), |
| 147 | }, |
| 148 | (table) => [index("oauthClients_userId_idx").on(table.userId)], |
| 149 | ); |
| 150 | |
| 151 | export const oauthResources = sqliteTable("oauth_resources", { |
| 152 | id: text("id").primaryKey(), |
| 153 | identifier: text("identifier").notNull().unique(), |
| 154 | name: text("name").notNull(), |
| 155 | accessTokenTtl: integer("access_token_ttl"), |
| 156 | refreshTokenTtl: integer("refresh_token_ttl"), |
| 157 | signingAlgorithm: text("signing_algorithm"), |
| 158 | signingKeyId: text("signing_key_id"), |
| 159 | allowedScopes: text("allowed_scopes", { mode: "json" }), |
| 160 | customClaims: text("custom_claims", { mode: "json" }), |
| 161 | dpopBoundAccessTokensRequired: integer("dpop_bound_access_tokens_required", { |
| 162 | mode: "boolean", |
| 163 | }).default(false), |
| 164 | disabled: integer("disabled", { mode: "boolean" }).default(false), |
| 165 | createdAt: integer("created_at", { mode: "timestamp_ms" }), |
| 166 | updatedAt: integer("updated_at", { mode: "timestamp_ms" }), |
| 167 | policyVersion: integer("policy_version").default(1), |
| 168 | metadata: text("metadata", { mode: "json" }), |
| 169 | }); |
| 170 | |
| 171 | export const oauthClientResources = sqliteTable( |
| 172 | "oauth_client_resources", |
| 173 | { |
| 174 | id: text("id").primaryKey(), |
| 175 | clientId: text("client_id") |
| 176 | .notNull() |
| 177 | .references(() => oauthClients.clientId, { onDelete: "cascade" }), |
| 178 | resourceId: text("resource_id") |
| 179 | .notNull() |
| 180 | .references(() => oauthResources.identifier, { onDelete: "cascade" }), |
| 181 | metadata: text("metadata", { mode: "json" }), |
| 182 | createdAt: integer("created_at", { mode: "timestamp_ms" }), |
| 183 | }, |
| 184 | (table) => [ |
| 185 | uniqueIndex("oauthClientResources_clientId_resourceId_uidx").on(table.clientId, table.resourceId), |
| 186 | index("oauthClientResources_clientId_idx").on(table.clientId), |
| 187 | index("oauthClientResources_resourceId_idx").on(table.resourceId), |
| 188 | ], |
| 189 | ); |
| 190 | |
| 191 | export const oauthRefreshTokens = sqliteTable( |
| 192 | "oauth_refresh_tokens", |
| 193 | { |
| 194 | id: text("id").primaryKey(), |
| 195 | token: text("token").notNull().unique(), |
| 196 | clientId: text("client_id") |
| 197 | .notNull() |
| 198 | .references(() => oauthClients.clientId, { onDelete: "cascade" }), |
| 199 | sessionId: text("session_id").references(() => sessions.id, { |
| 200 | onDelete: "set null", |
| 201 | }), |
| 202 | userId: text("user_id") |
| 203 | .notNull() |
| 204 | .references(() => users.id, { onDelete: "cascade" }), |
| 205 | referenceId: text("reference_id"), |
| 206 | authorizationCodeId: text("authorization_code_id"), |
| 207 | resources: text("resources", { mode: "json" }), |
| 208 | requestedUserInfoClaims: text("requested_user_info_claims", { |
| 209 | mode: "json", |
| 210 | }), |
| 211 | expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(), |
| 212 | createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(), |
| 213 | revoked: integer("revoked", { mode: "timestamp_ms" }), |
| 214 | rotatedAt: integer("rotated_at", { mode: "timestamp_ms" }), |
| 215 | rotationReplayResponse: text("rotation_replay_response"), |
| 216 | rotationReplayExpiresAt: integer("rotation_replay_expires_at", { |
| 217 | mode: "timestamp_ms", |
| 218 | }), |
| 219 | authTime: integer("auth_time", { mode: "timestamp_ms" }), |
| 220 | confirmation: text("confirmation", { mode: "json" }), |
| 221 | scopes: text("scopes", { mode: "json" }).notNull(), |
| 222 | }, |
| 223 | (table) => [ |
| 224 | index("oauthRefreshTokens_clientId_idx").on(table.clientId), |
| 225 | index("oauthRefreshTokens_sessionId_idx").on(table.sessionId), |
| 226 | index("oauthRefreshTokens_userId_idx").on(table.userId), |
| 227 | index("oauthRefreshTokens_authorizationCodeId_idx").on(table.authorizationCodeId), |
| 228 | ], |
| 229 | ); |
| 230 | |
| 231 | export const oauthAccessTokens = sqliteTable( |
| 232 | "oauth_access_tokens", |
| 233 | { |
| 234 | id: text("id").primaryKey(), |
| 235 | token: text("token").notNull().unique(), |
| 236 | clientId: text("client_id") |
| 237 | .notNull() |
| 238 | .references(() => oauthClients.clientId, { onDelete: "cascade" }), |
| 239 | sessionId: text("session_id").references(() => sessions.id, { |
| 240 | onDelete: "set null", |
| 241 | }), |
| 242 | userId: text("user_id").references(() => users.id, { onDelete: "cascade" }), |
| 243 | referenceId: text("reference_id"), |
| 244 | authorizationCodeId: text("authorization_code_id"), |
| 245 | resources: text("resources", { mode: "json" }), |
| 246 | requestedUserInfoClaims: text("requested_user_info_claims", { |
| 247 | mode: "json", |
| 248 | }), |
| 249 | refreshId: text("refresh_id").references(() => oauthRefreshTokens.id, { |
| 250 | onDelete: "cascade", |
| 251 | }), |
| 252 | expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(), |
| 253 | createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(), |
| 254 | revoked: integer("revoked", { mode: "timestamp_ms" }), |
| 255 | confirmation: text("confirmation", { mode: "json" }), |
| 256 | scopes: text("scopes", { mode: "json" }).notNull(), |
| 257 | }, |
| 258 | (table) => [ |
| 259 | index("oauthAccessTokens_clientId_idx").on(table.clientId), |
| 260 | index("oauthAccessTokens_sessionId_idx").on(table.sessionId), |
| 261 | index("oauthAccessTokens_userId_idx").on(table.userId), |
| 262 | index("oauthAccessTokens_authorizationCodeId_idx").on(table.authorizationCodeId), |
| 263 | index("oauthAccessTokens_refreshId_idx").on(table.refreshId), |
| 264 | ], |
| 265 | ); |
| 266 | |
| 267 | export const oauthConsents = sqliteTable( |
| 268 | "oauth_consents", |
| 269 | { |
| 270 | id: text("id").primaryKey(), |
| 271 | clientId: text("client_id") |
| 272 | .notNull() |
| 273 | .references(() => oauthClients.clientId, { onDelete: "cascade" }), |
| 274 | userId: text("user_id").references(() => users.id, { onDelete: "cascade" }), |
| 275 | referenceId: text("reference_id"), |
| 276 | resources: text("resources", { mode: "json" }), |
| 277 | requestedUserInfoClaims: text("requested_user_info_claims", { |
| 278 | mode: "json", |
| 279 | }), |
| 280 | scopes: text("scopes", { mode: "json" }).notNull(), |
| 281 | createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(), |
| 282 | updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull(), |
| 283 | }, |
| 284 | (table) => [ |
| 285 | index("oauthConsents_clientId_idx").on(table.clientId), |
| 286 | index("oauthConsents_userId_idx").on(table.userId), |
| 287 | ], |
| 288 | ); |
| 289 | |
| 290 | export const oauthClientAssertions = sqliteTable("oauth_client_assertions", { |
| 291 | id: text("id").primaryKey(), |
| 292 | expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(), |
| 293 | }); |
| 294 | |
| 295 | export const usersRelations = relations(users, ({ many }) => ({ |
| 296 | sessions: many(sessions), |
| 297 | accounts: many(accounts), |
| 298 | oauthClients: many(oauthClients), |
| 299 | oauthRefreshTokens: many(oauthRefreshTokens), |
| 300 | oauthAccessTokens: many(oauthAccessTokens), |
| 301 | oauthConsents: many(oauthConsents), |
| 302 | })); |
| 303 | |
| 304 | export const sessionsRelations = relations(sessions, ({ one, many }) => ({ |
| 305 | user: one(users, { |
| 306 | fields: [sessions.userId], |
| 307 | references: [users.id], |
| 308 | }), |
| 309 | oauthRefreshTokens: many(oauthRefreshTokens), |
| 310 | oauthAccessTokens: many(oauthAccessTokens), |
| 311 | })); |
| 312 | |
| 313 | export const accountsRelations = relations(accounts, ({ one }) => ({ |
| 314 | user: one(users, { |
| 315 | fields: [accounts.userId], |
| 316 | references: [users.id], |
| 317 | }), |
| 318 | })); |
| 319 | |
| 320 | export const oauthClientsRelations = relations(oauthClients, ({ one, many }) => ({ |
| 321 | user: one(users, { |
| 322 | fields: [oauthClients.userId], |
| 323 | references: [users.id], |
| 324 | }), |
| 325 | oauthClientResources: many(oauthClientResources), |
| 326 | oauthRefreshTokens: many(oauthRefreshTokens), |
| 327 | oauthAccessTokens: many(oauthAccessTokens), |
| 328 | oauthConsents: many(oauthConsents), |
| 329 | })); |
| 330 | |
| 331 | export const oauthResourcesRelations = relations(oauthResources, ({ many }) => ({ |
| 332 | oauthClientResources: many(oauthClientResources), |
| 333 | })); |
| 334 | |
| 335 | export const oauthClientResourcesRelations = relations(oauthClientResources, ({ one }) => ({ |
| 336 | oauthClient: one(oauthClients, { |
| 337 | fields: [oauthClientResources.clientId], |
| 338 | references: [oauthClients.clientId], |
| 339 | }), |
| 340 | oauthResource: one(oauthResources, { |
| 341 | fields: [oauthClientResources.resourceId], |
| 342 | references: [oauthResources.identifier], |
| 343 | }), |
| 344 | })); |
| 345 | |
| 346 | export const oauthRefreshTokensRelations = relations(oauthRefreshTokens, ({ one, many }) => ({ |
| 347 | oauthClient: one(oauthClients, { |
| 348 | fields: [oauthRefreshTokens.clientId], |
| 349 | references: [oauthClients.clientId], |
| 350 | }), |
| 351 | session: one(sessions, { |
| 352 | fields: [oauthRefreshTokens.sessionId], |
| 353 | references: [sessions.id], |
| 354 | }), |
| 355 | user: one(users, { |
| 356 | fields: [oauthRefreshTokens.userId], |
| 357 | references: [users.id], |
| 358 | }), |
| 359 | oauthAccessTokens: many(oauthAccessTokens), |
| 360 | })); |
| 361 | |
| 362 | export const oauthAccessTokensRelations = relations(oauthAccessTokens, ({ one }) => ({ |
| 363 | oauthClient: one(oauthClients, { |
| 364 | fields: [oauthAccessTokens.clientId], |
| 365 | references: [oauthClients.clientId], |
| 366 | }), |
| 367 | session: one(sessions, { |
| 368 | fields: [oauthAccessTokens.sessionId], |
| 369 | references: [sessions.id], |
| 370 | }), |
| 371 | user: one(users, { |
| 372 | fields: [oauthAccessTokens.userId], |
| 373 | references: [users.id], |
| 374 | }), |
| 375 | oauthRefreshToken: one(oauthRefreshTokens, { |
| 376 | fields: [oauthAccessTokens.refreshId], |
| 377 | references: [oauthRefreshTokens.id], |
| 378 | }), |
| 379 | })); |
| 380 | |
| 381 | export const oauthConsentsRelations = relations(oauthConsents, ({ one }) => ({ |
| 382 | oauthClient: one(oauthClients, { |
| 383 | fields: [oauthConsents.clientId], |
| 384 | references: [oauthClients.clientId], |
| 385 | }), |
| 386 | user: one(users, { |
| 387 | fields: [oauthConsents.userId], |
| 388 | references: [users.id], |
| 389 | }), |
| 390 | })); |