Auth Apps
Manage authentication services for your applications (Firebase Auth alternative). All management endpoints require Authorization: Bearer TOKEN.
These endpoints manage the end-users of apps you’ve built on top of the platform. To manage your own Ghayma account (profile, password, email), see Account. For the public sign-in endpoints your app calls (register, login, refresh, verify JWT), see the Auth Integration Guide.
Create Auth App
POST /api/v1/auth-appsBody:
{
"name": "My App Auth",
"app_id": "my-app",
"project_id": "prj_abc123"
}| Field | Type | Description |
|---|---|---|
name | string | Required. Display name, 2–50 chars |
app_id | string | Required. URL slug, 3–30 lowercase alphanumeric characters or hyphens. Globally unique and immutable |
project_id | string | Required. Project id or slug |
auth_tier_slug | string? | Capacity bracket; defaults to the smallest |
team_id | string? | Owning team |
connect_site_ids | string[]? | Site uuids of the project to connect the new app to, at level client. Omitted or empty → connected to no site |
allowed_origins | string[]? | Origins outside the project to accept — see Allowed origins. Omitted → none |
app_id is the app’s public identifier: it is the slug in every auth URL
(https://auth.ghayma.tech/v1/my-app/login) and the aud claim of every JWT
the app issues.
Response: 201
{
"auth_app": { "id": "aap_abc123", "app_id": "my-app", "name": "My App Auth" },
"endpoint": "https://auth.ghayma.tech/v1/my-app",
"connections": { "connected": ["<site-uuid>"], "pending": [], "failed": [] }
}Only the sites in connect_site_ids receive the app’s variables. They are
checked before anything is created, and connections reads as it does for a
database.
OAuth providers are not configured at creation. Enable them afterwards with
Update Auth App, in the Dashboard, or via
ghayma auth config.
List Auth Apps
GET /api/v1/auth-appsGet Auth App
GET /api/v1/auth-apps/:idUpdate Auth App
PUT /api/v1/auth-apps/:idBody: any subset of the writable fields below. Unknown keys are ignored, and
app_id / project_id cannot be changed.
| Field | Type | Description |
|---|---|---|
name | string | Display name |
allowed_origins | string[] | Origins outside the project to accept; replaces the list — see Allowed origins |
email_verification_required | boolean | Block login until the email is verified |
jwt_expiry_seconds | number | Access-token lifetime (default 900) |
refresh_expiry_seconds | number | Refresh-token lifetime (default 604800) |
google_oauth_enabled | boolean | Enable Google sign-in |
google_client_id / google_client_secret | string | Google OAuth credentials |
github_oauth_enabled | boolean | Enable GitHub sign-in |
github_client_id / github_client_secret | string | GitHub OAuth credentials |
email_locale | string | End-user email language: en, fr, ar |
reset_url | string | Your own password-reset page, https on an allowed origin (http only on localhost) |
auth_tier_slug | string | Capacity bracket (re-prices the app) |
two_fa_enabled | boolean | Offer TOTP two-factor auth (the authenticator-app method, a priced add-on). A JSON boolean only |
two_fa_policy | string | disabled, optional or enforced — see Two-factor policy |
See the OAuth Providers guide for the provider setup flow.
Allowed origins
One set of origins decides CORS on the auth endpoints, which OAuth
redirect_uri is accepted, and where reset_url may point. It is:
- Every domain of every site in the app’s project, as
https://<hostname>: the.ghayma.appURLs and your custom domains. A domain added later counts within a minute, with no change to the app. A custom domain still waiting for DNS counts once it is live. - Local development:
http://localhostandhttp://127.0.0.1, any port. - The entries in
allowed_origins.
So allowed_origins is only for origins outside the project: a frontend hosted
elsewhere, a preview URL, or a mobile app’s deep link such as
com.example.app://callback. A custom scheme is accepted only from an exact
entry.
Each entry is scheme://host[:port], with no path and no trailing slash, and is
stored lowercase. "*" is no longer accepted. A refused list answers 400:
error | When |
|---|---|
"*" is no longer allowed: your project's sites and localhost are always allowed; list any other origin explicitly | The list contains "*" |
allowed_origins: "https://app.example.com/" is not an origin; use scheme://host[:port] with no path, like https://app.example.com | An entry with a path, a query or a trailing slash, or that is not an origin (the entry is quoted) |
Every response that returns the app carries a read-only site_origins field:
the project’s domains that are always allowed. The console shows them under
Allowed Origins → Always allowed.
"*" no longer allows every origin: it matches nothing, and responses leave it
out. An app that relied on it keeps working from its project’s domains and
localhost; list any other origin that calls it.
Two-factor policy
two_fa_policy decides who is asked for a second factor, at the password login
and at every Google or GitHub sign-in:
disabled(the default) — nobody.optional— users who enrolled.enforced— everyone: a user without a second factor enrols at their next sign-in.
optional and enforced need the method on (two_fa_enabled: true), and the
method cannot be turned off while either is set: set the policy to disabled
first, or in the same request. A refused update answers 400 with one of:
error | When |
|---|---|
two_fa_enabled must be a boolean | two_fa_enabled is anything but true / false — a string "true" included |
two_fa_policy must be one of: disabled, optional, enforced | An unknown policy |
two_fa_policy "enforced" requires the authenticator-app 2FA method to be enabled (two_fa_enabled) | Setting optional or enforced while the method is off |
the authenticator-app 2FA method cannot be turned off while two_fa_policy is "enforced"; set two_fa_policy to disabled first | two_fa_enabled: false while the policy is optional or enforced |
The quoted policy in the last two messages is the one in play ("optional" or
"enforced").
Update the SDK of every app that signs users in with Google or GitHub before
you set optional or enforced: older versions break those sign-ins for the
users the policy applies to. See
Minimum versions for 2FA.
Delete Auth App
DELETE /api/v1/auth-apps/:idGet Statistics
GET /api/v1/auth-apps/:id/statsResponse: 200
{
"total_users": 1542,
"active_users": 890,
"logins_today": 87,
"signups_today": 12
}Rotate Signing Keys
Invalidates all existing user tokens.
POST /api/v1/auth-apps/:id/rotate-keysList Users
GET /api/v1/auth-apps/:id/users?page=1&limit=20&search=john| Parameter | Type | Default | Description |
|---|---|---|---|
page | number | 1 | Page number |
limit | number | 20 | Results per page |
search | string | — | Search by name or email |
Get User
GET /api/v1/auth-apps/:id/users/:userIdDisable / Enable User
Separate routes, no request body.
POST /api/v1/auth-apps/:id/users/:userId/disable
POST /api/v1/auth-apps/:id/users/:userId/enableDisabling ends every session of the user at once, and a disabled user cannot sign in. Re-enabling lets them sign in again; nothing from before the disable comes back.
Set User App Metadata
Developer-owned data (roles, plan, tenant id) that is embedded in the user’s JWT
as the app_metadata claim. Replace semantics — send the full object; {}
clears it. Max 8 KB.
PATCH /api/v1/auth-apps/:id/users/:userId/app-metadataBody:
{
"app_metadata": { "roles": ["admin"], "plan": "pro" }
}Requires the admin or owner role on the project. Changes reach the user’s tokens at their next refresh.
Reset a User’s 2FA
Clears the user’s two-factor enrolment so a locked-out user can sign in again — with a password, Google or GitHub — and re-enrol. It is also the way out for a user asked for a code they never set up after a Google or GitHub sign-in took over their account. Requires the admin or owner role; audited.
POST /api/v1/auth-apps/:id/users/:userId/reset-2faDelete User
DELETE /api/v1/auth-apps/:id/users/:userId