---
title: Business Roles
description: Understand and configure Business Roles for Forge user access.
moved_from:
  - guides/security/configuration/business-roles.md
---

# Business Roles

Learn how Business Roles work and best practices for managing user access in Forge applications.

---

## 📋 Overview

Business Roles are the foundation of user access management in Forge applications. They represent organizational identities that bridge the gap between your company's structure and application permissions.

---

## What is a Business Role?

A **Business Role** is a security group in your identity provider that aggregates users sharing an organizational position. Business Roles provide a logical name that applications can reference without knowing how membership is derived.

**Key Concept**: Corporate Business Roles are populated from **user profile attributes** synced from on-premises AD (division and job title). External Business Roles are populated from **Okta group membership**. Either way, users who match automatically receive the Business Role.

**Examples**:

| Business Role Name | Populated From                                                 |
| ------------------ | -------------------------------------------------------------- |
| `Claims Adjuster`  | Claims Division → job titles Claims Adjuster I, II, etc.        |
| `Finance Analyst`  | Finance Division → job titles Financial Analyst, Senior Analyst |
| `Premium Auditor`  | Audit Division → job titles Premium Auditor, Senior Auditor     |
| `HR Manager`       | HR Division → job titles HR Manager, Senior HR Manager          |

!!! info "Uniqueness Required"
    Business Roles must be **unique across the entire organization**. Each Business Role name can only exist once in the identity system.

!!! warning "Create Before Use"
    Business Roles must be **created** in the business roles repository before they can be **referenced** in your application's auth configuration. You cannot use a Business Role that doesn't exist.

---

## How Business Roles Work

```
User's Organization Position → Assigned Business Role → Granted App Roles → Application Access
(Claims Department, Adjuster)  (Claims Adjuster)        (App.Read, App.Write) (Can use features)
```

**The Flow**:

1. **Assignment**: User is assigned Business Roles based on organizational position
2. **Mapping**: Applications define which Business Roles can access them
3. **Permissions**: Each Business Role is granted specific App Roles (permissions)
4. **Access**: Users automatically receive App Roles from their Business Roles

---

## Corporate vs. External Business Roles

Forge distinguishes between two types of Business Roles:

=== "Corporate (Entra ID)"

    | Aspect                | Details                                                 |
    | --------------------- | ------------------------------------------------------- |
    | **Who**               | Employees and internal staff                            |
    | **Identity Provider** | Microsoft Entra ID                                      |
    | **Storage**           | Entra ID security groups                                |
    | **Created In**        | `{Project}-okta-business-roles-corp` Azure DevOps repo  |
    | **Deployed**          | Organization-wide (any app can use)                     |
    | **Used In**           | `infra/auth/corp/config.yml` (in your app repo)         |
    | **Examples**          | `Claims Adjuster`, `Finance Analyst`, `Premium Auditor` |

=== "External (Okta)"

    | Aspect                | Details                                                             |
    | --------------------- | ------------------------------------------------------------------- |
    | **Who**               | Policyholders, injured workers, employers, providers                |
    | **Identity Provider** | Okta                                                                |
    | **Storage**           | Okta groups                                                         |
    | **Created In**        | `{Project}-okta-business-roles-external` Azure DevOps repo          |
    | **Deployed**          | Organization-wide (any app can use)                                 |
    | **Used In**           | `infra/auth/ext/user/business-role-app-role.yml` (in your app repo) |
    | **Examples**          | `Policy Holder`, `Injured Worker`, `Employer Representative`        |

---

## 🏷️ Terminology Reference

| Term                   | Also Known As            | Description                                       |
| ---------------------- | ------------------------ | ------------------------------------------------- |
| **Business Role**      | External Role, User Role | Organizational position (Division + Title)        |
| **App Role**           | App Permission, Role     | Permission within your application                |
| **Scope**              | API Scope                | Permission for app-to-app calls on behalf of user |

The platform also manages one user-delegation scope per provider; [Authorization](../../../reference/authorization.md#platform-managed-delegation-scopes) owns those names.

### Key Distinctions

- **Business Role vs. App Role**: Business Role = organizational position; App Role = what they can do in your app
- **App Role vs. Scope**: App Role = user permissions; Scope = app-to-app permissions on behalf of user

---

## 💡 Best Practices

### ✅ Do's

| Practice                     | Reason                                          |
| ---------------------------- | ----------------------------------------------- |
| Use descriptive names        | `Finance Analyst` is clearer than `FA01`        |
| Follow naming convention     | Use `Division Title` format consistently        |
| Ensure uniqueness            | No duplicate Business Roles across organization |
| Document ownership           | Know which team owns each Business Role         |
| Regular reviews              | Audit memberships periodically                  |
| Principle of least privilege | Grant minimum App Roles needed                  |

### ❌ Don'ts

| Anti-Pattern                        | Why to Avoid                                 |
| ----------------------------------- | -------------------------------------------- |
| Application-specific Business Roles | Business Roles should be organization-wide   |
| Generic names (`User`, `Admin`)     | Too vague, leads to over-permissioning       |
| Mixing concerns                     | Don't create roles based on app access needs |
| Excessive proliferation             | Reuse existing roles when possible           |
| Over-granting permissions           | Manager ≠ admin access to everything         |

---

## 🔍 Example Scenarios

### Scenario 1: New Employee Joins

1. User is assigned `Claims Adjuster` Business Role
2. Your app has `Claims Adjuster` mapped to `[App.Read, App.Write]`
3. User automatically gets those permissions
4. No manual access grant needed

### Scenario 2: External User Access

1. Injured worker is assigned `Injured Worker` Business Role (external)
2. Your app maps `Injured Worker` to `[App.Read]`
3. Injured worker gets read-only access to their claim
4. Clearly separated from corporate users

### Scenario 3: Department Change

1. User moves from Claims to Finance
2. Business Role changes: `Claims Adjuster` → `Finance Analyst`
3. User automatically loses Claims permissions, gains Finance permissions
4. No app configuration changes needed

---

## ⚙️ Creating Business Roles

Business Roles are **created** in dedicated Azure DevOps repositories, separate from your application. Once created, they are **deployed organization-wide** and can be **used** by any application across teams.

### Find Your Project's Repo First

!!! warning "Don't create a second repo for a workspace tag that already has one"
    Business roles repos map one-to-one to a Terraform Cloud workspace, tagged
    `<teamname-lowercase>-busrole-corp` or `<teamname-lowercase>-busrole-external`. The tag comes
    from both the team name and the tenant, so a team is expected to have two repos: one corp and
    one external. Creating the second tenant's repo is correct. Creating another repo that carries
    a workspace tag already in use (same team **and** same tenant) does **not** create a new
    workspace: both repos end up managing the same state, and deployments reference the wrong role
    definition.

Before creating anything, search Azure DevOps for the repo that already owns your team's workspace for the tenant you need (corp or external). Search on the workspace tag rather than the repo name: names vary, some teams still have a single unsplit `{Project}-business-roles` repo, and the repo may live in a different project:

- [Corp business roles repos](https://dev.azure.com/SAIFCorporation/_search?action=contents&text=-busrole-corp%20ext%3Atf&type=code&lp=custom-Collection&filters=&pageSize=25), query: `-busrole-corp ext:tf`
- [External business roles repos](https://dev.azure.com/SAIFCorporation/_search?action=contents&text=-busrole-external%20ext%3Atf&type=code&lp=custom-Collection&filters=&pageSize=25), query: `-busrole-external ext:tf`

If a repo turns up for your team and tenant, add your Business Role there. If it still uses the deprecated `okta-business-roles` module or the old PascalCase YAML schema, follow the [migration guide](../../../learn/migration/okta-business-roles-migration.md) first. If the repo you find is a single unsplit `{Project}-business-roles` repo, not yet split into corp and external, the migration guide does not cover that case: coordinate with the Platform team before doing anything further, rather than following the migration guide or creating a new repo. Only create a new repo when the search returns nothing for that team and tenant combination.

### Repository Structure

Business roles repositories typically follow a per-project naming convention:

| Type              | Repository Pattern                       | Example                             |
| ----------------- | ---------------------------------------- | ----------------------------------- |
| Corporate (Entra) | `{Project}-okta-business-roles-corp`     | `SAIF-okta-business-roles-corp`     |
| External (Okta)   | `{Project}-okta-business-roles-external` | `SAIF-okta-business-roles-external` |

!!! info "Team-Specific Repos, Org-Wide Deployment"
    Most teams manage their Business Roles in project-specific repos, but the naming pattern above is a convention, not a guarantee. The repo that owns your team and tenant workspace tag is authoritative, even when it sits in another project or uses a different name. Always trust the [workspace tag search](#find-your-projects-repo-first) over the naming convention.

    However the repo is named, the roles are deployed **organization-wide**. Any application across the organization can reference and use Business Roles created by any team.

    This means:

    - You can use Business Roles created by other teams
    - Check if a suitable role already exists before creating a new one
    - Coordinate with other teams to avoid duplicate roles

```mermaid
flowchart LR
    subgraph repo1["Business Roles Repository"]
        direction TB
        file1["infra/okta/okta-business-roles.yml"]
        config1["BusinessRoles:<br/>- Name: Premium Auditor<br/>  RoleSets: ..."]
        action1[/"CREATES the Business Role"/]
    end

    subgraph repo2["Your Application Repository"]
        direction TB
        file2["infra/auth/corp/config.yml"]
        config2["business_roles:<br/>- name: Premium Auditor<br/>  app_roles:<br/>    - App.Read"]
        action2[/"USES the Business Role"/]
    end

    repo2 -->|"references"| repo1
```

!!! danger "Common Error: Business Role Not Found"
    If you reference a Business Role in your app's auth config that doesn't exist in the business roles repository, you'll see:
    ```
    Error: No group found matching specified filter
    (displayName eq 'bus-role-np.developer' and securityEnabled eq true)
    ```
    **Solution**: Create the Business Role in the appropriate business roles repo first, then reference it in your app.

=== "Corporate (Entra ID)"

    Corporate Business Roles select users by the profile attributes Entra Connect syncs from on-premises AD.

    **File:** `infra/entra/business-roles.yml` (in your corp business roles repo)

    ```yaml
    business_roles:
      - name: "Claims Adjuster"
        description: "Claims staff who process and adjust claims"
        role_sets:
          - division: Claims Division
            job_titles:                   # jobTitle values from Entra ID user profiles
              - Claims Adjuster I         # matches users where jobTitle = "Claims Adjuster I"
              - Claims Adjuster II        # matches users where jobTitle = "Claims Adjuster II"
              - Senior Claims Adjuster    # matches users where jobTitle = "Senior Claims Adjuster"
        manual_users: []

      - name: "Premium Auditor"
        description: "Staff who perform premium audits"
        role_sets:
          - division: Audit Division
            job_titles:
              - Premium Auditor
              - Senior Premium Auditor
        manual_users: []

      - name: "HR Manager"
        description: "Human Resources management staff"
        role_sets:
          - division: Human Resources Div
            job_titles:
              - HR Manager
              - Senior HR Manager
        manual_users:
          - "jsmith"  # Temporary access for testing
    ```

    **Configuration Fields:**

    | Field          | Description                                                         |
    | -------------- | ------------------------------------------------------------------- |
    | `name`         | The Business Role name your app will reference                      |
    | `description`  | Human-readable description of the role's purpose                    |
    | `role_sets`    | User-attribute selectors; multiple entries are OR'd                 |
    | `division`     | Matches the synced `division` attribute, and must also be the display name of a real AD security group |
    | `job_titles`   | Matches the `jobTitle` attribute; free-form values, OR'd within the entry |
    | `manual_users` | Individual usernames (`mailNickname`) to add, for testing/exceptions |

    !!! warning "Membership is matched on attributes, not group membership"
        Both fields match user profile attributes synced from on-premises AD. Entra's `memberOf`
        operator does not evaluate on-prem synced groups, so a user is selected by the *value* of
        their `division` and `jobTitle`, never by what groups they belong to. Team names and AD
        group names will silently match nobody.

        The two fields are not validated the same way:

        - **`division`** is also looked up as an Entra ID security group display name during
          production plans. Divisions have real backing AD security groups, so a wrong value
          fails the plan rather than matching nothing.
        - **`job_titles`** are free-form profile values and are **not** validated. A wrong value
          plans and applies cleanly, then matches no users — which is the failure mode to watch
          for.

        Confirm real values before deploying:

        ```powershell
        Get-ADUser -Identity jsmith -Properties Title,Division |
            Select-Object Name, Title, Division
        ```

        To see every title available in a division:

        ```powershell
        Get-ADUser -Filter "Division -eq 'Claims Division'" -Properties Title |
            Group-Object Title | Sort-Object Count -Descending | Select-Object Count, Name
        ```

    !!! note "Non-production membership"
        In non-production these groups (`bus-role-np.*`) are created **empty**. `role_sets` and
        `manual_users` apply to **production only** — Terraform will warn if you set `manual_users`
        in a non-production workspace.

        To grant yourself a corp role for testing, add it to `Roles` in
        `infra/np-role-assignment-corp/my-roles.yml` in your personal test tools repo and run the
        `{project_id}-np-roles` pipeline. That pipeline adds you directly to the `bus-role-np.*`
        group. See [Create Test Tools Repository](../testing/create-test-tools-repository.md).

=== "External (Okta)"

    External Business Roles use a simpler structure without division grouping.

    **File:** `infra/okta/business-roles.yml` (in your external business roles repo)

    ```yaml
    business_roles:
      - name: Injured Worker
        description: Workers who have filed injury claims
        roles:
          - InjuredWorkerAccess
        manual_users:
          - TESTUSER001  # Test user for development

      - name: Employer Representative
        description: Employer contacts who manage claims for their organization
        roles:
          - EmployerRepAccess
          - EmployerAdminAccess
        manual_users: []

      - name: Policy Holder
        description: Policyholders who can view their policy information
        roles:
          - PolicyholderAccess
        manual_users: []

      - name: Medical Provider
        description: Healthcare providers who submit treatment requests
        roles:
          - MedicalProviderAccess
        manual_users: []
    ```

    #### CompoundRoles (AND-Based Membership)

    Use `compound_roles` when a user must belong to **all** listed groups simultaneously. This is useful for expressing complex access requirements without managing composite Okta groups out-of-band.

    **How the logic works:**

    - **`roles`** entries are **OR'd** — user needs membership in *any* of the listed groups
    - **`compound_roles`** entries are **AND'd** within each entry — user must be in *all* groups in that entry
    - Multiple `compound_roles` entries are **OR'd** with each other and with flat `roles`

    ```yaml
    business_roles:
      - name: "Policy Payroll Manager"
        description: "Users who are both an NGP User AND a Policy Payroll Manager"
        roles: []
        compound_roles:
          - roles:
              - NGP User
              - Policy Payroll Manager
        manual_users: []

      - name: "Senior Claims Admin"
        description: "Super Admins OR users who hold both Claims and Admin roles"
        roles:
          - Super Admin
        compound_roles:
          - roles:
              - Claims User
              - Admin User
        manual_users: []
    ```

    **Conceptual Okta expression examples:**

    The expressions below illustrate the logical result of the configuration shown above. They are **conceptual examples**, not guaranteed byte-for-byte output from the module — exact formatting, parentheses, and quoting may differ from the actual generated expression.

    | Business Role             | Example Okta Expression                                                                                      |
    | ------------------------- | ------------------------------------------------------------------------------------------------------------ |
    | `Policy Payroll Manager`  | `(isMemberOfGroupName("NGP User") AND isMemberOfGroupName("Policy Payroll Manager"))`                        |
    | `Senior Claims Admin`     | `isMemberOfGroupName("Super Admin") OR (isMemberOfGroupName("Claims User") AND isMemberOfGroupName("Admin User"))` |

    !!! tip "When to Use compound_roles"
        Use `compound_roles` when a single Okta group doesn't exist for the intersection of roles you need. If a group already represents the combined membership, use a flat `roles` entry instead.

    **Configuration Fields:**

    | Field           | Description                                                                          |
    | --------------- | ------------------------------------------------------------------------------------ |
    | `name`          | The Business Role name used in app configuration                                     |
    | `description`   | Human-readable description of the role's purpose                                     |
    | `roles`         | Okta groups that grant this Business Role (OR logic — any group qualifies)            |
    | `compound_roles`| AND-based group requirements — user must be in **all** groups within each entry       |
    | `manual_users`  | Individual users to add (use sparingly, for testing)                                  |

    !!! tip "`manual_users` for Testing"
        Use `manual_users` sparingly—primarily for test accounts or temporary exceptions. Production access should flow through proper security group membership.

---

## 🔗 Using Business Roles in Your App

Once a Business Role exists, reference it in your application's auth configuration to grant users access.

=== "Corporate (Entra ID)"

    **File:** `infra/auth/corp/config.yml` (in your app repo)

    ```yaml
    # Map existing Business Roles to your app's permissions
    business_roles:
      - name: Premium Auditor      # Must exist in business roles repo
        app_roles:
          - App.Read
          - App.Write
      - name: Claims Adjuster      # Must exist in business roles repo
        app_roles:
          - App.Read
    ```

=== "External (Okta)"

    **File:** `infra/auth/ext/user/business-role-app-role.yml` (in your app repo)

    ```yaml
    # Map existing Business Roles to your app's permissions
    authorized_business_roles:
      - name: Injured Worker       # Must exist in business roles repo
        app_roles:
          - App.Read
      - name: Employer Representative
        app_roles:
          - App.Read
          - App.Write
    ```

!!! warning "Deployment Order Matters"
    1. **First**: Deploy the business roles repo to create the Business Role
    2. **Then**: Deploy your app's auth config that references the Business Role

    If you deploy your app first, the auth pipeline will fail because the Business Role doesn't exist yet.

---

## 📊 Business Role Management

### Who Creates Business Roles?

Development teams create and manage Business Roles based on their needs:

- Identify organizational positions needing access
- Create the Business Role in the identity directory
- Follow the Division-Title naming convention
- Check if a suitable role already exists first

### Team Responsibilities

| Action    | Description                                     |
| --------- | ----------------------------------------------- |
| Create    | New Business Roles for your application's needs |
| Configure | Map Business Roles to App Roles in config files |
| Document  | Which Business Roles your app supports and why  |
| Review    | Check existing roles before creating new ones   |

---

## 🔐 Security Considerations

| Consideration        | Guidance                                            |
| -------------------- | --------------------------------------------------- |
| Sensitivity          | Business Roles represent org structure—protect them |
| Audit trail          | Log changes to Business Role assignments            |
| Separation of duties | Role creators ≠ role assigners                      |
| Regular reviews      | Verify users still need their Business Roles        |
| External scrutiny    | Extra attention to external Business Roles          |

---

## 📚 Related Documentation

- [Configure User Permissions](user-permissions.md) - Map Business Roles to app permissions
- [Configure App Permissions](app-permissions.md) - Set up API-to-API authorization
- [Authorization Concepts](../../../reference/authorization.md) - How authorization works in Forge
