Limited Time Offer!
For Less Than the Cost of a Starbucks Coffee, Access All DevOpsSchool Videos on YouTube Unlimitedly.
Master DevOps, SRE, DevSecOps Skills!
Authentication + Org Selection — the happy path

1. GET / (User → wizbrand-web)
No local session? The app kicks off OIDC.
2. Redirect to Auth (wizbrand-web → Keycloak)
Auth Code + PKCE: protects against token interception and CSRF.
3. Callback → tokens (Keycloak → wizbrand-web)
Your backend exchanges the code
for tokens containing all org memberships.
At this point you know all the orgs and roles of the user.
4. If multi-org → prompt selection (wizbrand-web → Org Switcher UI)
If the user belongs to more than one org, show the Org Switcher. If just one, you can auto-select it.
5. Token exchange / refresh to set preferred_org
(Org Switcher → Keycloak)
Once the user picks an org:
- Option A: Token Exchange (or a silent refresh) so Keycloak sets a user session note and issues a new Access Token carrying
preferred_org
. - Option B: Keep the full list in the token but put the active org in
preferred_org
to keep downstream checks simple.
6. Reduced token returns (Keycloak → wizbrand-web)
Now your Access Token contains preferred_org
and (optionally) only the roles relevant to that org. This keeps the token small and unambiguous.
7. API calls (wizbrand-web → wizbrand-api)
Every request includes the Bearer token. Your API middleware does:
- Pick current org (route param / header / subdomain)
- Verify token signature,
aud
,exp
- Check membership in that org + role → action matrix:
ORG_ADMIN
: Create/Read/Update/DeleteORG_MANAGER
: Read/UpdateORG_USER
: Read
8. Logout (User → wizbrand-web) (dashed in diagram)
App calls frontchannel and backchannel logout at Keycloak, clearing both the application session and the SSO session, so sidecar apps are logged out too.
Provisioning Flow — create org & invite members

1. Create group (Admin/Backend → Keycloak Admin API)
Create /orgs/{org_slug}
with attributes like:
org_id
(internal GUID)org_slug
(DNS-safe name)plan
,features[]
(used for entitlements)
2. Create/find user (Admin/Backend → Keycloak Admin API)
Lookup by email. If not found, create the user (optionally mark emailVerified=false until they verify).
3. Add membership + role mapping (Admin/Backend → Keycloak Admin API)
Add the user to the org Group and attach the appropriate realm role mapping at the group level:
ORG_ADMIN
orORG_MANAGER
orORG_USER
Because it’s group-scoped, the same person can be Admin in Org A and User in Org B.
4. Email invite link (Admin/Backend → email)
Send a magic/activation link to your app, not to Keycloak admin. On first login the membership is already in place.
5. Persist audit (Admin/Backend → DB)
Record who invited whom, role assigned, and timestamps. This is critical for traceability and support.
(Role changes and removals use the same API calls: update group role mapping, or remove user from the group.)
How authorization is enforced per request (the “gate”)
- In Laravel API: route middleware extracts current org (subdomain/path/header) and checks the JWT claims for:
preferred_org == current_org
(or membership contains current org)- role meets the action (CRUD matrix)
- At gateways for sidecar apps (oauth2-proxy/Envoy):
- Derive org from
host
({org}.seo.wizbrand.com
) or path (/org/{slug}/…
) - Validate the token → membership contains that org → required role for the requested method/path
- If allowed, pass through with headers (
X-User-Id
,X-Email
,X-Org-Slug
,X-Org-Roles
) - Else,
401
(unauthenticated) or403
(authenticated but not permitted)
- Derive org from
Token contents you should expect
Example Access Token fields your code will read:
{
"aud": ["wizbrand-web", "wizbrand-api"],
"sub": "uuid-of-user",
"email": "user@example.com",
"org_memberships": [
{"org_id":"8f2e3a","org_slug":"acme","roles":["ORG_ADMIN"],"features":["seo","dam"]},
{"org_id":"91b6df","org_slug":"globex","roles":["ORG_MANAGER"]}
],
"preferred_org": "acme",
"tenant_ids": ["8f2e3a","91b6df"],
"exp": 1737440000
}
Leave a Reply