Get started

Concepts

Tenants, environments, applications, APIs, scopes, connections and sessions, and how they fit together.

View .md llms.txt

#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:

IssuerWhat it is
https://acme.9in.ioProduction
https://acme-sandbox.9in.ioSandbox
https://login.acme.comProduction 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:

KindClient typeGrants
Single-page appPublic (no secret)Authorization code + PKCE, refresh token
Regular web appConfidential (secret)Authorization code (+ PKCE), refresh token
Native / mobilePublicAuthorization code + PKCE, refresh token
Machine-to-machineConfidentialClient credentials
TV & devicePublicDevice 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 (aud is your client ID).
  • Access token: for your API (aud is the API identifier), JWT or opaque.
  • Refresh token: with the offline_access scope; 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.