External Authentication Guide
Learn how to turn users who are already signed in to your own authentication system into authenticated Fanfare consumers, without routing them through Fanfare OTP.
Overview
External authentication lets an organization with its own identity system — Auth0, Okta, a homegrown login, or any other provider — vouch for a signed-in user and hand them a real Fanfare session. Fanfare trusts your backend’s assertion instead of re-verifying the user itself, so there’s no second sign-in step for your customers.
What you’ll learn:
- The two-hop authorize/exchange flow
- Requesting an exchange code from your backend
- Exchanging the code for a Fanfare session in the browser
- Consolidating multiple external identities onto one consumer with
canonicalSubject
Complexity: Advanced
Time to complete: 30 minutes
Prerequisites
- Fanfare account with API credentials, including a secret key
- A backend server that can make outbound HTTPS requests and already verifies your users (an OIDC provider, session cookies, or equivalent)
- Fanfare SDK installed and configured client-side
When to Use External Authentication
Use external authentication when:
- Your application already has its own login system, and you don’t want to make users sign in twice
- You want signed-in users to become authenticated Fanfare consumers (not guests) as soon as they land on a Fanfare-powered experience
- You run your own identity consolidation — collapsing duplicate accounts by address, vendor signals, or another internal process — and want those consolidations reflected in Fanfare’s consumer records
If you don’t have your own authentication system, use Identified Consumers (email/phone OTP) instead.
How It Works
External authentication is a two-hop exchange. Your backend proves who the user is; the browser redeems that proof for a session. Fanfare never sees or verifies the upstream identity token — your backend is the trust boundary.
- The user signs in to your system, however you normally authenticate them.
- Your backend verifies that sign-in (validates the OIDC
id_token, checks a session cookie, etc.).
- Your backend calls Fanfare’s
POST /auth/external/authorize, authenticated with your secret key, asserting the user’s identity.
- Fanfare finds-or-creates a consumer for that identity and returns a one-time exchange code.
- Your backend returns the exchange code to the browser.
- The browser exchanges the code for a standard Fanfare session — identical in shape to an OTP login.
Step 1: Authorize From Your Backend
This call is server-to-server only. Verify your own user’s session first, then assert their identity to Fanfare:
Response:
exchangeCode is one-time-use and expires in 60 seconds. Generate it on demand during the user’s session bootstrap — never ahead of time or speculatively.
resolution tells you what Fanfare did with the identity:
claims are profile hints, not source-of-truth writes. email, fullName, and name are recognized and used only to backfill empty fields on the consumer — Fanfare never overwrites a field that’s already set.
Step 2: Exchange the Code in the Browser
Your backend hands the exchange code to the browser (as part of a page load, an API response, whatever fits your app). The browser redeems it for a real session.
With the SDK:
This installs the session automatically and, where the platform supports it, binds it to a device key via DPoP — you don’t need to store or forward tokens yourself.
Or directly over HTTP:
Response: a standard Fanfare auth session — identical to what you’d get from OTP login:
Identity Mapping
Consumers are found-or-created by the unique triple (provider, issuer, subject), scoped to your organization. The same subject from two different providers (or two different issuers under the same provider) always maps to two different identities.
Fanfare does not auto-link identities by email or phone. Two different external identities that happen to share an email stay separate consumers unless you explicitly consolidate them with canonicalSubject.
Consolidating Consumers With canonicalSubject
If you run your own identity consolidation — collapsing duplicate customer accounts by address, vendor signals, or another internal process into one canonical person — pass that person’s identifier as canonicalSubject. It’s org-scoped and must be stable for a given person (1-255 characters).
All authorize calls that assert the same canonicalSubject resolve to the same Fanfare consumer:
- The first call for a given
canonicalSubject creates the consumer (resolution: "created").
- Later calls with a new
(provider, issuer, subject) identity, asserting the same canonicalSubject, attach that identity to the existing consumer (resolution: "linked").
This is deliberate consolidation, not a convenience. Linked identities share one consumer — per-consumer purchase limits, entry limits, queue and waitlist positions, and order history are all shared across them. Someone who signs in through two different linked identities is, for every access-control purpose, one person.
Conflict Policy: The Account Identity Wins
canonicalSubject only has effect the first time an identity is created. If an identity is already bound to a consumer and a later authorize call asserts a canonicalSubject that disagrees with what’s on record — or one already claimed by a different consumer — Fanfare still authenticates as that identity’s existing consumer. It never re-links or merges consumers implicitly; the disagreement is recorded for operational review.
For example: your first authorize call for a user omits canonicalSubject, so Fanfare creates a standalone consumer for that identity. A later call for the same identity now asserts canonicalSubject: "person_42" — but the identity is already bound to its own consumer, so that consumer is who authenticates. The consumer is not retroactively moved under person_42.
Assert canonicalSubject from the first authorize call for a given identity. Retroactive re-clustering does not move consumers that already exist.
Email Backfill Across Linked Identities
If linked identities carry different emails, the consumer keeps the first non-empty email it was given. Each identity’s own email is still preserved on that identity’s record — linking doesn’t overwrite or discard it, it just doesn’t promote it to the consumer’s primary email once one is already set.
Complete Example
Backend (Node.js)
Verify your own session, then assert identity to Fanfare:
Browser (React)
Security
POST /auth/external/authorize requires your secret key and must only ever be called from your backend. Never call it from a browser, and never ship the secret key in client-side code.
- The exchange code is one-time-use with a 60-second TTL. Generate it during the user’s session bootstrap, not ahead of time — a code minted early and cached is a code that’s expired by the time you need it.
- Fanfare performs no verification of the upstream identity token. Your backend is the trust boundary: verify your own provider’s tokens or session before calling
authorize.
Troubleshooting
Exchange Code Expired
- Codes expire 60 seconds after
authorize returns them
- Generate the code immediately before exchanging it, not in advance
- Check for network latency between your backend and the browser
Unexpected resolution
resolution: "existing" when you expected "created" means this (provider, issuer, subject) triple was already authorized before — that’s expected on repeat sign-ins
- A
canonicalSubject that doesn’t produce "linked" usually means the target identity already has its own consumer — see Conflict Policy
401/403 From authorize
- Confirm the key has the
sk_ prefix, not pk_
- Confirm the key belongs to the organization you expect
- Confirm the call is coming from your backend, not the browser — publishable keys cannot call
authorize
What’s Next
- Consumer Linking - Upgrade anonymous sessions to identified accounts
- JWT Tokens - Validate admission grants during checkout
- Gates and Auth - Respond to the
authentication gate from the SDK’s journey contract