The core principle — delegate, don't build
Everything below flows from one idea: delegate identity to the hosted Gate; keep application permissions in your app. Obelisk owns passkeys, multi-factor authentication (MFA), time-based one-time password (TOTP) step-up, and account recovery. A token reports the authentication strength actually proved; recovery is not silently treated as a passkey sign-in. Check the required assurance before sensitive actions.
Hosted Universal Login. The sign-in experience lives at /auth on obeliskgate.com. Obelisk owns the login UI, passkeys, TOTP, and recovery centrally, so hardening any of them is a single deploy that updates every app at once — and your app never renders a credential field. This is the modern (Passport v2) path; the older per-app callback bridge is kept only for compatibility.
Founders often wire the oldest path out of habit. If you're integrating today, choose edge-gate or the SDK below — not the v1 script bridge.
Three ways to integrate — you pick one
Integration is a decision, not a lock-in. All three modes delegate to the same hosted Gate; they differ only in how much (if any) code lives in your app.
| Mode | What you add | Best for |
|---|---|---|
| Edge-gate | Zero app code — one reverse-proxy / Worker in front | Static sites, admin panels, internal tools |
| SDK drop-in (private beta — request access) | ~3 files with @vaultspark/obelisk-auth | Next.js, Express, full-account apps |
| v1 bridge (legacy) | One <script> tag — no install, no secret | Legacy pages only — deprecated |
1 · Edge-gate — zero app code. If the whole surface is just "signed-in or not," you wire no auth into the app at all. Put a standard OIDC forward-auth proxy in front of it (oauth2-proxy, or Caddy forward_auth to a tiny verifier) pointed at /.well-known/openid-configuration. The proxy owns the callback and the session and only forwards a request once a valid Obelisk session exists — anyone without one is bounced to /auth. Your application is never touched; the work is one-time proxy config.
2 · SDK drop-in — about three files. (private beta — request access) For apps that need per-user data or a full account, the official @vaultspark/obelisk-auth SDK does the redirect, PKCE exchange, local ES256 verification, and refresh rotation. On Next.js it's three App Router route files (login, callback, logout) plus getSession. See the SDK section below.
3 · v1 bridge — one script tag (legacy). The served /auth-client.js wires data-obelisk-signin / -signup / -recover buttons to redirect to the Gate — no install, no secret. It is the deprecated Passport v1 path, retained only as a measured compatibility bridge. New builds should choose edge-gate or the SDK, not this.
The mental model
Obelisk owns authentication — proving who a user is. Your app keeps owning your data — everything that user does. The two meet at one stable value: the user's Obelisk subject id (sub). You never see or store passwords; Obelisk never touches your database.
One line to remember: Obelisk hands you a signed subject plus only the claims your registered scopes allow. You map sub to your own row; profile claims are attributes, never account ownership by themselves.
- Obelisk handles: passkeys (WebAuthn), magic links, TOTP step-up, recovery codes, rate-limiting, lockout protection, the audit trail.
- You handle: your application data, your authorization rules, your UX after login.
- The bridge: a verified ID token (or a server-to-server session check) carrying the user's
sub.
How it works with your own database
This is the part teams ask about most. Obelisk is not a database for your app — it's the gate in front of it. You keep your existing users table; you just stop storing credentials in it.
Before Obelisk, a typical users table holds email, password_hash, mfa_secret, reset_token… all of it sensitive, all of it your liability.
With Obelisk, you drop every credential column and add one:
| Column | Holds | Source |
|---|---|---|
obelisk_sub | The stable subject id for this user | Obelisk ID token (sub claim) |
email, name, … | Profile (optional, cached) | Obelisk claims, or your own forms |
| your app columns | Whatever your product needs | You |
On a user's first sign-in, you just-in-time provision: look up obelisk_sub; if it's new, insert a row. On every later sign-in, find the row by sub. To migrate an existing account, require an authenticated legacy session plus an Obelisk sign-in, a verified exact email match, and a collision-free issuer/subject binding. Never bulk-link accounts by email alone.
Zero credential storage. No password hashes, no MFA secrets, no reset tokens in your database — so they can't leak from your database. That single change removes the most common and most damaging class of breach from your surface area.
Your data stays yours, in your database, under your control. Obelisk only ever answers one question: "is this the user they claim to be?"
Your Trust Flight Plan
Every integration begins with a versioned Project Trust Profile. It records whether the project is public, private, hybrid, or custom; which humans and agents use it; its environments, resources, redirect URIs, data class, and required assurance. Declared intent and live observation stay separate so missing evidence can never appear green.
- Classify: declare exposure, constituencies, data, and human/agent access.
- Register: receive an active public or confidential client with exact callbacks, scopes, grants, and resources.
- Integrate: use Passport v2 OIDC Code + S256 PKCE; public clients hold no secret.
- Verify: prove discovery, callback rejection, token claims, refresh rotation, health, logout, and rollback.
- Canary: enable a bounded cohort while Passport v1 remains a measured rollback bridge.
- Attest: publish fresh evidence; only then can enforcement turn green.
How rollouts stay honest: trust profiles and migration plans are signed artifacts that each app's own team reviews and applies in their own repository — Obelisk never reaches into your codebase to manufacture conformance. The same profile drives a guided registration for your app, and enforcement only turns green after your deployment publishes fresh evidence.
The machine contract is project-trust-profile/v1; Passport is independently versioned at v2, so policy can evolve without forcing another authentication rewrite.
Quickstart — standard OIDC
Obelisk is a standards-compliant OpenID Connect provider, so any OIDC client library works. Discovery lives at /.well-known/openid-configuration; protected-resource metadata at /.well-known/oauth-protected-resource; keys at the JWKS endpoint discovery identifies.
- Register your app with exact production and staging callback URLs, permitted scopes, and a public or confidential client type. Use My Apps where self-service registration is enabled, or request a managed registration.
- Send the user to
/auth/authorizewithresponse_type=code, Proof Key for Code Exchange (PKCE) using S256, and fresh randomstateandnonce. Keep the verifier, state, and nonce in protected, short-lived request storage. - Exchange the code at
/auth/tokenfor an ID token + access token (+ a rotating refresh token whenoffline_accessis granted). - Validate the callback against the saved state, then verify the ID token signature, issuer, client audience, expiry, and saved nonce against discovery and the JSON Web Key Set (JWKS). Do not use the API resource audience as the ID-token audience.
- Create your own session and look up or provision the user by the verified issuer +
sub. Clear your application session when signing out, as well as redirecting to Obelisk logout.
The resource parameter is optional. If supplied, it must exactly match a registered resource and becomes the access-token audience; otherwise the audience is the client ID. Request email for email claims and profile for profile claims. Require email_verified === true before an email-dependent admission or account-linking decision; never use an email address alone as proof of account ownership.
Standard OIDC libraries can handle protocol validation; your app still owns its request storage, session cookie, data mapping, and permissions. PKCE is required. Refresh tokens require the registered grant and offline_access; each successful refresh rotates them.
The first account — all the way back to your app
A new person follows the Gate's passkey enrollment and backup setup. If your client requires verified email, the Gate pauses the original sign-in for address verification. The person opens the email in the same browser, confirms it, then returns to the original tab and selects Continue. The email proof does not upgrade authentication strength or create a new login session.
Enrollment follows the environment's signup policy: invite-only deployments still require an invitation. A registered callback alone is not a completed integration. Test a fresh account, returning sign-in, recovery, refresh, and logout on staging before enabling production traffic.
Manage callbacks and client credentials
My Apps supports personal and organization-owned clients where self-service is enabled. Declare human and agent access separately, register exact callbacks, and keep staging in the allowlist. Editing, secret rotation, and revocation require a sufficiently strong session and an authorized owner or organization administrator.
Public clients use none and hold no client secret. Confidential clients must protect their secret on the server. A newly issued secret is shown once; rotation invalidates the previous secret immediately. Coordinate deployment before rotating, and never paste a secret into browser code, support messages, or logs. Concurrent edits require a fresh revision instead of silently overwriting another change.
Sign in without leaving your page
The classic flow sends a person to obeliskgate.com/auth and back. Since the W272 release, a registered project can keep them on its own page — three standards-based transports, each fail-closed to origins registered with the Gate, each leaving the same signed receipt:
| Transport | How it feels | Works where |
|---|---|---|
| Popup hand-back | A small Obelisk window runs the passkey ceremony and posts the authorization code back to your page (response_mode=web_message, origin-bound to your registered redirect_uri). | Every browser |
| FedCM | The browser's own account chooser shows an Obelisk prompt inline — no window, no redirect (/.well-known/web-identity). | Chrome, Edge |
| Passkey on your origin | The passkey prompt appears on your domain (WebAuthn related origins, /.well-known/webauthn) and POST /auth/authorize/direct turns the assertion — or a fresh registration — into a code. | Browsers with Related Origin Requests |
The browser helpers ship with the SDK: openPassportPopup(), signInWithFedCM(), directGrant(). All three require Passport v2 registration (a client_id plus exact redirect URIs); a project still on the v1 return= flow keeps the redirect until it registers. Step-up (TOTP) inside the direct grant is refused with a pointer to the popup — the ceremony that needs the Gate's own page runs on the Gate's own page.
What never happens: Obelisk is never embedded in an iframe, never widens its cookie to your domain, and never hands a code to an origin it did not register. In-place sign-in changes where the person stands, not what the Gate trusts.
Skip the boilerplate — the official SDK
The SDK is in private beta. @vaultspark/obelisk-auth is not yet on the public npm registry — request access and we'll get you the package. You don't have to wait for it: the edge-gate and standard-OIDC paths above use only a stock OIDC client and work today, against the same hosted Gate. The snippet below is the SDK's shape.
You don't have to hand-wire the steps above. The official SDK, @vaultspark/obelisk-auth, does the redirect, the PKCE code exchange, local signature verification against the JWKS, and rotation-aware refresh. Your app still validates saved request state and owns its application session and permissions.
npm install @vaultspark/obelisk-auth # private beta — request access
import { createObeliskAuth } from "@vaultspark/obelisk-auth";
const auth = createObeliskAuth({
issuer: "https://obeliskgate.com",
clientId: "YOUR_APP", // your registered client_id
redirectUri: "https://yourapp.com/auth/callback",
requireVerifiedEmail: true, // fail closed before any account link
});
const pending = await auth.beginLogin();
// Save pending.state, pending.nonce, and pending.codeVerifier in protected
// short-lived request storage; redirect to pending.url. At the callback,
// consume that saved request once and pass the returned state explicitly:
const { tokens, claims } = await auth.completeLogin({
code, codeVerifier: pending.codeVerifier, nonce: pending.nonce,
state: returnedState, expectedState: pending.state,
});
const { ok, claims: live } = await auth.verifyAccessToken(bearer); // LOCAL — no round-trip
// Map verified claims.iss + claims.sub to your row; create your app session.
On Next.js? @vaultspark/obelisk-auth/next exports createNextHandlers — drop-in App Router route handlers for login, callback, logout, and getSession, so the wiring is three one-line files. Your app holds the cookie secret; Obelisk never sees it.
Everything is a machine contract
The entire API is published as a standard OpenAPI 3.1 document — point an SDK generator, Postman, or your API gateway straight at it and the whole surface is typed for you:
curl -s https://obeliskgate.com/openapi.json
Building for the agent era? Obelisk ships a purpose-built discovery pair beside it: /agents.json (a structured capability + safety contract) and /.well-known/llms.txt (the plain-text convention agents try first), plus a single secret-free introspection endpoint at /agent that returns Obelisk's whole contract — capabilities, read surfaces, callable actions — in one GET. All of them name the same OIDC, JWKS, MCP, and organization entry points.
Wiring tools, not a web app? Connect through the MCP trust flight — explicit capability, receipt, and rollback boundaries — or read the agent integration guide. Turning one identity into a team boundary? See Organizations. Full reference: the API manifest and OpenAPI document.
Human sign-in and agent authority are separate
Interactive OpenID Connect sign-in represents the human who completed the ceremony. It does not turn an app, script, or agent record into an authorized AI agent. Agents use a separately approved, scoped runtime credential with its own ownership and revocation policy. Verify the credential appropriate to your endpoint, then enforce principal type, audience, scope, and assurance.
// After validating the interactive ID token:
const principal = claims.obelisk?.principal;
if (principal?.type !== "human") throw new Error("Human sign-in required");
// Agent API calls use the separately documented agent grant contract.
Humans register and manage their agents in Agent Studio, where each agent gets its own scoped, revocable credential and a receipt trail. Protecting an MCP server or other tool surface? Put it behind Obelisk via the MCP trust flight — agent calls carry sender-bound (DPoP) tokens, so a leaked bearer token without the client's key is inert, and every mutation leaves an attempt-and-outcome receipt.
Watch it end to end
Six steps, and you only write the last one. Press play, or click any step — the payload shows what's actually moving.
Public clients — zero project secrets
Browser and edge applications register as public clients with token authentication method none. They hold no client secret: S256 PKCE, an exact callback allowlist, one-time codes, and registered resource audiences bind the flow. Server applications may instead register as confidential clients when their deployment can protect a secret.
The old thin-RP callback session and POST /auth/verify-session are Passport v1, retained only as a deprecated, measured rollback bridge. New integrations use Passport v2 OIDC; no bearer-like session appears in a callback URL.
Drop in the verified seal
Want the fastest possible proof on your login page? Paste one script tag and Obelisk renders a “Secured by Obelisk Gate” login card — served live from obeliskgate.com inside a framed card, so it can't be forged, auto-updates itself, and shows your site's live Obelisk Rating with a ✓ that links to your verified relying-party page.
<script src="https://obeliskgate.com/embed/seal.js" async></script>
Full setup, framework snippets, and the verification model live in Add the Obelisk Gate seal.
Or be the IdP for your whole stack
Obelisk isn't only OIDC. It's a full OpenID Connect provider and a SAML 2.0 IdP (RSA-SHA256-signed, cert-pinned assertions), so beyond your own app it can be the passkey-first front door for the tools your team already uses — Cloudflare, 1Password, and Microsoft Entra over OIDC, and GitHub, Google Workspace, AWS, Slack, Atlassian and more over the SAML 2.0 IdP. Browse the connector catalogue →
Verifying tokens
Tokens are signed ES256 (elliptic-curve, node-native crypto — no custom primitives). Verify them the standard way: fetch the JWKS, match the kid, check signature, iss, aud (the registered resource, or client_id when no resource is requested), client_id, and expiry. Treat email as linkable only when email_verified is exactly true. Keys rotate; cache the JWKS and refetch on an unknown kid. Refresh tokens rotate on every use, so a stolen refresh token is detectable.
Sender-bound tokens (DPoP). For agent and server workloads where a token might ride in logs, env vars, or long-lived memory, opt in to DPoP (RFC 9449): send a DPoP proof header at the token endpoint and Obelisk binds the access token to your client-held P-256 key (cnf.jkt). Every protected call then requires both the token and a fresh proof signed with that key — a stolen token without the key is inert. The plain bearer flow is untouched; DPoP is opt-in per request.
What you never have to build again
- Password storage, hashing, and breach monitoring — gone.
- Passkey / WebAuthn ceremonies, including the orphan-credential edge cases — handled.
- MFA, step-up, and recovery flows — handled.
- Rate-limiting, lockout, and abuse protection on the auth path — handled.
- A tamper-evident audit of every login and token — built in (receipts).
You ship your product; Obelisk is the front door. See the machine-readable API manifest for endpoints, and Security for the trust model.