epass

In active development

The identity provider you can run, read and keep.

epass is an OAuth 2.1 authorization server you host yourself. Passkeys, one-time codes, second factors and the device grant for every application you build. No per-user meter, no surcharge to turn on the enterprise features, and no proprietary extension model to unlearn when you leave.

One binary and a PostgreSQL database. No agent, no sidecar, no phone-home.

Three reasons teams move, and what epass does about each

Almost nobody replaces an identity provider because the login screen was wrong. They replace it because of the invoice, the exit cost, or who ends up carrying the pager.

  • The bill scales with something you do not control

    Monthly-active-user pricing turns a good quarter into a budget emergency, and the features that close enterprise deals sit behind a per-connection surcharge.

    epass has no meter in it. There is no code in the product that counts your users, because self-hosting means the software is not in a position to bill you for them.

  • Leaving takes a quarter

    Logic written against a vendor's own hooks and rules engine does not port anywhere. The longer you stay, the more of your auth behaviour lives in someone else's runtime.

    There is nothing proprietary to port. epass has no rules engine, no hook runtime and no plugin SPI. Your integration is standard OAuth 2.1, so it points at something else the day you decide it should.

  • Self-hosting turns into a second job

    The open-source escape route usually arrives as a JVM, a clustering story, an extension API and an upgrade path that your custom code has to survive.

    epass is one ahead-of-time compiled binary and a Postgres schema with a tracked migration runner. No JVM, no cluster to operate, and no extension API whose upgrades can break code you wrote.

How the choices differ

Written to be argued with. The last column is where epass is behind today, and it is on this page for the same reason the rest of it is.

epass compared with the two shapes it is usually weighed against.
  epass Hosted CIAM Self-hosted incumbent
What the price scales with Nothing in the product counts users Monthly active users Your own infrastructure
Cost to enable enterprise SSO No surcharge exists Often per connection, per month None
Proprietary extension code None. No hooks, no rules engine Vendor hooks and actions Provider SPIs in a JVM
What you deploy One native binary and Postgres Nothing. It is theirs JVM, cluster, cache tier
Passkeys First-class, on every account Usually a paid tier Supported, configuration-heavy
Can the provider correlate your users across your apps? No. Identifiers are per-application Yes, by design Yes, unless you build around it
Sign-in without JavaScript Every flow, including passphrase and codes No Partly
SAML, SCIM and OIDC id_tokens Not built yet Yes Yes

What it does today

One authorization server for all of your applications, with the sign-in methods people already have on their devices.

Ways people sign in

  • Passkeys, first

    WebAuthn sign-in with the platform authenticator people already unlock their device with. Nothing to remember, nothing to phish. Cloned-authenticator detection locks a credential that starts counting backwards.

  • Codes and links

    One-time codes and embedded magic links over email or Telegram, for the people and the devices that passkeys do not reach yet.

  • Passphrases, done carefully

    Argon2id hashing, a length-first strength rule instead of character classes, and a breach check against known-exposed passwords before one is accepted.

  • Second factors that recover

    TOTP with single-use recovery codes, step-up verification before a factor can be turned off, and lockout after repeated failures. A lost phone is an inconvenience, not a support ticket you cannot close.

The OAuth surface

  • OAuth 2.1, not opt-in

    PKCE with S256 is required of every client, confidential ones included. There is no setting that turns it off, because the implicit and password grants were never written.

  • Refresh tokens that notice theft

    Every refresh rotates. Presenting a token that has already been used revokes the entire family it belongs to, in one transaction, rather than serving the attacker.

  • Device grant for TVs and CLIs

    RFC 8628 with a human verification page, so hardware without a browser gets the same authorization server as everything else.

  • Introspection and revocation

    Your own services validate a token by asking, and a compromised token dies the moment you say so. Opaque tokens mean revocation takes effect immediately, not at expiry.

  • Public clients, no shared secret

    SPAs, mobile apps and CLIs register as public clients and authenticate by PKCE alone. Browser origins are validated against the client's own registered redirect URIs, so there is no second CORS list to keep in sync.

  • Headless when you need it

    A JSON API for the whole sign-in ceremony, so you can build the screens yourself and still get the security work for free.

Running it

  • Realms, not tenants-in-a-column

    Each realm is its own population of users, its own clients, its own policy. Isolation is enforced in every query, not by a filter someone has to remember.

  • Accounts that cannot be enumerated

    Sign-in, code send, reset and passkey start return byte-identical responses for an address that does not exist, down to the lockout state. Your user list is not readable from the outside.

  • Every screen works without JavaScript

    The sign-in surface is server-rendered. Scripts enhance it and never carry it, which makes the whole flow auditable by reading HTTP rather than a bundle.

  • Operations that are already there

    Tracked migrations, a health endpoint, rate limiting across the auth and token surface, forwarded-header trust that defaults to trusting nothing, and a reaper that deletes expired rows instead of keeping them.

We can't lose what we never hold.

That is a statement about how epass is built, not a promise about how it is run. Four properties, each one a thing the code does rather than a policy someone maintains:

Pairwise identities
Every application sees a different identifier for the same person. Two of your apps comparing notes learn nothing, and neither does epass.
Contact details encrypted at rest
Email addresses and phone numbers are stored encrypted. A copy of the database is not a mailing list.
Secrets stored as hashes
Tokens, one-time codes and client secrets exist in the database only as SHA-256 hashes. The plaintext never lands there at all.
Self-hosting as the default
Run it on your own infrastructure against your own PostgreSQL. The strongest proof that a vendor is not harvesting your users is that the vendor is not in the path.

Getting the first user signed in

Three requests. A developer with an epass account registers their own client without an administrator, and there is no SDK to install because there is no proprietary handshake to wrap.

  1. Register the client

    Self-serve, with your own account token. The client is public and PKCE-only by construction: it is never issued a secret to leak.

    POST /applications
    Authorization: Bearer <your account token>
    
    { "name": "Ledger", "redirectUris": ["https://ledger.example/callback"] }
  2. Send people to sign in

    The authorization endpoint, with a code challenge. epass renders the sign-in and consent screens, so the passkey ceremony is not yours to implement.

    GET /authorize
      ?client_id=<client id>
      &redirect_uri=https://ledger.example/callback
      &response_type=code
      &code_challenge=<S256 challenge>
      &code_challenge_method=S256
  3. Exchange the code

    You get an access token and a rotating refresh token. Your services check a token with the introspection endpoint, and read the signed-in person with /oauth/userinfo.

    POST /token
    grant_type=authorization_code&code=<code>&code_verifier=<verifier>

Everything above is discoverable at /.well-known/oauth-authorization-server, and worked examples for a CLI, a browser-only SPA and a server-rendered app ship in the repository.

What epass is, and is not

The second list matters as much as the first. epass holds credentials for signing in to your applications: it is not a place to keep your own secrets, and it is not trying to become one.

It is

  • An OAuth 2.1 authorization server
  • A passkey and second-factor registry
  • User management, scoped per realm
  • A device-approval authenticator for your desktop
  • Software you can read, run and keep

It is not

  • A password vault for the rest of your life
  • An autofill extension with access to every site you visit
  • A place to share secrets with other people
  • An analytics product wearing a login page
  • A profile of your users, assembled across your apps

Not built yet

A page that lists only what a product does is a page you have to verify yourself. These are the things buyers ask for that epass does not have today, named here rather than discovered in week three of an evaluation.

  • SAML and SCIM Designed and reviewed, no code. If your buyer requires either one to sign, epass is not ready for that deal yet.
  • OpenID Connect id_tokens epass is OAuth 2.1 with opaque tokens and an introspection endpoint. That makes revocation immediate, and it means a drop-in OIDC client library will not configure itself against epass today.
  • Machine-to-machine clients The client credentials grant is absent, so a service or an agent that needs its own identity cannot get one from epass yet.
  • Audit trail and admin console Administration is an HTTP API. There is no exportable event log and no console for someone who does not want to use one.

Built on the standards, not beside them

Nothing here is a proprietary handshake. If your client library speaks these, it speaks epass.

  • OAuth 2.1
  • The OAuth 2.0 Framework · RFC 6749
  • PKCE · RFC 7636
  • Token Revocation · RFC 7009
  • Token Introspection · RFC 7662
  • Authorization Server Metadata · RFC 8414
  • Device Authorization Grant · RFC 8628
  • WebAuthn · FIDO2
  • TOTP · RFC 6238

Run it before you trust it

That order is the whole argument. The software is one binary against your own database, so nothing on this page has to be taken on faith.