Skip to main content

Authorization

Watch the walkthrough (jumps to the Authorization section):

BFFless uses a two-level permission system: global roles for system-wide access and project roles for per-project permissions.

Overview

BFFless Auth as identity provider

BFFless Auth is the identity provider for every site you host on a BFFless workspace. One account works across every site you're a member of, the same way "Sign in with Apple" or Auth0 work — sites consume that identity rather than minting their own.

My Sites — the central account hub

Visit https://admin.sites.bffless.app/account (or https://admin.<your-workspace>/account on self-hosted CE) to see and manage your identity. The page shows:

  • The account you're signed in as
  • Change Password, MFA, and connected sign-in methods (Google, etc.)
  • My Sites — every site you currently belong to, with the role you hold there

<AuthDialog> on consumer sites links here via the "Powered by BFFless Auth · Manage account" footer.

Leaving a site

Each card on the My Sites page has a Leave button. Leaving:

  • Revokes your project membership immediately
  • Does not delete or change your BFFless Auth account
  • Is reversible only by the site owner re-inviting you

You cannot leave a site you own — the Leave button is disabled, and the API rejects the request with a 400. Transfer ownership to another member first (see Transferring Ownership), then leave.

One identity, scoped per project

Authentication (your credentials) is workspace-wide; authorization (your access to a specific site) is per-project. A user signed in via BFFless Auth still needs an explicit project membership to access a site's user features. New site visitors join via:

  • An invitation from a site admin (always available)
  • Self-signup, when the site owner has enabled allowPublicSignup for that project

Visiting a private site you have no membership in returns a 403 page that links you back to your account hub — so it's always clear which identity you're signed in as and where to manage it.

Workspace opt-in

The per-project membership enforcement described above is gated by a workspace-level master switch, REQUIRE_PROJECT_MEMBERSHIP, which defaults to off for back-compat. Without it, any workspace user can sign in on any site under the workspace. See Project membership gate below to enable it.

Project membership gate

A workspace-level master switch controls whether having a project role is sufficient to use a BFFless-hosted site, or whether the user must also have an explicit project-membership row for the project the hostname maps to.

This is effectively a third layer on top of the existing global roles and project roles:

┌────────────────────────────────────────────────────────┐
│ L1 · Global role Admin / User / Member │
│ L2 · Project role Owner ... Guest │
│ L3 · Project membership row present? yes / no (new) │
└────────────────────────────────────────────────────────┘

L1 and L2 describe what a user can do once they're recognized. L3 is the precondition that decides whether they're recognized as a user on this site at all. When L3 fails, the user is treated as anonymous on that specific site regardless of their global role.

The master switch: REQUIRE_PROJECT_MEMBERSHIP

PropertyValue
TypeWorkspace-level feature flag
Defaultfalse (back-compat — current behavior preserved)
Toggle UIadmin.<your-workspace>/admin/settings/authProject Membership card
Env overrideFEATURE_REQUIRE_PROJECT_MEMBERSHIP=true

When false, every membership check in this section is skipped and BFFless authenticates exactly as documented above — credentials valid against the workspace user pool is enough.

When true:

  • POST /signin rejects the request if the user has no membership in the project that maps to the request hostname.
  • GET /session returns { user: null } for an authenticated user without project membership — this is what closes the cross-subdomain cookie bleed (SuperTokens cookies live on the workspace parent domain).
  • Authenticated data routes are gated by a global ProjectMembershipGuard. Routes that should remain reachable for non-members (auth endpoints themselves, public-content serving) opt out with a @PublicProjectAccess() decorator.
  • The admin domain (admin.<your-workspace>) bypasses the gate so admin and staff workflows still work as before.

Who should turn it on

Workspace shapeRecommendation
Single owner, all internal projectsLeave off — SSO across your own sites is convenient.
Multi-customer or whitelabel hostingTurn on — each customer's site needs independent auth; cookie bleeds between sister sites are a security and UX issue.
Mixed (some internal, some public-facing)Turn on, then use allowPublicSignup per project to decide which sites accept self-signup.

Per-project signup: projects.allowPublicSignup

When the master switch is on, the signup endpoint is gated per project. The boolean column projects.allowPublicSignup (default false) controls whether visitors can self-register on a given site:

  • false — signup is disabled; only users invited via the Members tab can join. The endpoint returns PUBLIC_SIGNUP_DISABLED.
  • true — anyone can sign up; the user is auto-granted the guest role on first signup. Existing-email collisions receive a friendly "account exists — sign in instead" response.

Toggle it in Project Settings → Members → Allow public signup. The toggle is disabled with an explainer when the workspace master switch is off (the per-project flag has no effect in that state).

Two different allowPublicSignup* flags

The per-project projects.allowPublicSignup (this section) is distinct from the workspace-wide system_config.allowPublicSignups (note the trailing s), which controls whether anyone can register an account at the admin domain at all. Both default to false.

Behavior matrix

With REQUIRE_PROJECT_MEMBERSHIP = true, on a hostname that resolves to a project:

EndpointUser stateallowPublicSignupOutcome
POST /signinHas membershipn/aSign in, mint cookies
POST /signinNo membershipn/aReject with opaque WRONG_CREDENTIALS_ERROR
POST /signupAlready a membern/aTreated as signin (no role downgrade)
POST /signupNot a memberfalseReject with PUBLIC_SIGNUP_DISABLED
POST /signupNew emailtrueCreate user, auto-grant guest, mint cookies
POST /signupEmail exists, password matchestrueAuto-grant guest on this project, mint cookies
POST /signupEmail exists, password wrongtrue"Account exists — sign in instead" hint
GET /sessionHas membershipn/aReturn current user
GET /sessionNo membershipn/aReturn { user: null }; cookie is left intact

The admin domain (admin.<your-workspace>) skips this entire matrix — auth there is workspace-level so admins can manage projects they aren't members of.

Effect on the identity-provider model

When REQUIRE_PROJECT_MEMBERSHIP is on, the identity-provider model becomes load-bearing: a user has one BFFless Auth account workspace-wide, but their presence on any given site is decided entirely by project membership. The My Sites hub at admin.<your-workspace>/account becomes the canonical listing of sites the user can actually use, and the "Powered by BFFless Auth" footer on <AuthDialog> makes the cross-site identity explicit.

Global Roles

Global roles determine system-wide capabilities. Every user has exactly one global role.

RoleDescriptionCapabilities
AdminSystem administratorFull access to all projects, user management, system settings
UserRegular userCreate projects, manage own projects, access granted projects
MemberBasic member (default)Access granted projects only, cannot create projects

New users are assigned the member role by default. Admins can promote users to higher roles.

Admin Capabilities

Admins have unrestricted access to:

  • View and manage all projects
  • Create, edit, and delete users
  • Access system settings and configuration
  • View platform analytics and logs
  • Manage API keys for any user

User Capabilities

Users can:

  • Create new projects (become owner automatically)
  • Manage projects where they have appropriate permissions
  • Create personal API keys
  • Invite others to their projects

Member Capabilities

Members can:

  • Access projects where they've been granted permissions
  • View and interact based on their project role
  • Cannot create new projects

Project Roles

Project roles control access to individual projects. Users can have different roles on different projects.

RoleLevelCapabilities
Owner4Full control, transfer ownership, delete project
Admin3Manage permissions, all read/write operations
Contributor2Create deployments, upload assets, modify content
Viewer1Read-only access to admin backend, view deployments and files
Guest0Site access only, no admin backend access

Role Hierarchy

Roles are hierarchical—higher roles include all permissions of lower roles:

Owner (4) → Admin (3) → Contributor (2) → Viewer (1) → Guest (0)

For example, a user with Admin role automatically has all Contributor and Viewer permissions.

Owner

Each project has exactly one owner. The owner:

  • Has complete control over the project
  • Can transfer ownership to another user
  • Can delete the project
  • Cannot have their access revoked (must transfer ownership first)
  • Is automatically assigned when creating a project

Admin

Project admins can:

  • Grant and revoke permissions (except owner)
  • Manage project settings
  • Create and delete deployments
  • Configure domains and traffic rules
  • View all project analytics

Contributor

Contributors can:

  • Create new deployments
  • Upload assets via API or GitHub Action
  • Modify traffic splitting rules
  • Cannot manage other users' permissions

Viewer

Viewers have read-only access to the admin backend:

  • View deployments and their status
  • Browse uploaded files
  • View traffic configuration
  • Cannot modify anything

Guest

Guests can access private site content but have no admin backend access:

  • Can log in and view private deployments via the public URL
  • Cannot see the admin dashboard, repository list, or settings
  • Ideal for end users of private sites (e.g., event guests, clients)
  • Use the project's "Required Role" setting with "Guest or higher" to restrict access to invited guests only

Permission Sources

Users can receive project permissions through multiple sources:

Direct Permissions

Permissions granted directly to a user on a project:

User → Project (role: contributor)

Group Permissions

Permissions inherited through group membership:

User → Group → Project (role: viewer)

Groups cannot be assigned the owner role.

Effective Permission

When a user has permissions from multiple sources, they receive the highest role:

Direct RoleGroup RoleEffective Role
viewercontributorcontributor
adminvieweradmin
(none)contributorcontributor
contributor(none)contributor

API Key Access

API keys provide programmatic access for CI/CD pipelines and automation. Only admin global role users can create and manage API keys.

Project-Scoped Keys

API keys can be scoped to a specific project:

  • Only works for that project
  • Inherits contributor permissions
  • Ideal for CI/CD deployments

Global Keys

Admin users can create global API keys:

  • Work across all projects
  • Used for automation and integrations
  • Should be carefully protected

Public vs Private Access

Projects and deployments can be public or private:

Public Projects

  • Anyone can view deployments
  • No authentication required for viewing
  • Modifications still require appropriate permissions

Private Projects

  • Only users with granted permissions can access
  • Unauthorized access returns 404 (hides existence)
  • Or redirects to login (configurable)

Visibility Hierarchy

Visibility can be set at multiple levels:

  1. Project level - Default for all deployments
  2. Alias level - Override for specific aliases (e.g., production)
  3. Domain level - Override for specific custom domains

The most specific setting takes precedence.

Managing Permissions

Granting Permissions

Admins and owners can grant permissions:

  1. Go to Project → Settings → Permissions
  2. Click "Add User" or "Add Group"
  3. Select the user/group and role
  4. Click Save

Revoking Permissions

  1. Go to Project → Settings → Permissions
  2. Find the user or group
  3. Click Remove
  4. Confirm revocation

Transferring Ownership

Only owners can transfer ownership:

  1. Go to Project → Settings → General
  2. Click "Transfer Ownership"
  3. Select the new owner
  4. Confirm transfer

You will become an admin after transferring ownership.

Permission Matrix

ActionOwnerAdminContributorViewerGuest
Access private site contentYesYesYesYesYes
View admin backendYesYesYesYes-
View deploymentsYesYesYesYes-
Browse filesYesYesYesYes-
Create deploymentYesYesYes--
Delete deploymentYesYes---
Configure trafficYesYesYes--
Manage domainsYesYes---
Manage settingsYesYes---
Grant permissionsYesYes---
Delete projectYes----
Transfer ownershipYes----

User Groups

Groups simplify permission management for teams:

Creating Groups

  1. Go to Settings → Groups
  2. Click "Create Group"
  3. Name the group (e.g., "Frontend Team")
  4. Add members

Assigning Group Permissions

  1. Go to Project → Settings → Permissions
  2. Click "Add Group"
  3. Select the group and role
  4. All group members inherit this permission

Group Limitations

  • Groups cannot be assigned the owner role
  • Users can belong to multiple groups
  • Removing a user from a group immediately revokes inherited permissions

Best Practices

Principle of Least Privilege

Grant the minimum permissions needed:

  • Use guest for end users who only need to access private site content
  • Use viewer for stakeholders who need read-only admin access
  • Use contributor for developers who deploy but don't manage access
  • Reserve admin for team leads who manage permissions

Use Groups for Teams

Instead of granting individual permissions:

  1. Create groups matching your team structure
  2. Assign project permissions to groups
  3. Add/remove users from groups as needed

This simplifies onboarding and offboarding.

Protect Production

For production deployments:

  • Limit who has contributor or higher access
  • Use project-scoped API keys for CI/CD
  • Review permissions regularly

API Key Security

  • Use project-scoped keys when possible
  • Rotate keys periodically
  • Never commit keys to source control
  • Use GitHub Secrets for CI/CD