Recipes
Step-by-step changes. Commands run from the root of grandstack-auth with the auth stack up (docker compose -f compose.yaml -f compose.auth.yaml up --build in the grandstack repo) unless stated. Each recipe ends with how to check it.
Where to run this. Commands on this page that start with
npm,nodeordocker buildrun inside a clone of this repository:git clone https://github.com/grand-stack/grandstack-auth, thencd grandstack-auth(Node 22 or later; runnpm cionce). Commands that need the running stack use thegrandstackrepository’s compose files: see the quickstart.
Add a role that can delete any review
Example: a critic role, like a moderator but only for reviews.
- Keycloak. In
keycloak/grandstack-realm.jsonadd a realm rolecriticthat includesuser, and a user for it:Add a user in the{ "name": "critic", "description": "Can delete any review", "composite": true, "composites": { "realm": ["user"] } }usersarray with"realmRoles": ["critic"]and a password credential like the others. Add the role to the demo users’ list in keycloak.md. - Recreate Keycloak so it imports the change (how). On a first start there is nothing to recreate: the realm file is imported automatically.
- Gateway. Nothing to change: the
critictoken also carriesuser(the role is composite), and that already grantsreviews:write. If a role should not includeuser, add it to the map instead: setROLE_PERMISSIONSon the gateway, or editDEFAULT_ROLE_PERMISSIONSingateway/rules.tsand its tests. - Server rule. The
Reviewrule inschema/auth.graphqlis one filter shared by update and delete, so adding the role to itsORwould also let critics edit anyone’s review. To allow only deleting, split it into one rule per operation and add the role to the delete rule only:(If you do want critics to edit too, just add the role to the existing single rule.)@authorization( filter: [ { operations: [UPDATE], where: { OR: [{ node: { author: { sub: { eq: "$jwt.sub" } } } }, { jwt: { roles: { includes: "moderator" } } }] } } { operations: [DELETE], where: { OR: [{ node: { author: { sub: { eq: "$jwt.sub" } } } }, { jwt: { roles: { includes: "moderator" } } }, { jwt: { roles: { includes: "critic" } } }] } } ] ) - Apply: restart the server (it reads
auth.graphqlat startup):docker compose -f compose.yaml -f compose.auth.yaml restart server, then the gateway (it reads the server’s schema at startup). - Test:
- In
scripts/auth-smoke.mjsadd the new user next to the others (the lineconst [alice, bob, carol] = await Promise.all([...].map((u) => login(u)))) and write checks: the critic can write a review, cannot edit alice’s review (assert the text is unchanged), can delete it, and cannot create, update or delete a movie, person or genre. Thennpm run smoke. - Optionally add a critic case to
gateway/test/rules.test.ts(a critic token is allowedcreateReviewand refusedcreateMovies; no gateway code changes, because the role includesuser). - If the client should show a delete button for critics, do not reuse
isModerator(critics are not moderators, and the playground uses it to predict editing). Add a separate helper such ascanDeleteAnyReviewinclient/src/auth/claims.tswith tests, and use it inclient/src/pages/MovieDetail.tsxonly.
- In
- Docs: update the role tables in how-it-works.md and role-and-scope-model.md, and the demo users in keycloak.md.
Protect a new mutation
- Add the mutation to the server schema (or an extension file) and, if it changes data, a row rule in
schema/auth.graphql. - Add its field name to
MUTATION_PERMISSIONSingateway/rules.tswith the permission it needs. Without this the gateway refuses it (default deny). - Add a unit test in
gateway/test/rules.test.tsand checks inscripts/auth-smoke.mjs. - Rebuild the gateway:
docker compose -f compose.yaml -f compose.auth.yaml up -d --build gateway.
Change who can do what at the gateway
Permissions per role are DEFAULT_ROLE_PERMISSIONS in gateway/rules.ts. To change them without editing code, set the gateway’s ROLE_PERMISSIONS environment variable in compose.auth.yaml (JSON, role to array of permissions). The permission needed by each mutation is MUTATION_PERMISSIONS. Keep client/src/auth/claims.ts in step if you want the client’s buttons to match.
Add a demo user
Add an entry to users in keycloak/grandstack-realm.json (copy alice), then recreate Keycloak (how). For a quick, non-permanent user use the admin console at http://localhost:8081 (admin / admin); it is lost when the container is recreated.
Change the token lifetime
In keycloak/grandstack-realm.json, accessTokenLifespan (seconds) sets it for the realm; a client can override with the attribute access.token.lifespan (the grandstack-smoke-expiring client does this with 5). Recreate Keycloak to apply.
Before you commit
(cd gateway && npm test && npm run typecheck)
(cd client && npm test && npm run typecheck)
npm run docs:check
# with the stack running:
npm run smoke && npm run e2e
Source: grandstack-auth/docs/recipes.md (this page is a copy; edit it in the repository)