# Authorization

## 📋 Summary

Forge uses two identity providers to secure API access:

- **Corporate (Entra ID)** — internal employees and service accounts
- **External (Okta)** — policyholders, injured workers, employers, and providers

Both providers are always active. When you expose permissions on your API or call a downstream API, you must configure both.

---

## 🏷️ Key Terminology

| Concept           | What It Is                                                                          | Example                         |
| ----------------- | ----------------------------------------------------------------------------------- | ------------------------------- |
| **Scope**         | A permission **requested** by a calling application — on behalf of a user, or as itself | `Client.Read`               |
| **App Role**      | A permission **granted** to a user (via Business Roles) or to an application        | `App.Read`                      |
| **Business Role** | An organizational position that maps to one or more App Roles                       | `Claims Adjuster`, `HR Manager` |

---

## 🏷️ Permission Naming Conventions

Two prefixes, two mechanisms. The prefix tells you exactly what a permission is and where it goes — the same in **both** providers:

!!! danger "Entra ID: Scopes and App Roles cannot share the same value"
    In Entra ID, a `scope` value and an `app_role` value on the same app registration must never be identical. With this convention `Client.*` names only ever appear under `scopes:` and `App.*` names only ever under `app_roles:`, so collisions cannot happen.

| Prefix | Mechanism | Entra config section | Okta config file | Token claim |
| ------ | --------- | -------------------- | ---------------- | ----------- |
| `Client.*` | **Scope** — requested by calling applications | `infra/api/config.yml` → `scopes:` | `infra/auth/ext/okta-client/scopes.yml` | `scp` |
| `App.*` | **Role** — granted to users or applications | `infra/api/config.yml` → `app_roles:` | `infra/auth/ext/okta-client/user_groups.yml` | `roles` (Entra) / `user-groups` (Okta) |

!!! note "Platform-managed delegation scopes"
    `user_impersonation` (Entra) and `user-groups` (Okta) are requested automatically by the platform for user-delegated calls — never add them to `options.Scopes` or define them as custom scopes. Downstream APIs must still **grant** them to calling apps in their `authorized_apps` configuration. Experience APIs additionally expose a platform-managed `access` scope for the OIDC login flow — leave it alone too.

!!! note "Service-to-service calls"
    Okta callers request your `Client.*` scopes directly. Entra callers always request `.default` and receive the `App.*` roles you granted them in `authorized_apps` (in the token's `roles` claim). For service-to-service access, define a `Client.*` scope and a matching `App.*` role, and accept either in `@useAuth` — see [App Permissions](../guides/security/configuration/app-permissions.md).

!!! tip "When writing raw APIM or JWT claim checks"
    If you write raw policy or claim validation, check the **Token claim** column above — `Client.*` is always in `scp`, while `App.*` is in `roles` (Entra) or `user-groups` (Okta). If you use the platform's TypeSpec `@useAuth` decorator and generated APIM policies, the platform handles claim-location differences for you.

!!! tip "App.* is granted, never requested"
    `App.*` roles are assigned to users (via Business Roles) or to applications (Entra `authorized_apps`). Never put `App.*` names in `options.Scopes` when configuring a Kiota client — `options.Scopes` only ever contains `Client.*` names.

**Examples:**

- `Client.Read`, `Client.Write` — what a calling application may do (on behalf of a user, or as itself)
- `App.Read`, `App.Admin` — what a user with a certain Business Role, or a service granted the role in Entra, may do

See [App Permissions](../guides/security/configuration/app-permissions.md) for configuration examples.

---

## 🏛️ Dual-Identity Architecture

| Aspect | Corporate (Entra ID) | External (Okta) |
| ------ | -------------------- | --------------- |
| **Users** | Internal employees | Policyholders, injured workers, employers, providers |
| `Client.*` scopes defined in | `infra/api/config.yml` → `scopes:` | `infra/auth/ext/okta-client/scopes.yml` |
| `App.*` roles defined in | `infra/api/config.yml` → `app_roles:` | `infra/auth/ext/okta-client/user_groups.yml` |
| **Business Role mapping** | `infra/auth/corp/config.yml` | `infra/auth/ext/user/business-role-app-role.yml` |
| **Platform delegation scope** | `user_impersonation` (auto-added) | `user-groups` (auto-added) |

---

## 🎫 Proof of Identity and Authorization

When accessing a resource, identity and authorization are validated via a JWT that was digitally signed by the authorization server that minted it. This is a JSON payload that contains claims that contain the identity and access authorization.

**Sample JWT:**

```json
{
  "ver": 1,
  "jti": "AT.Bdk6ykeyEsSGj9sFNvZC7_9LzH7-ypGpK34a2iCZwsQ",
  "iss": "https://saif-oie.oktapreview.com/oauth2/aushpkatj89kOhK6Y1d7",
  "aud": "api://it-api-sys-envsvc",
  "iat": 1729721521,
  "exp": 1729725121,
  "cid": "0oafxgi1h1dCu0ffJ1d7",
  "uid": "00uci6hkxxR6Ms7lk1d7",
  // scp claim determines requested scope access
  "scp": [
    "it-api-sys-envsvc.Client.Read",
    "it-api-sys-envsvc.user-groups"
  ],
  "auth_time": 1729721521,
  // sub claim determines the user (subject)
  "sub": "shasca@saif.com",
  // user-groups claim determines user role access
  "user-groups": ["it-api-sys-envsvc.App.Read", "it-api-sys-envsvc.App.Write"]
}
```

This example shows an Okta token, where permission values include the project ID prefix. In TypeSpec, use the logical permission names (`Client.Read`, `App.Read`); the platform-generated APIM policies handle provider-specific formatting.

---

## 🛡️ Where the Authorization Occurs

Authorization is performed by **Azure API Management**. When an API is deployed, policies are created on either the API, or individual endpoints specifying values in the **scp** and **user-groups** claims of the token required for authorization to occur.

### API Policy Types

The `api_policy_type` setting in `infra/api/vars.yml` controls how APIM authenticates incoming requests:

| Policy Type        | Authentication Method           | Use Case                                                                 |
| ------------------ | ------------------------------- | ------------------------------------------------------------------------ |
| `standard`         | JWT validation (Okta + Entra)   | Default — user-delegated and service-to-service flows with OAuth tokens  |
| `filevine`         | JWT validation (Filevine IdP)   | Filevine webhook integration with dedicated JWT issuer                   |
| `subscription_key` | APIM subscription key           | Legacy server-to-server access without OAuth — see [Subscription Key Authentication](../guides/security/configuration/subscription-key-auth.md) |

!!! note "Subscription Key Auth"
    The `subscription_key` policy type is only available for Experience APIs (`is_experience_api = true`). It bypasses JWT validation entirely — APIM validates the subscription key natively and sets `x-forward-tenant-origin: corp` for all requests.

---

## ✅ True Authorization

For true authorization, both the requesting app and user the request is on behalf of must be authorized to access the resource. If either does not meet the policy requirements, then access is forbidden by API Management.

![True Authorization Venn Diagram](./true-authorization-venn-diagram.png)

---

## 📚 Related Documentation

- [How Authentication Works](./how-authentication-works.md) - Flow mechanics: token validation, acquisition, and `ScopeBuilder` behavior
- [Security Configuration Guide](../guides/security/index.md) - Step-by-step instructions for configuring authentication and authorization
- [Subscription Key Authentication](../guides/security/configuration/subscription-key-auth.md) - Configure APIM subscription key auth for legacy apps
- [Business Roles](../guides/security/configuration/business-roles.md) - How Business Roles work and configuration examples
- [User Permissions](../guides/security/configuration/user-permissions.md) - Map Business Roles to App Roles
- [App Permissions](../guides/security/configuration/app-permissions.md) - Configure scopes and authorize upstream apps
- [Calling Downstream APIs](../guides/development/calling-apis.md) - Configure Kiota clients with proper scopes
- [Environments](./environments.md) - Environment configurations and deployment flow
