/** * Canonical 17-table Better-Auth SurrealQL schema. * * --- BEGIN BETTER_AUTH_SURREAL_SCHEMA --- * Source: /mnt/f/surrealdb/surql/features/02-auth/02-better-auth.surql * Byte-for-byte mirror of the canonical schema (modulo line-ending * normalisation). Schema version moves in lockstep with this package * version — pin them together. * --- END BETTER_AUTH_SURREAL_SCHEMA --- */ import { BETTER_AUTH_SURREALDB_CODES, BetterAuthSurrealError, } from "../error-codes/error-codes.js"; import type { SurrealClientView } from "../types/adapter.js"; export const BETTER_AUTH_SURREAL_SCHEMA: string = "-- User table definition\n-- As user was created prior using snake case, we'll conform to that.\n-- We must, however, create the user table first using user.surql\nDEFINE FIELD OVERWRITE emailVerified ON user TYPE bool DEFAULT false;\n-- might scrap this, don't see a purpose, as we're changing to firstName in config\nDEFINE FIELD OVERWRITE name ON user TYPE option;\nDEFINE FIELD OVERWRITE image ON user TYPE option;\n\n-- Session table definition\nDEFINE TABLE OVERWRITE session SCHEMAFULL;\nDEFINE FIELD OVERWRITE userId ON session TYPE record ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE expiresAt ON session TYPE option;\nDEFINE FIELD OVERWRITE token ON session TYPE option;\nDEFINE FIELD OVERWRITE ipAddress ON session TYPE option;\nDEFINE FIELD OVERWRITE userAgent ON session TYPE option;\nDEFINE FIELD OVERWRITE impersonatedBy ON session TYPE option;\nDEFINE FIELD OVERWRITE activeOrganizationId ON session TYPE option;\nDEFINE FIELD OVERWRITE activeTeamId ON session TYPE option;\nDEFINE FIELD OVERWRITE createdAt ON session TYPE datetime DEFAULT time::now() VALUE $before OR time::now();\nDEFINE FIELD OVERWRITE updatedAt ON session TYPE datetime DEFAULT time::now() VALUE time::now();\n\n-- Account table definition\nDEFINE TABLE OVERWRITE account SCHEMAFULL;\nDEFINE FIELD OVERWRITE userId ON account TYPE string;\nDEFINE FIELD OVERWRITE providerId ON account TYPE string;\nDEFINE FIELD OVERWRITE accountId ON account TYPE string;\nDEFINE FIELD OVERWRITE accessToken ON account TYPE option;\nDEFINE FIELD OVERWRITE refreshToken ON account TYPE option;\nDEFINE FIELD OVERWRITE idToken ON account TYPE option;\nDEFINE FIELD OVERWRITE password ON account TYPE string;\nDEFINE FIELD OVERWRITE accessTokenExpiresAt ON account TYPE option;\nDEFINE FIELD OVERWRITE refreshTokenExpiresAt ON account TYPE option;\nDEFINE FIELD OVERWRITE scope ON account TYPE option;\nDEFINE FIELD OVERWRITE createdAt ON account TYPE datetime DEFAULT time::now() VALUE $before OR time::now();\nDEFINE FIELD OVERWRITE updatedAt ON account TYPE datetime DEFAULT time::now() VALUE time::now();\n\n-- Verification table definition\nDEFINE TABLE OVERWRITE verification SCHEMAFULL;\nDEFINE FIELD OVERWRITE createdAt ON verification TYPE datetime DEFAULT time::now() VALUE $before OR time::now();\nDEFINE FIELD OVERWRITE updatedAt ON verification TYPE datetime DEFAULT time::now() VALUE time::now();\nDEFINE FIELD OVERWRITE identifier ON verification TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE val ON verification TYPE string ASSERT $value != NONE;\n\n-- Passkey table definition\nDEFINE TABLE OVERWRITE passkey SCHEMAFULL;\nDEFINE FIELD OVERWRITE userId ON passkey TYPE record ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE name ON passkey TYPE option;\nDEFINE FIELD OVERWRITE publicKey ON passkey TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE credentialID ON passkey TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE counter ON passkey TYPE int ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE deviceType ON passkey TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE backedUp ON passkey TYPE bool ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE transports ON passkey TYPE option;\nDEFINE FIELD OVERWRITE createdAt ON passkey TYPE option DEFAULT time::now() VALUE $before OR time::now();\n\n-- Jwks table definition\nDEFINE TABLE OVERWRITE jwks SCHEMAFULL;\nDEFINE FIELD OVERWRITE publicKey ON jwks TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE privateKey ON jwks TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE createdAt ON jwks TYPE datetime ASSERT $value != NONE DEFAULT time::now() VALUE $before OR time::now();\n\n-- SsoProvider table definition\nDEFINE TABLE OVERWRITE sso_provider SCHEMAFULL;\nDEFINE FIELD OVERWRITE issuer ON sso_provider TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE oidcConfig ON sso_provider TYPE option;\nDEFINE FIELD OVERWRITE samlConfig ON sso_provider TYPE option;\nDEFINE FIELD OVERWRITE userId ON sso_provider TYPE option>;\nDEFINE FIELD OVERWRITE providerId ON sso_provider TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE organizationId ON sso_provider TYPE option;\nDEFINE FIELD OVERWRITE domain ON sso_provider TYPE string ASSERT $value != NONE;\n\n-- OauthApplication table definition\nDEFINE TABLE OVERWRITE oauth_application SCHEMAFULL;\nDEFINE FIELD OVERWRITE name ON oauth_application TYPE option;\nDEFINE FIELD OVERWRITE icon ON oauth_application TYPE option;\nDEFINE FIELD OVERWRITE metadata ON oauth_application TYPE option;\nDEFINE FIELD OVERWRITE clientId ON oauth_application TYPE option;\nDEFINE FIELD OVERWRITE clientSecret ON oauth_application TYPE option;\nDEFINE FIELD OVERWRITE redirectURLs ON oauth_application TYPE option;\nDEFINE FIELD OVERWRITE type ON oauth_application TYPE option;\nDEFINE FIELD OVERWRITE disabled ON oauth_application TYPE option;\nDEFINE FIELD OVERWRITE userId ON oauth_application TYPE option;\nDEFINE FIELD OVERWRITE createdAt ON oauth_application TYPE option DEFAULT time::now() VALUE $before OR time::now();\nDEFINE FIELD OVERWRITE updatedAt ON oauth_application TYPE option DEFAULT time::now() VALUE time::now();\n\n-- OauthAccessToken table definition\nDEFINE TABLE OVERWRITE oauth_access_token SCHEMAFULL;\nDEFINE FIELD OVERWRITE accessToken ON oauth_access_token TYPE option;\nDEFINE FIELD OVERWRITE refreshToken ON oauth_access_token TYPE option;\nDEFINE FIELD OVERWRITE accessTokenExpiresAt ON oauth_access_token TYPE option;\nDEFINE FIELD OVERWRITE refreshTokenExpiresAt ON oauth_access_token TYPE option;\nDEFINE FIELD OVERWRITE clientId ON oauth_access_token TYPE option;\nDEFINE FIELD OVERWRITE userId ON oauth_access_token TYPE option;\nDEFINE FIELD OVERWRITE scopes ON oauth_access_token TYPE option;\nDEFINE FIELD OVERWRITE createdAt ON oauth_access_token TYPE option DEFAULT time::now() VALUE $before OR time::now();\nDEFINE FIELD OVERWRITE updatedAt ON oauth_access_token TYPE option DEFAULT time::now() VALUE time::now();\n\n-- OauthConsent table definition\nDEFINE TABLE OVERWRITE oauth_consent SCHEMAFULL;\nDEFINE FIELD OVERWRITE clientId ON oauth_consent TYPE option;\nDEFINE FIELD OVERWRITE userId ON oauth_consent TYPE option;\nDEFINE FIELD OVERWRITE scopes ON oauth_consent TYPE option;\nDEFINE FIELD OVERWRITE createdAt ON oauth_consent TYPE option DEFAULT time::now() VALUE $before OR time::now();\nDEFINE FIELD OVERWRITE updatedAt ON oauth_consent TYPE option DEFAULT time::now() VALUE time::now();\nDEFINE FIELD OVERWRITE consentGiven ON oauth_consent TYPE option;\n\n-- Organization table definition\nDEFINE TABLE OVERWRITE organization SCHEMAFULL;\nDEFINE FIELD OVERWRITE name ON organization TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE slug ON organization TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE logo ON organization TYPE option;\nDEFINE FIELD OVERWRITE metadata ON organization TYPE option;\nDEFINE FIELD OVERWRITE createdAt ON organization TYPE datetime DEFAULT time::now() VALUE $before OR time::now();\n\n-- Member table definition\nDEFINE TABLE OVERWRITE member SCHEMAFULL;\nDEFINE FIELD OVERWRITE userId ON member TYPE record ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE organizationId ON member TYPE record ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE role ON member TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE createdAt ON member TYPE datetime DEFAULT time::now() VALUE $before OR time::now();\n\n-- Invitation table definition\nDEFINE TABLE OVERWRITE invitation SCHEMAFULL;\nDEFINE FIELD OVERWRITE email ON invitation TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE inviterId ON invitation TYPE record ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE organizationId ON invitation TYPE record ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE teamId ON invitation TYPE option>;\nDEFINE FIELD OVERWRITE role ON invitation TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE status ON invitation TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE expiresAt ON invitation TYPE datetime ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE createdAt ON invitation TYPE datetime DEFAULT time::now() VALUE $before OR time::now();\n\n-- Team table definition\nDEFINE TABLE OVERWRITE team SCHEMAFULL;\nDEFINE FIELD OVERWRITE name ON team TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE organizationId ON team TYPE record ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE createdAt ON team TYPE datetime DEFAULT time::now() VALUE $before OR time::now();\nDEFINE FIELD OVERWRITE updatedAt ON team TYPE option DEFAULT time::now() VALUE time::now();\n\n-- TeamMember table definition\nDEFINE TABLE OVERWRITE team_member SCHEMAFULL;\nDEFINE FIELD OVERWRITE teamId ON team_member TYPE record ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE userId ON team_member TYPE record ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE createdAt ON team_member TYPE datetime DEFAULT time::now() VALUE $before OR time::now();\n\n-- TwoFactor table definition\nDEFINE TABLE OVERWRITE two_factor SCHEMAFULL;\nDEFINE FIELD OVERWRITE secret ON two_factor TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE backupCodes ON two_factor TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE userId ON two_factor TYPE record ASSERT $value != NONE;\n\n-- ApiKey table definition\nDEFINE TABLE OVERWRITE api_key SCHEMAFULL;\nDEFINE FIELD OVERWRITE name ON api_key TYPE option;\nDEFINE FIELD OVERWRITE start ON api_key TYPE option;\nDEFINE FIELD OVERWRITE prefix ON api_key TYPE option;\nDEFINE FIELD OVERWRITE key ON api_key TYPE string ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE userId ON api_key TYPE record ASSERT $value != NONE;\nDEFINE FIELD OVERWRITE refillInterval ON api_key TYPE option;\nDEFINE FIELD OVERWRITE refillAmount ON api_key TYPE option;\nDEFINE FIELD OVERWRITE lastRefillAt ON api_key TYPE option;\nDEFINE FIELD OVERWRITE enabled ON api_key TYPE bool DEFAULT true;\nDEFINE FIELD OVERWRITE rateLimitEnabled ON api_key TYPE bool DEFAULT false;\nDEFINE FIELD OVERWRITE rateLimitTimeWindow ON api_key TYPE option;\nDEFINE FIELD OVERWRITE rateLimitMax ON api_key TYPE option;\nDEFINE FIELD OVERWRITE requestCount ON api_key TYPE int DEFAULT 0;\nDEFINE FIELD OVERWRITE remaining ON api_key TYPE option;\nDEFINE FIELD OVERWRITE lastRequest ON api_key TYPE option;\nDEFINE FIELD OVERWRITE expiresAt ON api_key TYPE option;\nDEFINE FIELD OVERWRITE createdAt ON api_key TYPE datetime DEFAULT time::now() VALUE $before OR time::now();\nDEFINE FIELD OVERWRITE updatedAt ON api_key TYPE datetime DEFAULT time::now() VALUE time::now();\nDEFINE FIELD OVERWRITE permissions ON api_key TYPE option;\nDEFINE FIELD OVERWRITE metadata ON api_key TYPE option;\n\n-- Define indexes for performance\nDEFINE INDEX OVERWRITE account_provider_id ON account FIELDS providerId, accountId UNIQUE;\nDEFINE INDEX OVERWRITE verification_identifier ON verification FIELDS identifier, val UNIQUE;\nDEFINE INDEX OVERWRITE sso_provider_id ON sso_provider FIELDS providerId UNIQUE;\nDEFINE INDEX OVERWRITE oauth_app_client_id ON oauth_application FIELDS clientId UNIQUE;\nDEFINE INDEX OVERWRITE oauth_access_token_idx ON oauth_access_token FIELDS accessToken UNIQUE;\nDEFINE INDEX OVERWRITE oauth_refresh_token_idx ON oauth_access_token FIELDS refreshToken UNIQUE;\nDEFINE INDEX OVERWRITE organization_slug_idx ON organization FIELDS slug UNIQUE;\nDEFINE INDEX OVERWRITE passkey_credential_idx ON passkey FIELDS credentialID;\nDEFINE INDEX OVERWRITE phone_number_idx ON user FIELDS phoneNumber UNIQUE;\nDEFINE INDEX OVERWRITE api_key_prefix_idx ON api_key FIELDS prefix;\nDEFINE INDEX OVERWRITE api_key_user_idx ON api_key FIELDS userId;\nDEFINE INDEX OVERWRITE member_org_user_idx ON member FIELDS organizationId, userId UNIQUE;\nDEFINE INDEX OVERWRITE team_org_idx ON team FIELDS organizationId;\nDEFINE INDEX OVERWRITE team_member_idx ON team_member FIELDS teamId, userId UNIQUE;\nDEFINE INDEX OVERWRITE invitation_email_org_idx ON invitation FIELDS email, organizationId;\n"; /** * Apply the bundled schema string against the supplied SurrealDB client. * * Each top-level DDL statement is separated by `;` and executed in order. * Re-application is idempotent because every `DEFINE TABLE` / `DEFINE FIELD` * / `DEFINE INDEX` in the bundled schema uses the `OVERWRITE` flag. * * Wraps any underlying execution failure in `BetterAuthSurrealError` with * code `QUERY_FAILED`, preserving the original error as `cause`. */ export async function applyBetterAuthSchema( client: SurrealClientView ): Promise { try { await client.execute(BETTER_AUTH_SURREAL_SCHEMA); } catch (err) { throw new BetterAuthSurrealError( BETTER_AUTH_SURREALDB_CODES.QUERY_FAILED, "applyBetterAuthSchema failed", { cause: err } ); } }