AuthenSeeDocs

AuthenSee documentation

Zero-knowledge authentication for your app. Users prove who they are without passwords — and without your server (or ours) ever seeing their secrets.

AuthenSee authenticates your users with zero-knowledge proofs instead of passwords. Users enroll their factors — a passkey, plus an image-point or motion-gesture factor depending on your policy — once, on their own device, and reuse them across every app that integrates AuthenSee. Your backend receives a signed result; nobody's server ever sees a secret.

You integrate AuthenSee the same way you integrate a payment provider: mint a session on your backend, hand the user to a co-branded hosted flow, and exchange a one-time result code server-side.

Quickstart

Add AuthenSee login to a web app in about five minutes.

Get your API key

Create a provider in your AuthenSee dashboard and copy the secret key (sk_live_... or sk_test_...). Keep it on your server — it authenticates every server-to-server call. While you're there, add your callback origin (for example https://app.example.com) to the Allowed callback origins list; sessions that name a callback URL outside this list are rejected.

Mint a session on your backend

A session scopes one enrollment or login. Create it server-side with your secret key:

curl -X POST https://api.authensee.com/v1/sessions \
  -H "x-api-key: sk_live_9aB3xK7mQ2wE5rT8yU1iO4pL6s" \
  -H "Content-Type: application/json" \
  -d '{
    "scope": "full",
    "externalUserId": "user_12345",
    "callbackUrl": "https://app.example.com/authensee/callback"
  }'
{
  "sessionId": "9182e25e-0b7b-4d92-ae98-a3c710c905c1",
  "sessionToken": "sess_aG25OA7mEnbOSX2J8UwnEE-GhoRdTLxN2hVeFxhWons",
  "flowCode": "flow_UbxOJPFbQYyQ3wNhb1LEXBWGqXWKFxfvBjUKY9GmJao",
  "hostedUrl": "https://auth.authensee.com/flow/flow_UbxOJPFbQYyQ3wNhb1LEXBWGqXWKFxfvBjUKY9GmJao",
  "scope": "full",
  "expiresAt": "2026-07-09T12:00:00.000Z"
}

Hand the browser hostedUrl — a single-use link to the hosted flow. Never expose sessionToken to the browser.

Open the hosted flow

Install the drop-in and open the flow in a popup from a click handler:

npm install @rebellion-systems/authensee-embed
import { open } from '@rebellion-systems/authensee-embed';
 
button.addEventListener('click', () => {
  open({
    // Fetch the hostedUrl from your backend (step 2). Passing a function
    // keeps the popup gesture-safe: it opens immediately and navigates
    // once the URL resolves.
    flowUrl: async () => {
      const res = await fetch('/api/authensee/session', { method: 'POST' });
      const { hostedUrl } = await res.json();
      return hostedUrl;
    },
    onComplete: ({ authResultCode }) => {
      // Send the one-time code to your backend for exchange (step 5).
      fetch('/api/authensee/complete', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ authResultCode }),
      });
    },
    onError: ({ code, message }) => console.error(code, message),
  });
});

The user enrolls or authenticates inside the popup, on AuthenSee's origin — the only place the passkey ceremony is allowed to run. A full-page redirect to hostedUrl works too; see the hosted pages guide.

Relay the callback

When the flow finishes, the popup lands on your callbackUrl with a one-time result code. On that page, one call relays the result back to your opener page and closes the popup:

// https://app.example.com/authensee/callback
import { relayCallback } from '@rebellion-systems/authensee-embed';
 
relayCallback();

Exchange the result code on your backend

The authResultCode is single-use and short-lived. Exchange it server-side with your secret key for the signed auth result:

curl -X POST https://api.authensee.com/v1/auth-results/exchange \
  -H "x-api-key: sk_live_9aB3xK7mQ2wE5rT8yU1iO4pL6s" \
  -H "Content-Type: application/json" \
  -d '{ "authResultCode": "ar_bxvS6dFBzoJKW3BQxYtDm4qGh8N2LcVp" }'
{
  "id": "ar_bxvS6dFBzoJKW3BQxYtDm4qGh8N2LcVp",
  "status": "authenticated",
  "providerId": "01906dd2-4c8a-7bb3-b7f0-5e12c9a4d833",
  "providerSubject": "sub_9f3kQzWx7RmT2yLpB8vN4cD6",
  "externalUserId": "user_12345",
  "sessionId": "9182e25e-0b7b-4d92-ae98-a3c710c905c1",
  "challengeId": "01917f8a-2e5b-7d4c-9a1f-6b3e8c4d6f0a",
  "personaType": "human",
  "factorsVerified": ["image_points", "passkey"],
  "schemeId": "passkey_question_v1",
  "authTime": "2026-07-09T11:02:14.000Z",
  "expiresAt": "2026-07-09T11:12:14.000Z",
  "token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpc3MiOi...",
  "tokenType": "auth_result"
}

providerSubject is your stable identifier for this user, and token is an EdDSA-signed JWT you can verify against https://api.authensee.com/.well-known/jwks.json. The user is authenticated — create your application session.

Prefer typed calls over raw HTTP on your backend? @rebellion-systems/authensee-sdk wraps steps 2 and 5 — see the SDK overview.

Why zero-knowledge

PropertyWhat it means for you
No passwordsNothing to phish, stuff, or rotate — authentication is a proof of knowledge, not a stored credential
Server-blind factorsThe AuthenSee server verifies proofs without ever seeing answers, gestures, or keys
Exposure-resilientA full database dump reveals only opaque commitments and spent nullifiers — nothing exploitable
Replay-proofEvery proof carries a single-use nullifier; a captured proof can never be reused
Enroll once, use everywhereUsers reuse the same persona across every AuthenSee-integrated app
On-device provingProofs are generated on the user's device (WASM in the browser, native on mobile)

Supported factors

FactorDescription
PasskeyFIDO2/WebAuthn hardware-backed key. Every persona enrolls one.
Image pointsThe user selects memorable points on an image
Motion gestureThe user performs a memorable continuous motion, matched against an on-device template
Agent keypairRaw ECDSA-P256 keypair for headless agent personas (no WebAuthn ceremony)

You choose a factor combination for your provider — passkey_and_image_points (default), passkey_only, or passkey_and_behavior — and it determines which factors your users enroll and prove at login. Every persona also opportunistically registers a passkey-only factor at enrollment, so you can later switch to passkey_only without forcing re-enrollment. See Providers, personas, and policies.

Where to go next


Built by Rebellion Systems.

On this page