# User Permissions

Set up Business Roles and map them to application permissions for user access control.

| Property          | Value                                                      |
| ----------------- | ---------------------------------------------------------- |
| **Goal**          | Configure which users can access your application          |
| **Prerequisites** | Forge API project created, understanding of Business Roles |
| **Time Estimate** | 15-30 minutes                                              |
| **Difficulty**    | Intermediate                                               |

---

## 📋 Overview

User permissions control which Business Roles can access your application and what they can do. Configuration must be done in **both** identity providers to support corporate and external users.

### User Permission Flow

```
User → Authenticates via → Has Business Role → Granted App Roles → Can Access Features
       (Entra or Okta)    (Claims Adjuster)   (App.Read, App.Write) (based on roles)
```

---

## Step 1: Define Application Roles

Define the roles available in your application in `infra/api/config.yml`:

```yaml
# App Roles - assigned to users/groups and applications
app_roles:
  - value: App.Read
    display_name: Read Access
    description: Allows reading data
  - value: App.Write
    display_name: Write Access
    description: Allows writing data
  - value: App.Admin
    display_name: Administrator
    description: Full administrative access
```

!!! warning "Entra ID Constraint"
    App roles and scopes **cannot share the same `value`** in Entra ID. Use the [permission naming convention](../../../reference/authorization.md#permission-naming-conventions): `App.*` for app roles and `Client.*` for scopes.

For external (Okta) authentication, also define permissions in `infra/auth/ext/okta-client/user_groups.yml`:

```yaml
# Keep in sync with infra/api/config.yml app_roles
app_permissions:
  App.Read: Allows reading data
  App.Write: Allows writing data
  App.Admin: Full administrative access
```

!!! tip "Keep Files Synchronized"
    Both files must contain the same role values. Changes to one should be reflected in the other.

---

## Step 2: Map Business Roles to App Roles

Configure which Business Roles have access to which roles in **both** identity providers.

=== "Corporate (Entra ID)"

    **File:** `infra/auth/corp/config.yml`

    ```yaml
    business_roles:
      - name: Claims Adjuster
        app_roles:
          - App.Read
          - App.Write
      - name: Finance Analyst
        app_roles:
          - App.Read
    ```

=== "External (Okta)"

    **File:** `infra/auth/ext/user/business-role-app-role.yml`

    ```yaml
    authorized_business_roles:
      - name: Injured Worker
        app_roles:
          - App.Read
      - name: Employer Representative
        app_roles:
          - App.Read
    ```

---

## Step 3: Deploy Your API

Before running authentication pipelines, your API must be deployed:

1. Deploy your API using the standard pipeline (`azure-dotnet-api.yml`)
2. This creates the Entra app registration and Okta client
3. **Required**: API must be deployed to **all environments including production** before auth pipelines will work

---

## Step 4: Run Authentication Pipeline

Apply your security configuration:

**Option A: Standalone Auth Pipeline**

Run `azure-pipelines-auth.yml` to deploy auth configuration independently:

- Use when you only need to update auth configuration
- Deploys both corporate (Entra) and external (Okta) auth
- Useful for granting new apps access without redeploying your API

**Option B: With API Deployment**

Auth configuration also deploys automatically with your API:

- Standard API deployment includes auth configuration
- Use for regular deployments where both code and auth change

---

## ✅ Verification

After deploying, verify the configuration:

1. **Check Entra ID**: Verify app roles appear in the Enterprise Application
2. **Check Okta**: Verify groups are created for each app role
3. **Test Access**: Log in as a user with a mapped Business Role and verify permissions

---

## 🔍 Troubleshooting

### Users Can't Access the Application

✅ **Check:**

- Business Role is correctly mapped in the appropriate config file:
    - Corporate users: `infra/auth/corp/config.yml` → `business_roles`
    - External users: `infra/auth/ext/user/business-role-app-role.yml` → `authorized_business_roles`
- User has the correct Business Role assigned in the identity system
- Application roles are defined in `infra/api/config.yml`
- Auth pipeline has been run after configuration changes

### "Insufficient Permissions" Errors

✅ **Check:**

- The Business Role has the necessary app roles assigned
- The app roles match what the application code expects
- Both corp and ext configurations are in sync

### Configuration Out of Sync

✅ **Check:**

- App roles in `infra/api/config.yml` match `infra/auth/ext/okta-client/user_groups.yml`
- Auth pipeline has been run after all configuration changes

---

## 📋 TypeSpec Auth Configuration

Your API's TypeSpec definition includes an `@useAuth` decorator that specifies required authentication. One declaration covers both providers and all access patterns:

```typespec
@useAuth(Scopes<["Client.Read"]> | Roles<["App.Read"]>)
```

- `Scopes<["Client.Read"]>` matches the `scp` claim — delegated callers (both providers) and Okta service-to-service
- `Roles<["App.Read"]>` matches the `roles` (Entra) / `user-groups` (Okta) claims — users with Business Roles and Entra service-to-service callers

!!! note "Experience APIs and the `access` scope"
    External-facing Experience APIs are generated with `Scopes<["access"]>` instead of a `Client.*` scope. The `access` scope is platform-managed — automatically exposed for the OIDC login flow — so you don't define it in your config files. Treat it like `user_impersonation`/`user-groups`: leave it alone, and use `Client.*` for any custom scopes you add.

Ensure your TypeSpec auth decorator matches your configuration files:

| TypeSpec Reference      | Configuration File     | Key         |
| ----------------------- | ---------------------- | ----------- |
| `Roles<["App.Read"]>`   | `infra/api/config.yml` | `app_roles` |
| `Scopes<["Client.Read"]>` | `infra/api/config.yml` | `scopes`    |

!!! tip
    The template generates the correct `@useAuth` decorator based on your API type. If you add custom roles, update both the TypeSpec file and configuration files.

---

## 📚 Related Documentation

- [Understanding Business Roles](business-roles.md) - How Business Roles work
- [Configure App Permissions](app-permissions.md) - Allow other apps to call your API
- [Calling Downstream APIs](../../development/calling-apis.md) - Configure Kiota clients with scopes
