Integrate from Your App
Once auth is Active, you have a conformant OpenID Connect issuer. Any OIDC client library works without modification. This page covers the integration steps: discovery, the authorization code flow, token validation, and customer SMTP for magic links.
What your app needs
You need three values from the auth configuration (GET /app-services/$APP_ID/auth):
| Value | Where to find it |
|---|---|
| Issuer URL | auth.issuer_url (e.g. https://auth-1a2b3c4d.foundrydb.com) |
| Client ID | The id field of the auth configuration (auth.id) |
| Client secret | Set by the platform at enable time; retrieve it once from GET /app-services/$APP_ID/auth — the field is auth.client_secret (write-only after initial enable; store it securely on first retrieval) |
The issuer URL is the only URL you register in your OIDC library. Everything else is discovered from the discovery document.
OIDC discovery
The discovery document is served at:
https://<issuer-host>/.well-known/openid-configuration
Fetch it to confirm the issuer is live:
curl https://auth-1a2b3c4d.foundrydb.com/.well-known/openid-configuration
The document includes authorization_endpoint, token_endpoint, jwks_uri, and the full list of supported claims and scopes. Your client library reads this automatically; you do not need to hard-code any endpoint URLs.
Authorization code flow
App Auth implements the authorization code flow with PKCE. This is the standard flow for web apps and SPAs. The steps are:
- Your app redirects the user to
authorization_endpointwithresponse_type=code, yourclient_id, aredirect_uri,scope=openid email profile, and PKCE challenge parameters. - The platform serves the hosted login page. The user enters their email or clicks a social sign-in button.
- After successful authentication, the issuer redirects back to your
redirect_uriwith an authorizationcode. - Your app exchanges the code for tokens at
token_endpoint, receiving an ID token, access token, and refresh token. - Your app validates the ID token and establishes a session.
PKCE is required. The issuer does not issue tokens without a valid code verifier.
Integrate with Auth.js (NextAuth)
// app/api/auth/[...nextauth]/route.ts
import NextAuth from "next-auth";
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
{
id: "foundrydb",
name: "FoundryDB Auth",
type: "oidc",
issuer: process.env.AUTH_ISSUER_URL,
clientId: process.env.AUTH_CLIENT_ID,
clientSecret: process.env.AUTH_CLIENT_SECRET,
},
],
});
Set environment variables:
AUTH_ISSUER_URL=https://auth-1a2b3c4d.foundrydb.com
AUTH_CLIENT_ID=<auth_configuration_id>
AUTH_CLIENT_SECRET=<client_secret>
Auth.js reads the discovery document, drives the authorization code flow with PKCE, and verifies tokens against the JWKS automatically.
Integrate with oidc-client-ts (SPA)
import { UserManager, WebStorageStateStore } from "oidc-client-ts";
const userManager = new UserManager({
authority: "https://auth-1a2b3c4d.foundrydb.com",
client_id: process.env.VITE_AUTH_CLIENT_ID,
redirect_uri: `${window.location.origin}/auth/callback`,
response_type: "code",
scope: "openid email profile",
userStore: new WebStorageStateStore({ store: window.localStorage }),
});
// Redirect to login
await userManager.signinRedirect();
// Handle callback
const user = await userManager.signinRedirectCallback();
console.log(user.profile.sub); // The end-user's stable UUID
Validate tokens
The issuer signs JWTs with RS256. Your app should validate tokens using the public keys from the JWKS endpoint:
https://<issuer-host>/.well-known/jwks.json
Validate the following claims:
| Claim | Expected value |
|---|---|
iss | Your issuer URL exactly, e.g. https://auth-1a2b3c4d.foundrydb.com |
aud | Your client ID (the auth.id UUID) |
exp | Must be in the future |
sub | The end-user's stable UUID (matches _mdb_auth.users.id) |
Do not skip expiry and issuer validation. Standard JWT libraries handle these checks when you pass the issuer and audience.
Example with jose (TypeScript/Node.js):
import { createRemoteJWKSet, jwtVerify } from "jose";
const JWKS = createRemoteJWKSet(
new URL("https://auth-1a2b3c4d.foundrydb.com/.well-known/jwks.json")
);
async function verifyToken(token: string) {
const { payload } = await jwtVerify(token, JWKS, {
issuer: "https://auth-1a2b3c4d.foundrydb.com",
audience: process.env.AUTH_CLIENT_ID,
});
return payload;
}
The JWKS library caches keys and re-fetches when it encounters an unknown kid. During key rotation, the outgoing key stays in the JWKS through its grace window, so tokens signed by either the old or new key validate without any change to your app.
Read identity data from your database
Because the _mdb_auth schema lives in your PostgreSQL database, your application can query it directly with a read-only database user. This is useful for server-side lookups by email, listing users, or correlating sessions with application data.
The sub claim in the JWT is always the UUID from _mdb_auth.users.id. Use it to join auth identity data with your own application tables:
-- Find application data for the authenticated user
SELECT a.* FROM app_accounts a
JOIN _mdb_auth.users u ON u.id = $1::uuid
WHERE a.user_id = u.id;
Do not write to _mdb_auth from application code. The schema is owned by the mdb_auth role. Grant your app user SELECT on the schema only.
SMTP for magic links
Magic-link email delivery requires an SMTP server you control. There is no platform relay. SMTP credentials are supplied at enable time and stored in the platform secret store; they are never returned by any response.
The from_address and from_name values control the sender name and address on magic-link emails. Use a sending address that matches your domain's SPF and DKIM configuration so magic-link emails are not marked as spam.
To update SMTP credentials after enabling auth, disable auth and re-enable it with the new SMTP configuration. The _mdb_auth schema and your end-user data are preserved across disable and re-enable cycles.
Supported scopes and claims
| Scope | Claims included |
|---|---|
openid | sub, iss, aud, exp, iat |
email | email, email_verified |
profile | name (when available from the provider) |
The sub claim is always the _mdb_auth.users.id UUID. It is stable across sign-in methods: the same user signing in via magic link and via Google has the same sub.
Next steps
- Social Login — add Google and GitHub sign-in
- Enable Auth — key rotation, session revocation, and GDPR erasure
- App Hosting — deploy the application that uses this auth issuer