Concepts
A few ideas explain almost everything in Usher: where things live, who issues tokens, where people sign in, and how long each token lasts.
Workspaces, projects and environments
- A workspace is your team or company in Usher Console. Its members and its audit log belong to it.
- A project is one product: your web app, or a partner portal. A workspace can hold many.
- An environment is one running copy of a project, usually development and production. Environments are where people, applications, keys and settings live.
Workspace · Acme Ltd
Project · Web
- development · wise-otter-42
- production · acme
Project · Partner portal
- development · calm-heron-17
- production · acme-partners
Environments share nothing that matters. Each has its own Postgres schema, its own signing keys, and its own secrets sealed under its own data key. A person who signs up in development does not exist in production, and a token from one can never be verified with the other’s keys.
Issuers
Each environment is its own OpenID Connect issuer, at its own host on the tenant domain:
https://<handle>.id.hallelo.net- Production environments have a handle you choose. Handles that read as Usher, a sign-in service or a well-known brand are refused, and a handle once used is never given out again.
- Development environments get a random handle, such as
wise-otter-42, which nobody will mistake for a brand.
The issuer is fixed when the environment is created, because every token it signs names it in iss, and every app that trusts it checks that value.
The tenant domain is a different registrable domain from Usher’s own (usher.hallelo.net), so a customer’s sign-in pages are never same-site with Usher Console, and cookies are host-only: a browser sends an environment’s cookies to that environment alone.
The Lobby
The Lobby is the sign-in page each environment hosts at its issuer. Your app sends people there; it signs them in and sends them back.
- It always names the app the person is going to, in the “Continuing to” chip.
- It offers only the methods the environment allows: passkeys, emailed codes, magic links, social accounts (Google, GitHub, Microsoft, Apple) and passwords.
- It can require a second factor: an authenticator app (with backup codes) or a passkey.
- It can be open to anyone, or limited to a guest list of addresses and domains.
It looks the same whichever product sent the person. That is deliberate: people learn what a real Lobby looks like, and notice when a page isn’t one.
Usher accounts
Most environments have their own users. Usher accounts are different: one account that signs a person in to every product that uses Usher accounts, such as Mentor, Sidekick and Hallelo. People manage theirs at account.usher.hallelo.net.
Tokens and lifetimes
| Token | Default | Can be set to | What it’s for |
|---|---|---|---|
| Authorisation code | 5 minutes | Fixed | Swapped once for tokens. Single-use. |
| ID token | 10 minutes | 1 to 60 minutes | Tells your app who signed in. Not for calling APIs. |
| Access token | 10 minutes | 1 to 60 minutes | Sent to your API, which checks it. |
| Refresh token | 30 days | 1 to 90 days | Gets new access tokens. Rotates on every use. Needs offline_access. |
| Machine access token | 15 minutes | 1 to 60 minutes | For client_credentials: servers with no person behind them. |
| Emailed code, magic link | 10 minutes | Fixed | Single sign-in. A code allows three tries. |
Sessions in the Lobby have lifetimes too: a session ends after 7 days without use (1 to 30), and after 30 days in all (1 to 90). Changing something sensitive, such as an email address, needs a sign-in from the last 10 minutes.
Signing keys
Tokens are signed with RS256 by default; an environment can use ES256 or EdDSA instead. The public keys are published at the environment’s jwks_uri, so your libraries fetch them, and pick up new ones when keys are rotated.
Scopes
| Scope | What your app receives |
|---|---|
openid | Required. An ID token with the person’s sub. |
profile | Their name and picture. |
email | Their email address, and whether it’s verified. |
offline_access | A refresh token, so they stay signed in. |