Railbase
GPTClaude

Authentication & identity

Auth collections, passwords, OAuth, API tokens, and RBAC roles.

Updated

Video guide —watch on YouTube ↗

Authentication is built in. You declare which collections are sign-in capable, and Railbase mounts the endpoints, hashes passwords, issues tokens, and enforces your access rules.

Auth collections

The core guarantees a built-in _users auth collection — end users sign up and sign in against it with no schema work on your part. Add further auth collections in the schema when you need separate identity pools:

var Staff = s.AuthCollection("staff")   // injects email, password_hash, verified, …
func init() { s.Register(Staff) }

You can have several (_users, staff, customers) — each has its own sign-in endpoints and an isolated email namespace. Operator/admin accounts are separate from these app auth collections: seed the first one with railbase admin bootstrap <email> (a first-run / recovery seed that refuses once any administrator exists), then onboard the rest through the invite flow (admin UI → Administrators → Invite, or POST /api/_admin/admins/invite). See the _users note in Defining your schema before naming one users.

Endpoints

Each auth collection gets, under /api/collections/{name}/:

POST auth-signup              POST auth-with-password
POST auth-refresh             POST auth-logout
POST request-verification     POST confirm-verification
POST request-password-reset   POST confirm-password-reset
POST request-otp              POST auth-with-otp
GET  auth-with-oauth2/{provider}    + /callback

Plus TOTP, WebAuthn, and (where configured) LDAP/SAML. The signed-in identity is at GET /api/auth/me. Responses carry { token, record }.

OAuth providers

Railbase ships built-in providers — Google, GitHub, Apple, Microsoft plus a batch of social ones (Discord, GitLab, Bitbucket, …). Each comes online when you give it credentials — the simplest way is the admin OAuth wizard (Settings → Auth → Edit auth methods). It writes these oauth.<provider>.* settings, which you can equally set through the settings API, railbase config set, or the env fallback below. OAuth config is read at boot, so restart after changing it:

oauth.google.enabled       = true
oauth.google.client_id     = …
oauth.google.client_secret = …
oauth.google.scopes        = openid email profile   # space-separated

For 12-factor deployments, each setting has an environment-variable fallback following the RAILBASE_OAUTH_<PROVIDER>_<FIELD> pattern:

RAILBASE_OAUTH_GOOGLE_CLIENT_ID=…
RAILBASE_OAUTH_GOOGLE_CLIENT_SECRET=…
RAILBASE_OAUTH_GITHUB_CLIENT_ID=…
RAILBASE_OAUTH_GITHUB_CLIENT_SECRET=…

Generic / custom OIDC (Keycloak, Authentik, ZITADEL, Okta, …) has two paths. The easy one is the wizard's Generic OIDC card (or the oauth.oidc.* settings): give an issuer URL + client id/secret and Railbase runs OIDC discovery (<issuer>/.well-known/openid-configuration) to resolve the endpoints for you:

oauth.oidc.issuer         = https://id.example.com   # env RAILBASE_OAUTH_OIDC_ISSUER
oauth.oidc.client_id      = …
oauth.oidc.client_secret  = …

For an IdP without discovery, list provider names in oauth.providers (env RAILBASE_OAUTH_PROVIDERS, comma-separated) and supply the endpoints explicitly:

oauth.providers                 = keycloak
oauth.keycloak.auth_url         = https://id.example.com/authorize
oauth.keycloak.token_url        = https://id.example.com/token
oauth.keycloak.userinfo_url     = https://id.example.com/userinfo
oauth.keycloak.client_id        = …
oauth.keycloak.client_secret    = …

A provider must be enabled (oauth.<provider>.enabled = true, the default) and carry a client id + secret for its /auth-with-oauth2/{provider} routes to come online; a provider with no credentials wired responds 503 not configured. Apple's client_secret is a short-lived JWT minted with railbase auth apple-secret.

Clients can discover configured sign-in options with GET /api/collections/{name}/auth-methods; the response includes password, OAuth, OTP/magic-link, MFA and WebAuthn availability without exposing secrets. These are also enforced kill-switches, not just discovery hints: a method turned off (auth.password.enabled, auth.otp.enabled, auth.totp.enabled, auth.webauthn.enabled, auth.oauth.<provider>.enabled, …) is refused at the endpoint with 403 "sign-in method is currently disabled", so disabling one actually closes it, not merely hides it in the UI.

API tokens

For server-to-server access, mint long-lived bearer tokens:

railbase auth token create --owner <user-uuid> --collection _users \
  --name "ci-bot" --ttl 720h [--scopes connector.use,…] [--tenant <uuid>]
railbase auth token list   [--owner <uuid>] [--all]
railbase auth token rotate <id> [--ttl 720h]
railbase auth token revoke <id>

--collection is the owner's auth collection and defaults to the built-in _users — a token minted against a non-existent collection authenticates but can't load its owner. Two flags narrow a token's reach and are enforced at runtime: --scopes restricts it to the plugin verbs whose manifest permission is in the list (everything else on /api/* returns 403 token_scope), and --tenant pins it to a single workspace. A token never exceeds its owner's own permissions.

Important

The raw token is shown once, on create/rotate, and can't be recovered. Copy it then; if you lose it, rotate.

Roles & permissions (RBAC)

Authorization is role-based, in three scopes — site (global), tenant (per-workspace), and plugin (plugin:<slug>, the roles a plugin declares). Manage roles from the CLI:

railbase role create site editor --desc "Can edit content"
railbase role grant  site editor posts.update
railbase role assign _users/<user-id> site editor
railbase role list-for _users/<user-id>

A subject is <auth-collection>/<record-id>_users/<id> for app users (the built-in collection), _admins/<id> for operator accounts.

The system_admin site role is immutable. In the plugin model, plugins declare their own roles in their manifest and register them under the plugin:<slug> scope at install time; the marketplace catalog marks which of those roles are billable, which is what makes per-seat billing by role work. See Licensing & seats.

For enterprise provisioning, SCIM 2.0 is always mounted under /scim/v2/*; access is gated by possession of a valid SCIM bearer token — create one with railbase scim token ... — not by a global on/off toggle. Without a token the endpoints reject every request.

Was this page helpful?Thanks for your feedback!