Identity only — users, provider links, sessions and passkeys. No passwords exist in this schema and none ever will.
govdeets_auth · AUTH_DATABASE_URL · prisma/auth/schema.prisma
UserA person. The only personally identifying data we hold is what the identity provider hands us at sign-in.
Properties as follows:
id:email:emailVerified:displayName:avatarUrl:status:isMockSet only for users created by the local mock providers, so a production deployment can refuse to authenticate them even if this row travels.
createdAt:updatedAt:lastLoginAt:IdentityA link between a User and an external identity provider. One user can hold several — signing in with Google and later with Azure attaches a second Identity to the same User when the provider-verified email matches.
Access and refresh tokens are deliberately absent. We authenticate against
these providers; we do not act on the user's behalf at them. When an
integration genuinely needs a token (Slack, Jira), that token belongs to an
Integration in core, scoped to an organization — not to a person's login.
Properties as follows:
id:userId:provider
azure,amazon,github,tiktok, … and the same names again in local development, served by the mock provider set.
providerUserId: The provider's own stable subject identifier for this user.providerEmail:isMock:createdAt:lastUsedAt:WebAuthnCredentialA registered passkey. publicKey is public by definition, so storing it is
safe; the private half never leaves the user's authenticator.
Properties as follows:
id:userId:credentialId: Base64url-encoded credential ID as returned by the authenticator.publicKey:signCountReplay defence: must strictly increase across authentications, for authenticators that implement a counter at all.
transports:deviceType
singleDeviceormultiDevice. A multi-device credential is synced through a platform keychain and survives losing the device.
backedUp:aaguid:nickname: User-supplied label, so someone with four passkeys can revoke the right one.createdAt:lastUsedAt:SessionA logged-in session. The cookie the browser holds is a random 256-bit token; only its SHA-256 hash is stored here, so read access to this table cannot be used to impersonate anyone.
Properties as follows:
id:userId:tokenHash:expiresAt:createdAt:lastSeenAt:revokedAt:ip:userAgent:activeOrganizationIdWhich organization this session is currently acting in — an opaque pointer into the
coredatastore's Organization. No foreign key, by design.
AuthChallengeShort-lived server-side state for flows that span two requests: the OAuth authorization round trip (state + PKCE verifier) and the WebAuthn registration/authentication challenge.
Properties as follows:
id:kind:userId:value: The WebAuthn challenge, or the OAuth state parameter.codeVerifier: PKCE code verifier. OAuth flows only.provider:redirectTo: Where to send the user once the flow completes.expiresAt:consumedAt:createdAt:AuthEventSecurity audit trail. Kept in the auth database rather than alongside the application audit log, so "who tried to get in" survives independently of the business data — and so shipping business data to an analyst never ships the authentication history with it.
Properties as follows:
id:userId:kind
signin.start,signin.success,signin.failure,session.revoke,passkey.register,passkey.remove, …
provider:success:reason: Failure reason, safe to show an administrator.ip:userAgent:createdAt: