本文へ移動
runlot
リファレンスAPIApp user authentication

App user authentication

Covers 14 API operations in the app-auth category.

MethodPathDescription
GET/v1/orgs/{orgSlug}/projects/{projectName}/authEnd-user auth status (runlot auth)
POST/v1/orgs/{orgSlug}/projects/{projectName}/authTurns login back on after it was turned off
PATCH/v1/orgs/{orgSlug}/projects/{projectName}/authChange settings (runlot auth set)
DELETE/v1/orgs/{orgSlug}/projects/{projectName}/authDisables auth (runlot auth delete)
GET/v1/orgs/{orgSlug}/projects/{projectName}/auth/schemaThe runlot_auth schema DDL
GET/v1/orgs/{orgSlug}/projects/{projectName}/auth/brandingThe sign-in page branding, defaults filled
PUT/v1/orgs/{orgSlug}/projects/{projectName}/auth/brandingReplace the sign-in page branding
GET/v1/orgs/{orgSlug}/projects/{projectName}/auth/funnelSign-in funnel counters
GET/v1/orgs/{orgSlug}/projects/{projectName}/auth/branding/previewRender the chooser with draft branding
POST/v1/orgs/{orgSlug}/projects/{projectName}/auth/branding/previewA ten-minute preview ticket
PUT/v1/orgs/{orgSlug}/projects/{projectName}/auth/providers/{provider}Register or replace a social provider (runlot auth provider set)
DELETE/v1/orgs/{orgSlug}/projects/{projectName}/auth/providers/{provider}Disconnect a login provider (runlot auth provider rm)
GET/v1/orgs/{orgSlug}/projects/{projectName}/auth/usersThe app's end users (runlot auth users)
GET/v1/orgs/{orgSlug}/projects/{projectName}/auth/users/{userId}One end user

GET /v1/orgs/{orgSlug}/projects/{projectName}/auth

Whether end-user auth is granted and configured, which login providers are registered, and the host to point provider callbacks at.

The end-user table is not part of this API. User records live in the runlot_auth schema of the project's database; read them through the worker's env.auth.users.* or with runlot pg shell.

providers carries only the client id. Client secrets are stored encrypted and never appear in any response.

Requires viewer or higher.

operationId getAuth

Status codeDescriptionResponse body
200Auth statusAuthStatus
403——
404——
503no_core — the service is temporarily unavailableError

POST /v1/orgs/{orgSlug}/projects/{projectName}/auth

This is idempotent — calling it twice leaves the settings and login providers unchanged. Auth is either on or off; there is no "on but disabled" state.

A database is required. Without one this returns 409 database_required, because the end-user tables live inside the project's database. Every project has one unless it was deleted, so you will see this only after a database delete.

Once enabled, the built-in login endpoints start serving shortly afterwards. As with env.db and env.storage, no redeploy is needed. Table initialization is idempotent, so enabling twice performs it only once.

There is no request body. Settings start at their defaults; change them with PATCH.

Requires member or higher. Audited as auth.grant.

operationId grantAuth

Status codeDescriptionResponse body
200Grant status (existing value if it was already there)AuthStatus
403——
404——
409database_required — this project's database was deleted. Create a new one with POST …/database (runlot pg on).Error
503no_core — the service is temporarily unavailableError

PATCH /v1/orgs/{orgSlug}/projects/{projectName}/auth

This is a partial update. Only the fields you send change, and the rest stay as they are. Sending every field on each save can overwrite a change another member made moments earlier.

Returns 404 not_granted if auth has not been turned on for this project — settings cannot be saved ahead of the grant.

sessionDays also sets how long an end user's session is kept.

password and magicLink turn the email-and-password and email-link methods on or off. A change that would leave no sign-in method enabled is refused with 409 last_method; turn login off instead.

Requires member or higher. Audited as auth.settings.

operationId updateAuthSettings

Request body: application/json · AuthSettingsPatch

Status codeDescriptionResponse body
200The status that changedAuthStatus
400——
403——
404not_granted — this project has no auth. When the project itself does not exist, the code is not_found, not this one.Error
409last_method — the change would leave no sign-in method enabledError
503no_core — the service is temporarily unavailableError

DELETE /v1/orgs/{orgSlug}/projects/{projectName}/auth

End-user data is not deleted. The runlot_auth tables stay in the project's database as they are — deleting them is a database operation you run yourself with runlot pg. Turning auth back on lets the same people log in as before.

Shortly afterwards the built-in login endpoints stop serving, and /__runlot/auth/* passes straight through to your worker — if that path has no route, it is a 404.

Disabling is also idempotent. It returns 204 even when auth was not enabled.

Requires admin or higher — enabling takes member, but disabling signs out everyone who is logged in. Audited as auth.revoke.

operationId revokeAuth

Status codeDescriptionResponse body
204detached—
403——
404——
409access_mode_app — the deployment's access mode is app, which needs login. Choose another access mode first; login is never turned off under it silently.Error
503no_core — the service is temporarily unavailableError

GET /v1/orgs/{orgSlug}/projects/{projectName}/auth/schema

The statements that install the runlot_auth schema, the same ones the auth system installs on first use. The CLI applies them before a project's own migrations so a migration can reference runlot_auth.users(id). Every statement is idempotent; run them in order as one script. Viewer or higher.

operationId getAuthSchema

Status codeDescriptionResponse body
200The schema version and its statementsobject
403——
404——

GET /v1/orgs/{orgSlug}/projects/{projectName}/auth/branding

settings.brand (docs/custom-sign-in.md §12.3) with every default written out, and effectiveMethods, what the chooser draws now. Viewer or higher.

operationId getAuthBranding

Status codeDescriptionResponse body
200{brand, effectiveMethods}AuthBranding
404——

PUT /v1/orgs/{orgSlug}/projects/{projectName}/auth/branding

The whole brand object (absent fields are defaults). Refused fields answer 400 invalid_branding with error.details.fields: [{path, message}]. Member or higher.

operationId putAuthBranding

Request body: application/json · SignInBrand

Status codeDescriptionResponse body
200{brand, effectiveMethods}AuthBranding
400——
404not_granted—

GET /v1/orgs/{orgSlug}/projects/{projectName}/auth/funnel

Daily counters kept by the broker (docs/custom-sign-in.md §9.4), oldest first, 90-day retention. Events: start (key: idp, chooser or silent), chooser_view, method (key: password|google|github|passkey|session), success (key: method), error (key: auth_error code, or passkey_failed). No per-person data.

operationId getAuthFunnel

ParameterLocationRequiredTypeDescription
daysqueryNointeger—
Status codeDescriptionResponse body
200{days, rows: [{day, event, key, n}]}object

GET /v1/orgs/{orgSlug}/projects/{projectName}/auth/branding/preview

The real chooser template with every link inert. Authenticated by the session bearer or by ticket (from POST), so an <iframe> can load it; framable only by this origin. /v1/projects/{projectId}/auth/branding/preview is the same by project id.

operationId previewAuthBranding

ParameterLocationRequiredTypeDescription
ticketqueryNostring—
langqueryNostring—
themequeryNostring—
statequeryNostring—
draftqueryNostring—
Status codeDescriptionResponse body
200HTMLstring
400——
401no session and no valid ticket—

POST /v1/orgs/{orgSlug}/projects/{projectName}/auth/branding/preview

A ten-minute preview ticket

operationId mintAuthBrandingPreviewTicket

Status codeDescriptionResponse body
200{url, ticket, expiresAt}object

PUT /v1/orgs/{orgSlug}/projects/{projectName}/auth/providers/{provider}

Three kinds (see AuthProviderPut). builtin registers your project's own GitHub or Google OAuth app. oidc registers any OpenID Connect provider under a project-chosen id; the CP reads its discovery document when saving. runlot (id runlot) turns on "Continue with Runlot": email, Google and GitHub at Runlot, through the sign-in broker, with nothing to register.

The callback URL to register with the provider is per host: https://<host>/__runlot/auth/callback/<provider>. The host list is in AuthStatus.hosts.

clientSecret is stored encrypted and never appears in any response again. Rotation uses the same PUT — it is idempotent, so retries are safe.

Requires admin or higher. Recorded in the audit log as auth.provider.set (the secret itself is not recorded).

operationId setAuthProvider

Request body: application/json · AuthProviderPut

Status codeDescriptionResponse body
200The status that changedAuthStatus
400invalid_provider — the id does not match the pattern, the kind does not fit the id (builtin outside github/google, runlot outside runlot, oidc on a reserved id), or clientId/clientSecret is empty. bad_request — issuer on google, a non-http(s) GitHub base, a non-https OIDC issuer, or fields a runlot PUT does not take. oidc_discovery_failed — the issuer's /.well-known/openid-configuration could not be read; the message quotes the provider's answer. managed_removed — {"source":"managed"}; use the runlot provider.Error
403——
404——
409runlot_unavailable — this deployment has no sign-in broker, so the runlot provider cannot be turned on. Register your own app instead.Error
503no_core — the service is temporarily unavailable.Error

DELETE /v1/orgs/{orgSlug}/projects/{projectName}/auth/providers/{provider}

Anyone who signed in only with that provider will no longer be able to log in. The user record remains; only the matching linked identity is removed. Registering the provider again restores login with the same account.

Deleting a login provider that does not exist also returns 204.

Requires admin or higher. Recorded in the audit log as auth.provider.delete.

operationId deleteAuthProvider

Status codeDescriptionResponse body
204Disconnected—
403——
404——
409last_method — removing it would leave no sign-in method enabledError
503no_core — the service is temporarily unavailable.Error

GET /v1/orgs/{orgSlug}/projects/{projectName}/auth/users

The people who signed up to your app — not the members of your organization. They live in the project's own database, under the runlot_auth schema, and this route reads them over the same connection …/database/connect hands you for psql. Everything here is also visible through env.db and psql; this is a convenience, not a second source.

It is read-only. Blocking a user, deleting one, or resetting a password is not on this surface — write through env.db.

An app whose login has never been used has no schema yet (it is created on the first call), and that answers an empty list rather than an error.

Paging is by cursor, newest first. Feed it back unchanged; its absence is the end.

Requires member or higher, and db:read on a token — the subscriber list is the project's data, the same class as the SQL console.

operationId listAppUsers

ParameterLocationRequiredTypeDescription
searchqueryNostringmatches the email or the name, case-insensitively
cursorqueryNostringthe cursor of the previous page
limitqueryNointeger—
Status codeDescriptionResponse body
200One pageAppUserPage
400cursor is not one this endpoint issued, or limit is out of rangeError
403——
404no such project, or the project has no database (so it can have no users)Error
502could not reach this project's database (console_unreachable)—
504the database did not answer within the round-trip deadline (console_timeout)—

GET /v1/orgs/{orgSlug}/projects/{projectName}/auth/users/{userId}

The same record as the list, for one id. Requires member or higher, and db:read on a token.

operationId getAppUser

Status codeDescriptionResponse body
200The userAppUser
400userId is not a uuidError
403——
404——
502could not reach this project's database (console_unreachable)—

このページの目次