Skip to main content

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):

ValueWhere to find it
Issuer URLauth.issuer_url (e.g. https://auth-1a2b3c4d.foundrydb.com)
Client IDThe id field of the auth configuration (auth.id)
Client secretSet 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:

  1. Your app redirects the user to authorization_endpoint with response_type=code, your client_id, a redirect_uri, scope=openid email profile, and PKCE challenge parameters.
  2. The platform serves the hosted login page. The user enters their email or clicks a social sign-in button.
  3. After successful authentication, the issuer redirects back to your redirect_uri with an authorization code.
  4. Your app exchanges the code for tokens at token_endpoint, receiving an ID token, access token, and refresh token.
  5. 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:

ClaimExpected value
issYour issuer URL exactly, e.g. https://auth-1a2b3c4d.foundrydb.com
audYour client ID (the auth.id UUID)
expMust be in the future
subThe 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.

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

ScopeClaims included
openidsub, iss, aud, exp, iat
emailemail, email_verified
profilename (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