Skip to main content

Enable Auth

Prerequisites

Before enabling App Auth:

  • Your app must be in Running status.
  • 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:

ValueIssuer host
fallbackauth-<id>.foundrydb.com (platform-managed subdomain)
customYour 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 email
  • identities — identity rows linking users to their login method (magic link, Google, GitHub)
  • sessions — active browser sessions
  • refresh_tokens — long-lived tokens for session renewal
  • mfa_totp — encrypted TOTP secrets for users who have enrolled two-factor
  • mfa_recovery_codes — one-time recovery codes
  • login_tokens — short-lived magic-link tokens
  • oauth_codes — authorization codes in-flight during the OIDC flow
  • events — 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