Authorization
Authorization is evaluated for every command after identity resolution and before document access. Public rendering does no session lookup.
MembershipAuthorizer is the host-neutral reference implementation for this sequence. It requires a tenant-scoped identity, confirms tenant membership from the authoritative membership store, resolves current site roles, and only then evaluates the permission. It never treats a browser’s site-role claim as authorization for an arbitrary site. Agency roles can authorize within their tenant after membership is confirmed.
On Cloudflare, D1ControlPlane is the authoritative membership-store implementation. Tenant agency roles and client site roles are stored separately so a client-editor claim on one site cannot grant access to another.
Scope order
- Resolve the agency tenant from the request/site mapping.
- Resolve agency membership.
- Resolve site membership when the action is site-specific.
- Test the explicit permission required by the command.
- Record the actor, scope, command, and result in the audit trail.
Roles
Agency roles are agency-owner, developer, account-manager, and support. Site roles are client-owner, client-editor, and viewer.
The permission vocabulary in @wise-wig/contracts is the source of truth. Support impersonation must create an auditable, time-bound session and never silently inherit publish or domain-management authority.
Required controls
- Short-lived signed sessions; OAuth/OIDC with PKCE for the browser extension and Studio.
- Cookie-authenticated mutations require exact same-origin and Fetch Metadata checks before guarded command execution.
- Every mutation has a tenant ID, site ID where applicable, actor ID, expected document version, and idempotency key.
- Every mutation uses the shared guarded command path, checks its declared permission, records a succeeded/denied/replayed audit event, and requires confirmation for consequential effects.
- Publish, delete, theme switch, plugin install, domain changes, and outbound effects require elevated permission and explicit confirmation.
- A plugin receives a narrower capability set than the actor who invoked it.