Enable Auth
Prerequisites
Before enabling App Auth:
- Your app must be in
Runningstatus. - Your app must have at least one PostgreSQL database attached and that attachment must be healthy.
- You need an SMTP server you control. Magic-link delivery is mandatory; there is no platform relay and no silent fallback. SMTP credentials are accepted once, written to the platform secret store, and never returned by any API response.
The PostgreSQL attachment is where the _mdb_auth schema will be created. All end-user identity data (users, sessions, tokens, MFA secrets, audit log) will live there. Choose the attachment that should hold your users.
Find your attachment ID
Get the list of attachments for your app and note the id of the PostgreSQL attachment you want to use:
curl -u "$USER:$PASS" https://api.foundrydb.com/app-services/$APP_ID
The response includes an attachments array. Copy the id of the PostgreSQL entry.
Enable auth
curl -u "$USER:$PASS" -X POST \
https://api.foundrydb.com/app-services/$APP_ID/auth/enable \
-H "Content-Type: application/json" \
-d '{
"attachment_id": "'"$PG_ATTACHMENT_ID"'",
"issuer_domain_choice": "fallback",
"smtp": {
"host": "smtp.example.com",
"port": 587,
"username": "login@example.com",
"password": "smtp-password",
"from_address": "login@example.com",
"from_name": "Acme"
},
"theme": {
"display_name": "Acme",
"brand_color": "#4F46E5",
"logo_url": "https://acme.com/logo.svg"
}
}'
The theme object is optional. If omitted, the login page renders with generic FoundryDB branding.
The issuer_domain_choice is fixed at enable time:
| Value | Issuer host |
|---|---|
fallback | auth-<id>.foundrydb.com (platform-managed subdomain) |
custom | Your own domain (requires a custom domain configured on the app) |
A 201 Created response contains the auth configuration and signing key records:
{
"auth": {
"id": "...",
"app_service_id": "...",
"database_service_id": "...",
"attachment_id": "...",
"issuer_url": "https://auth-1a2b3c4d.foundrydb.com",
"fallback_domain": "auth-1a2b3c4d.foundrydb.com",
"status": "ProvisioningSchema",
"schema_version_applied": null,
"theme": { "display_name": "Acme", "brand_color": "#4F46E5", "logo_url": "..." },
"idp_providers": []
},
"signing_keys": [
{ "kid": "...", "algorithm": "RS256", "status": "active" }
]
}
Provisioning is asynchronous. Poll GET /app-services/$APP_ID/auth until status is Active. The issuer_url field contains the URL you need for OIDC integration.
Check auth status
curl -u "$USER:$PASS" https://api.foundrydb.com/app-services/$APP_ID/auth
When status is Failed, the response includes a failure_reason string describing what went wrong.
The _mdb_auth schema
Once auth reaches Active, your PostgreSQL database contains a _mdb_auth schema managed by the platform. It holds:
users— one row per registered end user, with UUID subject identifier and emailidentities— identity rows linking users to their login method (magic link, Google, GitHub)sessions— active browser sessionsrefresh_tokens— long-lived tokens for session renewalmfa_totp— encrypted TOTP secrets for users who have enrolled two-factormfa_recovery_codes— one-time recovery codeslogin_tokens— short-lived magic-link tokensoauth_codes— authorization codes in-flight during the OIDC flowevents— audit log rows
Your application code can read from _mdb_auth using a read-only database user. Writes are locked to the low-privilege mdb_auth role owned by the platform. Do not grant write access to _mdb_auth to application users; the schema is managed by the issuer and the platform only.
Rotate the JWT signing key
The signing key is a per-app RSA keypair held in the platform secret store. Key material is never returned by any API response.
Rotation is dual-key: when you rotate, the newly minted key is published in the issuer's JWKS alongside the outgoing key. Tokens signed by the previous key keep validating until that key retires. Live sessions do not see any disruption.
curl -u "$USER:$PASS" -X POST \
https://api.foundrydb.com/app-services/$APP_ID/auth/rotate-key
The response contains the new signing key record (not the key material):
{
"signing_key": {
"kid": "new-key-id",
"algorithm": "RS256",
"status": "active",
"activated_at": "2026-06-16T10:00:00Z"
}
}
The previous key enters retiring status and stays in the JWKS until it reaches retired.
Rotate signing keys regularly and whenever you suspect the key material may have been compromised.
Disable auth
Disabling auth tears down the managed issuer and sets the configuration to Disabled. It does not touch the _mdb_auth schema or your end-user data. The identity rows, sessions, and audit log remain in your database unchanged.
curl -u "$USER:$PASS" -X POST \
https://api.foundrydb.com/app-services/$APP_ID/auth/disable
You can re-enable auth on the same app. The platform will provision a new issuer and apply any pending schema migrations.
Revoke a session
To force-sign-out a specific user session, post to the revoke endpoint with the session ID from _mdb_auth.sessions:
curl -u "$USER:$PASS" -X POST \
https://api.foundrydb.com/app-services/$APP_ID/auth/sessions/$SESSION_ID/revoke
The revocation is dispatched asynchronously to the backing database's primary VM. The response returns a task ID:
{ "task_id": "..." }
Erase an end user (GDPR right to erasure)
To delete an end user and all their identity data from your database under GDPR Art. 17, address them by email or by their subject UUID:
# By email
curl -u "$USER:$PASS" -X POST \
https://api.foundrydb.com/app-services/$APP_ID/auth/users/delete \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com"}'
# By subject UUID
curl -u "$USER:$PASS" -X POST \
https://api.foundrydb.com/app-services/$APP_ID/auth/users/delete \
-H "Content-Type: application/json" \
-d '{"user_id": "00000000-0000-0000-0000-000000000000"}'
You can also use the path-based form:
# By email (path contains @)
curl -u "$USER:$PASS" -X DELETE \
"https://api.foundrydb.com/app-services/$APP_ID/auth/users/user@example.com"
# By subject UUID
curl -u "$USER:$PASS" -X DELETE \
"https://api.foundrydb.com/app-services/$APP_ID/auth/users/00000000-0000-0000-0000-000000000000"
The erasure cascades across all identity tables: users, identities, sessions, refresh_tokens, mfa_totp, mfa_recovery_codes, login_tokens, and oauth_codes. Audit rows in _mdb_auth.events are additionally scrubbed, since their JSONB metadata can carry personal data.
Erasing a user that does not exist is a no-op and returns 202 Accepted. The platform emits an auth.user.deleted webhook event referencing the user by UUID only; when the request addressed the user by email, the event uses a keyed HMAC digest, not the plaintext email.
The erasure is dispatched asynchronously and returns a task ID.
Next steps
- Social Login — add Google and GitHub sign-in buttons to the hosted login page
- Integrate from Your App — connect your OIDC client to the issuer