Concepts
Tenants, environments, applications, APIs, scopes, connections and sessions, and how they fit together.
#Tenants
A tenant is one isolated login service: its own users, applications, APIs, keys, themes, translations and settings. Most companies have one tenant per product. Each tenant has an issuer, the base URL of everything:
| Issuer | What it is |
|---|---|
https://acme.9in.io | Production |
https://acme-sandbox.9in.io | Sandbox |
https://login.acme.com | Production on a custom domain |
The issuer is the iss claim of every token and the prefix of every endpoint (<issuer>/oauth/token, <issuer>/.well-known/openid-configuration, <issuer>/api/v1/users). Your apps should be configured with the issuer and discover the rest.
#Environments
Every tenant has two environments with the same configuration but separate data:
- production: real users.
- sandbox: for development and tests. Users, sessions and activity are separate. Any password signs in (a user is created on the fly), one-time codes are always
000000, and nothing is emailed or texted. Sandbox users never count towards your plan.
Configuration (applications, APIs, branding, themes) is shared, so what you test in the sandbox is what runs in production.
#Applications
An application is anything users sign in to, or anything that calls your APIs: an OAuth client with a client ID (app_…). Its kind decides the flow:
| Kind | Client type | Grants |
|---|---|---|
| Single-page app | Public (no secret) | Authorization code + PKCE, refresh token |
| Regular web app | Confidential (secret) | Authorization code (+ PKCE), refresh token |
| Native / mobile | Public | Authorization code + PKCE, refresh token |
| Machine-to-machine | Confidential | Client credentials |
| TV & device | Public | Device code, refresh token |
First-party applications (your own, the default) skip the consent screen. Third-party applications show it, listing the scopes they ask for.
#APIs and scopes
An API is a resource server you protect with 9in, identified by an audience such as https://api.acme.com. When an app asks for a token with audience=https://api.acme.com, the access token is issued for that API (aud claim) and can only carry that API's scopes (such as orders:read). An API can also choose its own token format (JWT or opaque), signing key and lifetime; see Tokens and claims.
The OpenID Connect scopes openid, profile, email, phone and offline_access (a refresh token) are always available.
#Connections
A connection is where identities come from:
- User database: users stored in 9in (the default), or in your own database through the adapter protocol.
- Social: Google, GitHub, Microsoft, Apple and 10 more (social login).
- Enterprise: your business customers' identity providers (enterprise SSO).
A user can have several identities (a password, Google and their company login) linked to one account.
#Users
A user has an id (usr_…, or the id kept from an import) that becomes the sub claim and never changes, identifiers (email, phone, username), a profile, your own metadata, and custom profile fields. See Users and profiles.
#Sessions and tokens
When someone signs in, 9in starts a session on its domain (single sign-on: the next app they open doesn't ask again) and issues tokens to your app:
- ID token: a JWT that says who signed in, for your app only (
audis your client ID). - Access token: for your API (
audis the API identifier), JWT or opaque. - Refresh token: with the
offline_accessscope; rotated on every use.
Signing out, blocking a user or revoking a session or token takes effect at once on 9in; your API sees it as fast as its verification mode allows.
#Hosted pages
Sign-in, sign-up, password recovery, one-time codes, two-factor, consent, the device page, errors and the user's account page are all served by 9in on the tenant's domain, in the visitor's language, with your theme. Your app never handles passwords.