Skip to Content

Quickstart

Add login to your app in about ten minutes. This walkthrough uses the hosted Login with Foir flow — Foir renders the login screen, your app gets customer tokens back — and never defines a content model. At the end you have real customers who can sign in.

You’ll need a Foir project and an API key. If you don’t have a project yet, create one in the admin app; you do not need to add any models.

Note: This is the fast path. For the full reference — admin lane, OIDC discovery, verifying tokens yourself — see Login with Foir.

1. Register your app

A relying party is your app as Foir sees it. Declare it in foir.config.ts and push it. Relying parties are project-scoped.

// foir.config.ts import { defineConfig, defineRelyingParty } from '@foir/cli/configs'; export default defineConfig({ name: 'My App', relyingParties: [ defineRelyingParty({ clientId: 'my-app', name: 'My App', redirectUris: ['https://myapp.com/auth/callback'], allowedScopes: ['profile'], }), ], });
foir push --tenant <tenant> --project <project>

foir push creates the relying party on the first push and updates it on later ones. The redirectUris are an exact-match allow list — add your local URL (for example http://localhost:3000/auth/callback) while developing. See defineRelyingParty for every field.

2. Grant your API key the login scope

The login mutations are gated behind the auth:login_with_foir:customer scope. Add it to the key your app uses:

defineApiKey({ name: 'My App (public)', keyType: 'public', envVar: 'PLATFORM_API_KEY', scopes: ['auth:login_with_foir:customer'], });

Push again to apply it.

3. Send users to hosted login

When a user clicks “Sign in”, call customerLoginWithFoir from your server. It returns a redirectUrl on auth.foir.dev — redirect the browser there.

mutation BeginLogin($input: LoginWithFoirStartInput!) { customerLoginWithFoir(input: $input) { redirectUrl } }

Pass your clientId, the redirectUri you registered, and a returnTo path. Foir handles the password / OTP / OAuth screen from here.

4. Handle the callback

Foir sends the user back to your redirectUri with code and state query parameters. Exchange them for tokens:

mutation CompleteLogin($input: LoginWithFoirCallbackInput!) { customerLoginWithFoirCallback(input: $input) { token refreshToken customerId email returnTo } }

You now have a signed-in customer. Store the tokens however your app manages sessions — send them to the browser as bearer tokens, or swap them for an HttpOnly cookie with a first-party session (BFF).

5. Call the API as the customer

Send the access token as a bearer token alongside your API key:

curl https://api.foir.dev/graphql \ -H "Authorization: Bearer <token>" \ -H "x-api-key: <your-public-key>"

The 15-minute access token is an EdDSA JWT. To verify it on your backend, fetch the public keys from https://api.foir.dev/.well-known/jwks.json (issuer foir-customer) — never ship a verifier to the browser. When it expires, exchange the refresh token via customerRefreshToken. See Customer Authentication for the token model.

What’s next

  • How auth is billed — you’ll start accruing monthly active users as people sign in.
  • Webhooks — get customer.created events delivered to your backend.
  • Custom login domain — serve the hosted login from login.your-domain.com instead of auth.foir.dev, via customDomains on the relying party. See Config reference.
  • No backend? A verified login domain also lets a pure SPA hold an HttpOnly session with no tokens in JavaScript — see SPA (no backend).
  • Build your own login UI instead? The Token API exposes customerLogin, customerRegister, and OTP directly.
  • On React or Next.js? Skip the wiring — React & Next.js gives you a provider, hooks, and drop-in components.
Last updated on