The foundational plugin. It answers who is this person? and what are they
allowed to do?, and it enforces both app-wide: an AuthGuard is registered as
the global APP_GUARD, so every route requires a valid session unless it is
marked @Public().
Apograph is invite-only by design. There is no public registration; the only route into an account is an admin’s invite, or — since 0.4.0 — a first sign-in through an identity provider a deployment has configured.
Install
npm install @apograph/identity-server @apograph/identity-adminAdd an identity provider package only if you want single sign-on:
| Provider | Package | Speaks |
|---|---|---|
| OpenID Connect | identity-provider-oidc | Okta, Auth0, Keycloak, Google, Entra ID… |
| GitHub | identity-provider-github | OAuth2, no identity token |
| SAML 2.0 | identity-provider-saml | A POST binding and XML signatures |
| Scripted | identity-provider-fake | Nothing — offline and deterministic |
npm install @apograph/identity-provider-oidcTo reach a directory none of them speaks, see writing an SSO provider.
Register
// apps/server/src/plugins.ts
import { IdentityPlugin } from '@apograph/identity-server';
IdentityPlugin(config.plugins.identity);With providers, the second argument carries the constructed adapters:
IdentityPlugin(config.plugins.identity, {
sso: {
providers: [{ name: 'google', provider: createGoogleProvider({ /* … */ }) }]
}
});// apps/admin/src/plugins.ts
import { IdentityPlugin } from '@apograph/identity-admin';
IdentityPlugin();The admin half goes first in the admin list. It is the one plugin contributing public routes — the sign-in and accept-invite screens — which have to render outside the gated layout the shell wraps everything else in.
What it owns
Workspaces, users, roles, permissions, memberships, sessions and tokens are its
tables, and it ships their migrations. It also seeds the three system roles —
admin, contributor, viewer — idempotently on boot, and protects them from
deletion.
Environment
Sessions and tokens
| Variable | Default | What it does |
|---|---|---|
ALLOWED_ORIGINS | the dev admin origin | Comma-separated origins allowed to make state-changing calls |
SESSION_TTL_SECONDS | 604800 (7 days) | How long a session stays valid |
INVITE_TTL_SECONDS | 604800 (7 days) | Invite link lifetime |
RESET_TTL_SECONDS | 3600 (1 hour) | Password-reset link lifetime |
LOGIN_RATE_LIMIT | 10 | Login attempts per window |
LOGIN_RATE_LIMIT_TTL_SECONDS | 60 | The window |
Root administrator
| Variable | Default | What it does |
|---|---|---|
APOGRAPH_ROOT_ADMIN_EMAIL | unset | With an email set, an admin is provisioned on boot |
APOGRAPH_ROOT_ADMIN_PASSWORD | unset | Its first password |
APOGRAPH_ROOT_ADMIN_NAME | unset | Its display name |
Provisioning is idempotent and non-destructive: an existing account with that address is left exactly as it is. This is how you get your first login.
Single sign-on
| Variable | Default | What it does |
|---|---|---|
SSO_PUBLIC_BASE_URL | first allowed origin | The origin browsers reach the API on. Builds the redirect_uri |
SSO_REQUEST_TTL_SECONDS | 600 | How long one sign-in attempt stays live |
SSO_PROVISION_DOMAINS | unset — off | Comma-separated email domains that may be provisioned just in time |
SSO_PROVISION_ROLE | viewer | Role a provisioned account gets |
SSO_ALLOW_PASSWORD_LOGIN | true | Set false to turn passwords off. The root admin is always exempt |
SSO_SESSION_TTL_SECONDS | unset | A shorter session lifetime for SSO sessions alone |
Per-provider variables — SSO_OIDC_*, SSO_GITHUB_*, SSO_SAML_* — are on
the single sign-on page, along with what a sign-in is and
is not allowed to do to an account.
Providers and a strict cookie do not mix
The plugin refuses to boot with SSO providers registered and
session.cookieSameSite: 'strict'. A strict cookie is not sent on the
provider’s cross-site redirect back, so every sign-in would fail with a generic
error and nothing in the response would say why.
HTTP surface
Sign-in, sign-out and the redemption halves of the invite and reset flows live
here, under /api/auth. Issuing invites and resets belongs to the
users plugin; identity owns the tokens table and the
redemption.
The full route list is in the endpoint reference, and the permission keys the guards check are in the permission reference.
What is not here
No email is sent
Inviting somebody returns the raw invite token once, for an admin to turn into a link and deliver themselves. The same is true of a password reset. There is no mail transport in Apograph and no configuration for one.