Skip to main content

Bindle Authorization Layer

The Bindle Authorization Layer is the core security mechanism for the People Portal. It enables granular, resource-specific permission management (Bindles) on top of the standard OIDC authentication. It allows teams to manage access to shared resources (like GitHub repositories, Slack channels, etc.) by assigning specific "bindles" to subteams.

Security Middleware Operation

The Bindle Security Middleware intercepts requests to protected routes. It verifies the user's identity via OIDC, resolves the target team context, and enforces fine-grained permissions.

Authorization Flow Sequence

The following diagram illustrates the detailed sequence of operations when a request hits a Bindle-protected endpoint.

Everything Bindles

"Bindles" are the atomic units of permission within the People Portal ecosystem. They represent a specific capability within a shared resource (e.g., repo:allowcreate for Gitea, slack:allowpost for Slack).

Bindle Definitions

Bindles are defined by SharedResourceClient implementations. Each client (Gitea, Slack, Apple Account, etc.) defines the bindles it supports.

Structure of a Bindle:

script.ts
export interface BindlePermission {    friendlyName: string, // Human readable name for UI    description: string,  // Detailed description of what this permission allows}

Example: Gitea Client Definitions In src/clients/GiteaClient/index.ts, the supported bindles are strictly typed and exported:

script.ts
private readonly supportedBindles: BindlePermissionMap = {    "repo:allowcreate": {        friendlyName: "Allow Repository Creation",        description: "Enabling this allows members in this subteam to create repositories",    },    // ... other bindles}

Registering Shared Resources

For a Bindle to be recognized by the system, its Client must be registered in the global configuration at src/config.ts. This registry allows the BindleController to aggregate all available permissions dynamically.

script.ts
// src/config.tsexport const ENABLED_SHARED_RESOURCES: { [key: string]: SharedResourceClient } = {  giteaClient: new GiteaClient(),  peoplePortalClient: new PeoplePortalClient(),  // ... other clients}

Team Attribute Storage

Bindle assignments are stored directly on the Team (or Subteam) objects within Authentik as attributes. This allows permissions to persist alongside the group structure.

Data Layout in Authentik Attributes:

script.json
{  "bindlePermissions": {    "GiteaClient": {      "repo:allowcreate": true,      "repo:allowsome": false    },    "SlackClient": {      "channel:create": true    }  }}

SharedResourceClient Interface

To implement a new integration that supports Bindles, you must implement the SharedResourceClient interface.

script.ts
export interface SharedResourceClient {    // Unique identifier for the client (used as the key in attributes)    getResourceName(): string        // Returns the map of permissions this client supports    getSupportedBindles(): BindlePermissionMap    // Callback to synchronize state when permissions/memberships change    handleOrgBindleSync(        org: GetGroupInfoResponse,        callback: (updatedResourceCount: number, status: string) => void    ): Promise<boolean>}

Effective Permission Calculation

The BindleController.getEffectivePermissionSet method is responsible for flattening the complex hierarchy of subteams and assignments into a simple set of active permissions for a user.

It performs the following logic:

  1. Iterates through all subteams of the target team.
  2. Checks if the user is a member of that subteam (O(1) lookup).
  3. Aggregates all enabled bindles from the subteams the user is a part of.
  4. Returns a Set of strings (e.g., {"repo:allowcreate", "slack:join"}).

This "Effective Permission Set" is what the middleware checks against the required scopes.

Extending the System

To add a new Bindle:

  1. Choose or Create Client: Identify the SharedResourceClient that manages the resource (or create a new one in src/clients/).
  2. Define Bindle: Add the new permission key and description to the supportedBindles map in the client.
  3. Implement Logic: Update the handleOrgBindleSync method in your client to actually enforce or sync this permission to the external service (e.g., calling the Gitea API to add a user to a team with write access).
  4. Register (if new client): Add valid instance to ENABLED_SHARED_RESOURCES in src/config.ts.

Copyright © 2026 Atheesh Thirumalairajan