Auth
Three questions, answered separately.
| Question | Answered by |
|---|---|
| Who are you? | An identity provider — Entra or Google — or an API key |
| What is your session? | A JWT, minted by the identity API |
| What may you do? | The permissions carried on that JWT, checked per endpoint |
The login flow
A person signing in:
Browser Auth UI Account API Identity API
│ │ │ │
│─── GET / ───────────▶│ │ │
│ │─ GET /organizations/{id}/signin ──▶ │
│◀── branded page ─────│◀─ name, logo, CSS, providers ──── │ │
│ │ │ │
│─── choose provider ─▶│ │ │
│◀────────── redirect to Entra / Google ────────────────── │
│─── returns with provider identity ─────────▶│ │
│ │─ POST /users/provision ──────────▶│ │
│ │◀─ account + permissions ──────────│ │
│ │─ POST /generatejwt ─────────────────────────▶│
│ │◀─ signed JWT ───────────────────────────────│
│◀── x-tagshape-auth cookie ─┤ │ │
Four of those calls are anonymous by necessity — they exist to establish an identity that does not yet exist. See API permissions.
Two things happen at POST /users/provision that are easy to miss:
- An account may be created. If nobody holds this provider identity and nobody holds this email address, the organization's account patterns decide whether an account is made. No match refuses the login.
- An existing account is brought into step with the provider. A rename, a new address, a new photo — all applied here, before the token is minted, so the token reflects what is true now rather than at the last login.
An account is identified by what the provider calls it (provider + providerUserId), not by email address.
An address changes; the provider's identifier does not. Email is used for exactly one thing: recognising that
someone signing in with a second provider is the same person, and attaching the new identity to the account they
already have rather than making them a second one.
The machine flow
Script Identity API Account API
│ │ │
│─ POST /apikeyjwt ───────▶│ │
│ x-tagshape-api: ts_... │─ POST /apikeys/lookup ──▶│
│ │◀─ account + permissions ─│
│◀─ x-tagshape-auth: JWT ──│ │
The resulting token carries the same claims a person's would. Nothing downstream can tell an API key was involved.
Carrying the token
| Where | Used by |
|---|---|
x-tagshape-auth header | Service-to-service calls, and any machine caller |
x-tagshape-auth cookie | The browser, calling its own site — this is how the management UI's token refresh arrives |
Anything reading a request's identity looks in both. Looking in only one takes a signed-in caller for an anonymous one.
An API key is read from the header only. A key belongs to a machine with no session and no cookie jar, and honouring it from a cookie would let any site a signed-in user happens to visit spend their key for them.
See Headers for the complete list.
Read next
- Identity providers — configuring Entra and Google
- JWT — claims, lifetime, signing and renewal
- API keys — issuing, using and revoking
- Permissions model — groups, dependencies, granting versus holding
- API permissions — the endpoint-by-endpoint reference