SSO & access control
Who may open your app, how the platform tells your code who they are, and why you should not implement login yourself.
Sign-in is Google or Microsoft, handled by the platform before your code runs. Your app never sees a password, never stores a session, and never implements a login screen. What it gets is a set of headers describing who is knocking.
These prefixes are routed by the platform before your app is consulted, on your app’s own domain:
/auth, /api/auth, /api/deploy, /api/wf, /api/feedback,
/api/ai-usage, /git, /health, /__auth/, /_platform/
They are matched as prefixes, so this is wider than it looks: an app with
a route at /authors or /authenticate silently loses it — the request
never reaches your code, and nothing warns you at deploy time. Check your
route table against this list before you turn the login gate on.
Deciding who gets in
Decide who can open it covers the console. Two other ways to set the same thing:
$dibbla deploy . --alias my-app -m "feat: launch" --require-login --access-policy invite_only # on an app that already exists (note: takes a value here) $dibbla apps update my-app --require-login true --access-policy all_members
# or per service, in dibbla.yaml
services:
admin:
build: ./admin
port: 3000
public: true
auth:
require_login: true
access_policy: invite_only
| Policy | Who gets in |
|---|---|
all_members | anyone in your organisation |
invite_only | only users you grant access to |
email_domain | anyone whose e-mail matches a listed domain — no membership required |
The CLI’s --access-policy accepts all_members and invite_only, and the
domain policy needs its domain list alongside it — which no CLI flag
supplies, and which the manifest’s auth: block has no field for either.
Set it in the console’s Access & users dialog. It is the one access
policy you cannot express in a deploy.
What your app receives
Once someone is through the gate, the proxy passes their identity as headers.
The proxy strips any inbound X-User-* headers from the client first, so
what arrives cannot be spoofed:
| Header | Meaning |
|---|---|
X-User-ID | stable identifier — use this as the key in your database |
X-User-Email | e-mail address |
X-User-Name | display name |
X-User-Org | the organisation they are acting as, as a UUID |
X-User-Org-Slug | the same organisation, human-readable — use this if you are rendering it |
X-User-Org-Role | their role in it: owner, admin, developer or viewer |
X-User-GlobalAdmin | the literal string true, and sent only when they are one |
X-Session-Id | the platform session this request belongs to |
X-User-Deployment | set for a workload identity rather than a person |
In particular, a user with no organisation gets neither X-User-Org nor
X-User-Org-Slug nor X-User-Org-Role. That is not an edge case: it is the
normal state for anyone let in by an e-mail-domain policy, which is the
policy this page recommends. Read them as optional, and decide what an
org-less user is allowed to do.
app.use((req, res, next) => {
req.user = req.header("x-user-id")
? {
id: req.header("x-user-id"),
email: req.header("x-user-email"),
name: req.header("x-user-name"),
org: req.header("x-user-org"),
role: req.header("x-user-org-role"),
}
: null;
next();
});
E-mail addresses change — people marry, companies rebrand, aliases get
reassigned. X-User-ID is stable. Using the address as your primary key is
the mistake that is expensive to undo a year later.
Read the headers, not the cookie. There is an auth_token JWT cookie on
your app’s domain — the proxy sets it and it reaches your backend — but it is
the platform’s session, not a contract with your app. It can change shape
without notice, and the headers are what the proxy guarantees. If you find
yourself parsing that cookie, you have left the supported path.
Gate versus authorization
The platform answers may this person open the app. It does not answer may
this person delete that record — that is your code’s job, using the role and
identity in the headers. A common shape: let the platform handle who reaches
the app, then check X-User-Org-Role for the handful of destructive actions.
Domain auto-join
An organisation can claim e-mail domains, so a colleague signing in with a matching address lands in it automatically rather than in an empty personal space. It runs only on a genuinely new sign-in — an existing account is never moved by it.