Skip to content

feat(client): add permission and organization role checks for the client user - #241

Merged
Bccorb merged 2 commits into
mainfrom
feat/client-permissions
Nov 6, 2026
Merged

Bccorb merged 2 commits into
mainfrom
feat/client-permissions

Conversation

@Bccorb

@Bccorb Bccorb commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Closes #233

Permissions and organization roles on the client user, for showing and hiding UI. The client reads them from GET users/me and still never decodes a token. The server must still enforce, and the docs say so.

Public API

From @seamless-auth/client, re-exported by React, React Native, Angular, Vue and Svelte:

  • hasPermission(user, required, { organization? }): any of required
  • hasAllPermissions(user, required, { organization? }): all of required, and an empty list answers false
  • hasRole(user, required): instance roles only, scoped matching through hasScopedRole
  • hasOrganizationRole(user, required): active organization roles only, exact matching
  • types PermissionCheckOptions and UserAuthorization

From @seamless-auth/react only: usePermissions() and UsePermissionsResult. It returns the same four checks without the user argument, plus loading, roles, permissions, organizationRoles, organizationPermissions and activeOrganizationId.

Decisions

  • Separate hasOrganizationRole, not an option on hasRole. The two match differently (scoped and exact), and a forgotten option would be the failure ADR 0004 P2 rules out.
  • { organization: true } counts only the active organization, the same option as requirePermission in feat(guards): add organization aware requirePermission guards seamless-auth-server#275. The default is the union of permissions and organizationPermissions (ADR P6).
  • The standalone and hook hasRole is scoped, while useAuth().hasRole stays exact and answers undefined when signed out. Same name, different semantics for admin:read. I left the existing method alone and documented the difference.
  • First runtime import from @seamless-auth/types. The client imports @seamless-auth/types/permission/matching, which has no Zod. It is external in the client build, and AGENTS.md now records the exception. Each Jest config maps and transforms that one file, because the package ships ESM under the import condition only. An adopter whose Jest runs our client through its own transform allowlist needs @seamless-auth/types in that allowlist too.

What was run

All on Node 24, in this branch:

  • npm run lint: pass
  • npm run typecheck: pass, all six packages
  • npm run format:check: pass
  • npm run check:types-current: pass, 0.38.0
  • npm run build: pass, including check-react-dist and check-vue-dist
  • npm test -- --coverage --runInBand: pass. Jest 59 suites, 896 tests. Vitest (Svelte) 3 files, 56 tests. permissions.ts and usePermissions.ts at 100 percent
  • npm run check-npm-build: pass
  • Zod check: the repo has no automated one. I bundled packages/client/dist/index.js and packages/react/dist/index.js with esbuild for the browser and read the metafile: no Zod input, and the only @seamless-auth/types input is permission/matching.js

What was not run

Left out

  • usePermissions equivalents in React Native, Vue, Svelte and Angular. The issue names React, and those bindings get the helpers through their re-export lists. They also have no hasPermission method on their service or session class
  • ../seamless-auth-docs is not updated
  • An automated Zod-free bundle check

Noticed, not changed

  • packages/client/src/scopedRoles.ts is a hand-kept copy of @seamless-auth/types/role/matching. Now that a Zod-free runtime subpath is in use, it could import rather than copy
  • meHandler in seamless-auth-server/packages/core/src/handlers/me.ts returns only user and credentials, so organizations, activeOrganization and recovery do not reach the client through it. user passes through whole, so the new fields do

…ent user

Add hasPermission, hasAllPermissions, hasRole and hasOrganizationRole over the /users/me user,
re-exported from every binding, and a usePermissions hook in @seamless-auth/react.
For UI only: the server must still enforce.

Closes #233
@Bccorb Bccorb added this to the v0.23 milestone Nov 6, 2026
hasPermission and hasAllPermissions now take { organization: true }, the option the server
guards use in fells-code/seamless-auth-server#275.
@Bccorb
Bccorb merged commit de0a386 into main Nov 6, 2026
5 checks passed
@Bccorb
Bccorb deleted the feat/client-permissions branch November 6, 2026 06:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: permissions and organization roles on the client user

1 participant