-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Replace env-based admin/editor allowlists with app-owned authorization in Convex
Summary
We currently authenticate with WorkOS AuthKit and authorize a small set of privileged behaviors via environment/source allowlists. That works as a stopgap, but it is operationally awkward and not a clean long-term answer for admin vs non-admin behavior.
This issue is to move toward app-owned authorization in Convex data while keeping WorkOS as the authentication layer.
Why
- Local / preview / production use different WorkOS clients, so
subjectvalues differ by environment. - Email claims are not reliable enough to be the only authorization primitive in the current token path.
- We do not have a first-class admin management path today.
- We already have at least two distinct privilege levels:
- full admin
- users who can manage public icon libraries
Current State
- WorkOS AuthKit is the authentication layer for the web app and Convex backend.
- Convex derives privileged access from:
SKETCHI_ADMIN_EMAILSSKETCHI_ADMIN_SUBJECTSSKETCHI_ICON_LIBRARY_EDITOR_EMAILSSKETCHI_ICON_LIBRARY_EDITOR_SUBJECTS
- There is also a hardcoded default public icon library editor email (
anand@shpit.dev) in source. users.rolecurrently mirrors the env-based admin check during user upsert; it is not the durable source of truth for authorization.- Public icon library edit capability is checked in backend permission helpers and icon library queries/mutations.
- Preview E2E currently covers auth gates and general authenticated continuity, but not admin/public-icon-editor capability.
Relevant current touchpoints:
packages/backend/convex/lib/users.tspackages/backend/convex/users.tspackages/backend/convex/iconLibraries.ts.github/workflows/e2e-web.yml
Desired Direction
- Keep WorkOS responsible for authentication only: “who is this user?”
- Make Convex data the source of truth for authorization: “what can this user do?”
- Represent privileged access as explicit app roles/capabilities instead of deployment-specific allowlists.
- Support an initial bootstrap path for the first admin in a deployment without making env vars the permanent management model.
- Start with backend management primitives first; add a UI only if/when it becomes worth it.
High-Level Approach
We should introduce an app-owned authorization model in Convex that is keyed off the authenticated WorkOS identity (subject / external ID), not off deployment env vars.
This does not require changing authentication providers. WorkOS can remain the identity provider, while Convex stores and enforces app roles/capabilities.
At a high level, that likely means:
- keep
usersas the app-level identity mirror in Convex - make roles/capabilities in Convex the durable authz source of truth
- add a bootstrap path for the first admin in a deployment
- add guarded backend mutations / internal APIs for later role management
- optionally add a small admin UI later if role changes become common
Authz Flow
flowchart LR
A[WorkOS AuthKit] -->|access token| B[Convex auth]
B -->|subject / externalId| C[Convex users + roles/capabilities]
C --> D[Authorization checks in queries and mutations]
E[Admin management API or script] --> C
In Scope
- Decide and implement a clean app-owned authorization source of truth in Convex
- Define the bootstrap story for the first admin in a deployment
- Define how non-admin elevated permissions should be represented
- Migrate current admin/public-icon-editor checks away from env/source allowlists
- Add the minimum management surface needed to maintain roles/capabilities after bootstrap
Out of Scope
- Adopting a full organization model unless we explicitly decide to do that as part of this issue
- Fine-grained per-resource ACL/FGA
- Building a polished admin portal up front if backend management primitives are sufficient
Open Questions
- Should we model this as coarse roles (
user,admin) plus separate capabilities, or capabilities only? - Should “manage public icon libraries” remain a distinct capability from full admin?
- What should the bootstrap path look like in practice:
- internal mutation + CLI script
- one-time guarded admin setup flow
- something else
- Do we want a minimal admin page in the first pass, or just backend management primitives?
Notes
- This issue is intentionally about the direction and system shape, not a strict implementation script.
- WorkOS-native RBAC / roles and permissions remain a viable alternative, but the current app is not organization-aware today.
- Convex-owned authorization appears to be the best fit for the current product shape.
Acceptance Criteria
- There is one clear app-owned source of truth for privileged access in Convex
- The bootstrap story for the first admin in a deployment is defined
- Full admin and public-icon-editor access are represented intentionally, not as ad hoc allowlists
- The resulting design is preview/local/prod friendly and does not depend on hardcoded per-env subjects in source
- The repo has a defined path for managing authorization after bootstrap, even if the first version is backend/API only
Validation Ideas
convex: authz helper and role/capability resolutionconvex: bootstrap path behaviorconvex: public icon library permission checksstagehand: signed-in admin sees admin-only affordancesstagehand: signed-in non-admin cannot access admin-only flowsstagehand: signed-in public-icon-editor can edit/create public icon packs without full admin access