caputchin
All docs
View raw .md

Account login

How customers sign in to the dashboard. The other three management modalities (OpenAPI, MCP, Terraform) authenticate with a cpt_pat_* Personal Access Token — see management-api. This page is the dashboard side.

Three sign-in paths, one account

Path What you give the platform What we send back
Magic link An email address A single-use URL valid for 15 minutes
Continue with GitHub A GitHub OAuth consent A session cookie (after we read your primary verified email)
Continue with Google A Google OAuth consent A session cookie (after we read your verified email from the id_token)

All three resolve to the same account row keyed by your email. A customer who signs in with GitHub today and a magic-link tomorrow lands on the same account; a customer who signs in with Google to an address that already has a GitHub-bound caputchin account gets attached to the same row. The first sign-in for a new email creates the account; subsequent sign-ins find it.

What we store

One column of personal data per account: your email.

We do not store your name, your provider profile photo, your avatar, your locale, your given name, your family name, your provider display name, your timezone, or any other field the OAuth provider hands us. The provider response is read once at callback, used to assert a verified email, and discarded before any log line. See privacy.

The OAuth provider binding itself (which provider account proved control of which email) is stored in a separate table as (provider, provider_sub, account_id)provider_sub is the provider's stable user id. It's not personally identifying on its own and exists so we can detect "same GitHub account, different email later" attempts.

Magic link

You submit an email address. We HMAC-sign a token containing your email + a single-use nonce + a 15-minute expiry, and email you the URL. Clicking it atomically claims the nonce — if you click twice, only the first click signs you in. If you don't click within 15 minutes, the link is dead and you request another.

Rate limit: one in-flight magic-link per email address per 60 seconds. If you ask for a second link within that window we silently rate-limit so we don't leak whether the address exists in our account base; you still see the same "check your email" page either way.

OAuth — GitHub and Google

Both use the standard authorization-code flow. Google additionally uses PKCE; GitHub's arctic client doesn't expose it, so we rely on the signed state cookie alone — equivalent guarantee since the state is HMAC-signed and bound to the provider in the same envelope.

For both providers we require verified email:

  • GitHub — we fetch /user/emails and accept only the address where primary && verified. If your primary GitHub email isn't verified, you can't use this path; verify it on GitHub or use a magic link to the same address.
  • Google — we read email_verified === true from the id_token. Unverified addresses are rejected.

If your provider rejects the sign-in mid-flow (you click Cancel, your org's IdP refuses consent, etc.), you land back on /login with a clear error.

Sessions

When sign-in succeeds, the platform sets a caput_session cookie carrying an opaque session id. The cookie is HttpOnly, SameSite=Lax, and Secure in production. The actual session is a row in user_sessions — the cookie is just a pointer.

Property Value
TTL 30 days from last use (rolling)
Authority The database row, not the cookie
Refresh cadence last_seen_at ticks at most once per minute; full expiry rolled once per 5 days
Invalidation Sign out, or the row is deleted

Sign-out is a POST to /api/auth/sign-out from anywhere in the dashboard — the button lives in the dashboard sidebar. The server deletes the row and clears the cookie.

There is no "remember me" toggle and no "sign in across all browsers" flow at MVP. Every successful sign-in creates a new session row scoped to that browser; signing in on a second device gives you a second row.

What this is not

  • Not a single-sign-on for your customers. Caputchin's own users (you, the integrator) sign in via this flow. The end-users of the CAPTCHA never authenticate to us in any sense — see principles.
  • Not a password reset flow. There are no passwords. If you lose access to your email, you lose access to the account; recovery happens out-of-band (support).
  • Not a team / multi-user model. Each account is single-user at MVP. Multi-user organisations are deferred to the Enterprise tier — see roadmap.

See also