Developers

Integrate Obelisk

You don't build authentication — you delegate it to one hosted plane. Point your app at the Obelisk Gate and it hands back a passkey-first, MFA-protected, cryptographically verified identity, with no password or secret stored on your side. Pick one of three integration modes; here's how each fits your stack and your own database.

Last updated 2026-09-10

The core principle — delegate, don't build

Everything below flows from one idea: you never build auth per project — you point the app at the one hosted Gate. You don't stand up passkeys, password resets, MFA, TOTP step-up, or account recovery yourself; Obelisk owns all of it and hands your app a verified subject. That means the login is the same, and the same strength, everywhere.

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.

ModeWhat you addBest for
Edge-gateZero app code — one reverse-proxy / Worker in frontStatic sites, admin panels, internal tools
SDK drop-in (private beta — request access)~3 files with @vaultspark/obelisk-authNext.js, Express, full-account apps
v1 bridge (legacy)One <script> tag — no install, no secretLegacy 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:

ColumnHoldsSource
obelisk_subThe stable subject id for this userObelisk ID token (sub claim)
email, name, …Profile (optional, cached)Obelisk claims, or your own forms
your app columnsWhatever your product needsYou

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.

  1. Classify: declare exposure, constituencies, data, and human/agent access.
  2. Register: receive an active public or confidential client with exact callbacks, scopes, grants, and resources.
  3. Integrate: use Passport v2 OIDC Code + S256 PKCE; public clients hold no secret.
  4. Verify: prove discovery, callback rejection, token claims, refresh rotation, health, logout, and rollback.
  5. Canary: enable a bounded cohort while Passport v1 remains a measured rollback bridge.
  6. 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.

  1. Register your app to get a client_id and your redirect URI allow-listed.
  2. Send the user to /auth/authorize with PKCE (response_type=code, your state + code_challenge).
  3. Exchange the code at /auth/token for an ID token + access token (+ a rotating refresh token when offline_access is granted).
  4. Verify the ID token against the JWKS (ES256). The verified sub is your user key.
  5. Create your own session and look up / provision the user by sub.

Standard libraries (e.g. an OIDC client for your language) handle steps 2–4; you write step 5 against your database. PKCE is required; refresh-token rotation is on by default — or skip the wiring entirely with the official SDK below (private beta — request access).

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:

TransportHow it feelsWorks where
Popup hand-backA 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
FedCMThe browser's own account chooser shows an Obelisk prompt inline — no window, no redirect (/.well-known/web-identity).Chrome, Edge
Passkey on your originThe 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 six steps above. The official SDK, @vaultspark/obelisk-auth, does the redirect, the PKCE code exchange, local ES256 verification against the JWKS, and rotation-aware refresh — so a full integration is a few lines.

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 { url, codeVerifier, nonce } = await auth.beginLogin();      // stash + redirect
const { tokens, claims } = await auth.completeLogin({ code, codeVerifier, nonce });
const { ok, claims: live } = await auth.verifyAccessToken(bearer); // LOCAL — no round-trip
// map claims.sub to your own row. That is the whole integration.

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.

One field says human or agent

In the agent era, "who is acting" is a first-class question. Obelisk answers it in a single field: the verified session and the obelisk claim in every ID token carry a principal whose type is either "human" or "agent". Your relying party branches on that one value instead of guessing — and when the actor is an agent, the principal connects back to the human who owns it.

const { principal } = claims.obelisk;   // from the verified ID token
if (principal.type === "agent") {
  // an AI agent is acting — apply agent policy, log principal.owner
} else {
  // a human is acting
}

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.


          
Obelisk owns steps 1–5. You only write step 6.

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.