> ## Documentation Index
> Fetch the complete documentation index at: https://docs.strata.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Configure Authorization Policies

By the end of this guide, you will have a Maverics Orchestrator enforcing authorization policies on every request -- evaluating user attributes, roles, and context to determine whether access is allowed.

## Authentication vs. Authorization

Before diving in, it helps to understand the distinction between these two concepts because they are often confused. **Authentication** answers the question "Who is this user?" -- it verifies identity through credentials like passwords, OIDC tokens, or SAML assertions. **Authorization** answers the question "What is this user allowed to do?" -- it checks whether an authenticated user has permission to access a specific resource or perform a specific action.

The Maverics Orchestrator handles both. Your [authentication configuration](/guides/authentication/overview) verifies user identity. The authorization policies in this guide control what those authenticated users can access. Authentication always happens first -- you cannot authorize a user you have not identified.

## Prerequisites

* **A running Maverics Orchestrator** -- If you have not installed it yet, follow the [Quick Start guide](/guides/getting-started/quick-start) first.
* **An identity provider connected** -- Your Orchestrator should be authenticating users against an identity provider. The Quick Start or [SSO guides](/guides/authentication/overview) cover this.
* **Users authenticating successfully** -- Before adding authorization, confirm that users can log in and access your applications without policy restrictions.

## Configure Policies

### Choose your authorization approach

The Orchestrator provides different authorization mechanisms depending on the mode you are running. Choose the approach that matches your deployment:

* **HTTP Proxy** -- Uses location-based policies with declarative `and`/`or` rules per URL path. Each policy binds to a route and evaluates user attributes against conditions you define. See [HTTP Proxy authorization](#http-proxy) below.
* **OIDC Provider and SAML Provider** -- Uses app-level authorization with declarative rules. OIDC Provider also supports OPA token minting policies. Authorization is configured on the app itself rather than per-location. See [OIDC and SAML Provider authorization](#oidc-and-saml-provider) below.
* **MCP Bridge and MCP Proxy** (AI Identity Gateway) -- Uses OPA-based inbound authorization exclusively. Policies written in Rego evaluate per-tool invocations with access to MCP-specific context. See [OPA Authorization (AI Identity Gateway)](#opa-authorization-ai-identity-gateway) below.
* **LDAP Provider** -- Handles authorization entirely through Service Extensions. There are no declarative authorization rules. See the [Service Extensions reference](/reference/orchestrator/service-extensions) for the `authenticateSE` and `searchSE` hooks.

Not sure which mode applies? See [Choosing a Mode](/guides/authentication/choosing-a-mode).

### HTTP Proxy

In HTTP Proxy mode, authorization is configured per-location within the `policies` array on each app. Each policy specifies a URL path, the identity providers that handle authentication, and optionally the authorization rules that control access after authentication.

<Steps>
  <Step title="Define authentication policy binding">
    Before configuring authorization rules, you need a policy that binds your identity provider connectors to application routes.

    <Tabs>
      <Tab title="Console UI">
        <Info>
          **Console UI documentation is coming soon.** This section will walk you
          through configuring this component using the Maverics Console's visual
          interface, including step-by-step screenshots and field descriptions.
        </Info>

        <Frame caption="Policy model overview in Maverics Console">
          <img src="https://mintcdn.com/strataidentity/yo114yy_clZj7p9v/images/placeholder.svg?fit=max&auto=format&n=yo114yy_clZj7p9v&q=85&s=ea8d2ec72a69d5a8c7955d78abba6a30" alt="Policy model overview screen in Maverics Console showing access control configuration options" width="800" height="400" data-path="images/placeholder.svg" />
        </Frame>
      </Tab>

      <Tab title="Configuration">
        Define policies on your app with authentication connector bindings:

        ```yaml maverics.yaml theme={null}
        apps:
          - name: my-app
            type: proxy
            upstream: https://backend.example.com
            routePatterns:
              - "app.example.com"
            policies:
              - location: "/"
                authentication:
                  idps: ["azure", "ldap"]
        ```

        | Field                                            | Description                                                                  |
        | ------------------------------------------------ | ---------------------------------------------------------------------------- |
        | `policies[].location`                            | URL path this policy applies to (must be unique per app)                     |
        | `policies[].authentication.idps`                 | Connector names for authentication -- must match `connectors[].name` entries |
        | `policies[].authentication.allowUnauthenticated` | Allow unauthenticated access to this location (default: `false`)             |
        | `policies[].decision.lifetime`                   | Authorization decision cache TTL (duration string, e.g., `"5m"`)             |

        The `idps` array references connectors by name. The Orchestrator will redirect unauthenticated users to the first listed identity provider for login.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Define authorization rules">
    Authorization rules determine whether an authenticated user is allowed to access a resource. Rules support nested `and`/`or` conditions with four comparison operators: `equals`, `notEquals`, `contains`, and `notContains`.

    The Orchestrator supports multiple access control models, and you can combine them:

    **RBAC (Role-Based Access Control)** assigns permissions based on roles. A user has one or more roles (like "admin," "editor," or "viewer"), and each role grants access to specific resources. RBAC is straightforward and works well when your access patterns align with job functions.

    **ABAC (Attribute-Based Access Control)** makes decisions based on attributes -- properties of the user, the resource, or the request context. Attributes can include things like department, location, or any claim from the identity provider.

    **PBAC (Policy-Based Access Control)** makes decisions based on centralized policies evaluated against request context, user attributes, and environmental conditions. PBAC generalizes RBAC and ABAC by expressing access rules as policies that can incorporate any combination of roles, attributes, and contextual factors.

    **External PDP (Policy Decision Point)** integration delegates authorization decisions to an external policy engine -- such as OPA, Cedar, or a custom PDP -- for organizations that maintain centralized policy engines across multiple systems.

    <Tabs>
      <Tab title="Console UI">
        <Info>
          **Console UI documentation is coming soon.** This section will walk you
          through configuring this component using the Maverics Console's visual
          interface, including step-by-step screenshots and field descriptions.
        </Info>

        <Frame caption="Policy definition in Maverics Console">
          <img src="https://mintcdn.com/strataidentity/yo114yy_clZj7p9v/images/placeholder.svg?fit=max&auto=format&n=yo114yy_clZj7p9v&q=85&s=ea8d2ec72a69d5a8c7955d78abba6a30" alt="Policy definition screen in Maverics Console showing role and attribute condition builders" width="800" height="400" data-path="images/placeholder.svg" />
        </Frame>
      </Tab>

      <Tab title="Configuration">
        **Allow all authenticated users** (simplest authorization):

        ```yaml maverics.yaml theme={null}
        policies:
          - location: "/"
            authentication:
              idps: ["azure"]
            authorization:
              allowAll: true
        ```

        **Simple role-based rule:**

        ```yaml maverics.yaml theme={null}
        policies:
          - location: "/admin"
            authentication:
              idps: ["azure"]
            authorization:
              rules:
                - and:
                    - contains: ["{{ azure.groups }}", "admin-group"]
        ```

        **Nested and/or rules** for complex access control:

        ```yaml maverics.yaml theme={null}
        policies:
          - location: "/"
            authentication:
              idps: ["azure", "ldap"]
            authorization:
              rules:
                - and:
                    - equals: ["{{ ldap.role }}", "admin"]
                    - contains: ["{{ ldap.memberOf }}", "cn=managers"]
                - or:
                    - equals: ["{{ http.request.method }}", "GET"]
                    - notContains: ["{{ ldap.department }}", "restricted"]
              rulesAggregationMethod: "and"
        ```

        In this example: the first rule requires the user to be an admin AND a member of the managers group. The second rule allows GET requests OR users not in the restricted department. Both rules must pass (`rulesAggregationMethod: "and"`).

        **Operators:**

        | Operator      | Description                   | Operands   |
        | ------------- | ----------------------------- | ---------- |
        | `equals`      | Exact string match            | 2 operands |
        | `notEquals`   | Inverse exact match           | 2 operands |
        | `contains`    | Substring or membership check | 2 operands |
        | `notContains` | Inverse substring/membership  | 2 operands |

        **Operand formats:**

        * `{{ connector.attribute }}` -- claim from a named connector (e.g., `{{ azure.groups }}`)
        * `{{ http.request.method }}` -- HTTP request method
        * `"literal"` -- static comparison value

        See [Authorization Reference](/reference/orchestrator/authorization) for the complete rule syntax and per-mode comparison.
      </Tab>
    </Tabs>

    <Tip>
      Start with broad policies and refine as needed. A common pattern is to
      require authentication on all routes first, then add access control
      restrictions to specific paths. You can always tighten access later
      without disrupting users who already have the right permissions.
    </Tip>
  </Step>

  <Step title="Add programmatic authorization (optional)">
    For authorization logic that goes beyond declarative rules, HTTP Proxy mode supports two Service Extension hooks:

    * **`isAuthorizedSE`** runs after declarative rule evaluation and can override or supplement the result. This lets you implement dynamic authorization checks -- for example, querying an external system, checking time-of-day restrictions, or applying business logic that cannot be expressed as `and`/`or` rules. Configure at `policies[].authorization.isAuthorizedSE`. See [Service Extensions](/reference/orchestrator/service-extensions).

    * **`handleUnauthorizedSE`** customizes the response when authorization is denied. Instead of the default 403 Forbidden page, you can render a custom error page, redirect to a request-access workflow, or log additional context. Configure at `apps[].handleUnauthorizedSE`. This hook is mutually exclusive with the `unauthorizedPage` setting. See [Service Extensions](/reference/orchestrator/service-extensions).
  </Step>
</Steps>

### OIDC and SAML Provider

In OIDC Provider and SAML Provider modes, authorization is configured at the app level (`apps[].authorization`) rather than per-location. The same declarative rule syntax is available -- `allowAll`, `rules`, and `rulesAggregationMethod` -- but the configuration lives directly on the app instead of inside a `policies[]` block.

<Steps>
  <Step title="Configure declarative rules">
    <Tabs>
      <Tab title="Console UI">
        <Info>
          **Console UI documentation is coming soon.** This section will walk you
          through configuring this component using the Maverics Console's visual
          interface, including step-by-step screenshots and field descriptions.
        </Info>
      </Tab>

      <Tab title="Configuration">
        **Allow all authenticated users:**

        ```yaml maverics.yaml theme={null}
        apps:
          - name: my-oidc-app
            type: oidc
            authorization:
              allowAll: true
        ```

        **Role-based rule at the app level:**

        ```yaml maverics.yaml theme={null}
        apps:
          - name: my-oidc-app
            type: oidc
            authorization:
              rules:
                - and:
                    - contains: ["{{ azure.groups }}", "app-users"]
              rulesAggregationMethod: "and"
        ```

        The rule syntax (`and`, `or`, operators, operand formats) is the same as HTTP Proxy. The only difference is the config location: `apps[].authorization` instead of `apps[].policies[].authorization`.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Configure OPA token minting policies (OIDC Provider only, optional)">
    OIDC Provider mode supports OPA policies for token minting authorization. This provides a governance layer over which tokens the Orchestrator issues -- you can enforce policies on token exchange based on user attributes, client identity, or environmental conditions. This does not apply to SAML Provider mode, which issues assertions rather than tokens.

    ```yaml maverics.yaml theme={null}
    apps:
      - name: my-oidc-app
        type: oidc
        authorization:
          tokenMinting:
            accessToken:
              policies:
                - name: token-governance
                  rego: |
                    package orchestrator

                    default result := {"allowed": true}

                    # Deny tokens requested with the client_credentials grant type
                    result := {"allowed": false, "internal_message": "client_credentials not permitted"} {
                      input.request.oauth.grant_type == "client_credentials"
                    }
    ```

    OPA token minting policies must define a `result` object containing an `allowed` field. The Orchestrator denies token issuance if `allowed` is `false`. You can optionally include `internal_message` (logged server-side) and `external_message` (returned to the client) fields. See [OPA Policy Output Schema](#opa-policy-output-schema) for the full output format.

    #### OIDC app input schema

    The following JSON shows the complete data available under `input` when evaluating an OIDC token minting policy. Values are demonstrative.

    ```json theme={null}
    {
      "source": {
        "ip": "192.168.1.100"
      },
      "request": {
        "http": {
          "host": "idp.example.com",
          "method": "POST",
          "path": "/token",
          "headers": {
            "Content-Type": "application/x-www-form-urlencoded",
            "User-Agent": "Go-http-client/1.1"
          }
        },
        "oauth": {
          "client_id": "325859cf-5bbe-492f-b9e7-929f2bed1230",
          "scope": "read:users write:settings",
          "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
          "subject_token": {
            "claims": {
              "scope": "api:read api:write"
            }
          },
          "actor_token": {
            "claims": {
              "scope": "openid"
            }
          },
          "response": {
            "access_token": {
              "claims": {
                "scope": "read:users write:settings",
                "aud": "https://api.example.com",
                "client_id": "325859cf-5bbe-492f-b9e7-929f2bed1230"
              }
            },
            "scope": "read:users write:settings",
            "expires_in": 3600
          }
        }
      }
    }
    ```

    #### OIDC app example: enforce scope reduction on token exchange

    The following policy ensures that scopes are only reduced during token exchange flows. The requested scopes in the token exchange request must be a subset of the scopes in the `subject_token`. All other grant types are allowed without restriction.

    ```rego policy.rego theme={null}
    package orchestrator

    default result["allowed"] := false

    # Allow all non-token-exchange grant types.
    result["allowed"] if {
    	input.request.oauth.grant_type != "urn:ietf:params:oauth:grant-type:token-exchange"
    }

    # Parse scopes from space-separated strings into sets.
    requested_scopes := {s | some s in split(input.request.oauth.scope, " "); s != ""}
    subject_scopes := {s | some s in split(input.request.oauth.subject_token.claims.scope, " "); s != ""}

    # For token exchange grants, allow if requested scopes are a subset of subject token scopes.
    result["allowed"] if {
    	input.request.oauth.grant_type == "urn:ietf:params:oauth:grant-type:token-exchange"
    	print("requested_scopes:", requested_scopes)
    	print("subject_scopes:", subject_scopes)
    	count(requested_scopes - subject_scopes) == 0
    }

    result["internal_message"] := "requested scopes exceed subject_token scopes" if {
    	not result.allowed
    }

    result["external_message"] := "invalid_scope" if {
    	not result.allowed
    }
    ```
  </Step>

  <Step title="Add programmatic authorization (optional)">
    The `isAuthorizedSE` Service Extension hook is also available in OIDC Provider and SAML Provider modes. Configure at `apps[].authorization.isAuthorizedSE`. See [Service Extensions](/reference/orchestrator/service-extensions).
  </Step>
</Steps>

### AI Identity Gateway (OPA Authorization)

In MCP Bridge and MCP Proxy modes (AI Identity Gateway), OPA is the only authorization mechanism. There are no declarative `and`/`or` rules. All authorization is handled through Rego policies that evaluate per-tool invocations with access to MCP-specific context such as tool name and arguments.

Configure OPA via `authorization.inbound.opa` with a policy `name` and either `file` (path to a `.rego` file) or inline `rego`.

<Steps>
  <Step title="Configure MCP app inbound authorization policies">
    <Tabs>
      <Tab title="Console UI">
        <Info>
          **Console UI documentation is coming soon.** This section will walk you
          through configuring this component using the Maverics Console's visual
          interface, including step-by-step screenshots and field descriptions.
        </Info>

        <Frame caption="OPA policy configuration in Maverics Console">
          <img src="https://mintcdn.com/strataidentity/yo114yy_clZj7p9v/images/placeholder.svg?fit=max&auto=format&n=yo114yy_clZj7p9v&q=85&s=ea8d2ec72a69d5a8c7955d78abba6a30" alt="OPA policy configuration screen in Maverics Console" width="800" height="400" data-path="images/placeholder.svg" />
        </Frame>
      </Tab>

      <Tab title="Configuration">
        **Inline OPA policy:**

        ```yaml maverics.yaml theme={null}
        authorization:
          inbound:
            type: opa
            opa:
              name: my-auth-policy
              rego: |
                package orchestrator

                default result := {"allowed": false}

                # Allow the listUsers tool
                result := {"allowed": true} {
                  input.request.mcp.type == "tool"
                  input.request.mcp.tool.params.name == "listUsers"
                }

                # Allow the getUser tool
                result := {"allowed": true} {
                  input.request.mcp.type == "tool"
                  input.request.mcp.tool.params.name == "getUser"
                }
        ```

        **File-based OPA policy:**

        ```yaml maverics.yaml theme={null}
        authorization:
          inbound:
            type: opa
            opa:
              name: my-auth-policy
              file: /etc/maverics/policies/auth.rego
        ```
      </Tab>
    </Tabs>

    For the complete per-mode authorization comparison, see the [Authorization Reference](/reference/orchestrator/authorization#per-mode-authorization-differences).
  </Step>

  <Step title="OPA input schema and example policy">
    OPA policies must define a `result` object containing an `allowed` field. The Orchestrator denies the request if `allowed` is `false`. You can optionally include `internal_message` (logged server-side) and `external_message` (returned to the client) fields. See [OPA Policy Output Schema](#opa-policy-output-schema) for the full output format.

    #### MCP app input schema

    The following JSON shows the complete data available under `input` when evaluating an MCP Proxy or MCP Bridge inbound authorization policy. Values are demonstrative.

    ```json theme={null}
    {
      "source": {
        "ip": "192.168.1.100"
      },
      "request": {
        "http": {
          "host": "mcp.example.com",
          "method": "POST",
          "path": "/mcp",
          "headers": {
            "Authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
            "Content-Type": "application/json",
            "User-Agent": "MCP-Client/1.0"
          }
        },
        "mcp": {
          "type": "tool",
          "tool": {
            "params": {
              "name": "listUsers",
              "arguments": {
                "exampleBodyKeyAlpha": 10,
                "exampleBodyKeyBeta": true
              }
            }
          }
        }
      }
    }
    ```

    #### MCP app example: JWT-based tool authorization

    The following policy grants access to the `get_ticket_price` tool only if the `Authorization` header contains a JWT with the `tickets:read` scope. All other tools and requests are denied by default.

    ```rego policy.rego theme={null}
    package orchestrator

    # Default deny policy - all requests are denied unless explicitly allowed
    default result["allowed"] := false

    # Helper rule to extract and decode JWT from Authorization header.
    # Parses the Bearer token and returns the decoded payload for policy evaluation.
    jwt_payload := payload if {
    	auth_header := input.request.http.headers.Authorization
    	startswith(auth_header, "Bearer ")
    	token := substring(auth_header, 7, -1)
    	[_, payload, _] := io.jwt.decode(token)
    }

    # Allows access to the get_ticket_price tool if the token contains
    # the tickets:read scope. Logs the tool name, client ID, and subject
    # for audit purposes.
    result["allowed"] if {
    	print("request made to tool: ", input.request.mcp.tool.params.name)
    	input.request.mcp.tool.params.name == "get_ticket_price"

    	print("request made with client of: ", jwt_payload.client_id)
    	contains(jwt_payload.scope, "tickets:read")

    	print("access granted to subject:", jwt_payload.sub)
    }

    result["internal_message"] := "access is only permitted to the 'get_ticket_price' tool with tickets:read scope" if {
    	not result.allowed
    }

    result["external_message"] := "unauthorized, contact support@example.com" if {
    	not result.allowed
    }
    ```
  </Step>
</Steps>

### OPA Policy Output Schema

All OPA policies -- whether for OIDC token minting or MCP inbound authorization -- must return a `result` object with the following structure:

```json theme={null}
{
  "result": {
    "allowed": true,
    "internal_message": "user does not have necessary group membership: missing admin membership",
    "external_message": "unauthorized"
  }
}
```

| Field                     | Required | Description                                                                         |
| ------------------------- | -------- | ----------------------------------------------------------------------------------- |
| `result.allowed`          | Yes      | Whether access should be granted (`true`) or denied (`false`)                       |
| `result.internal_message` | No       | Details about the decision for server-side logs only. Not returned to clients.      |
| `result.external_message` | No       | Details returned to the client and also logged. Use for user-facing error messages. |

<Tip>
  Use `print()` statements in your Rego policies to emit debug output to Orchestrator logs. This is useful for tracing which rules matched and inspecting input values during policy development.
</Tip>

### Test policy enforcement

With policies defined and applied, test that they work as expected by making requests as users with different roles and attributes. Testing both the "allowed" and "denied" paths confirms that your policies are enforced correctly.

<Steps>
  <Step title="Start the Orchestrator">
    Start (or restart) the Orchestrator to load your policy configuration:

    ```bash theme={null}
    maverics -config /etc/maverics/maverics.yaml
    ```
  </Step>

  <Step title="Verify allowed access">
    Test with a user who should have access:

    1. **Log in** as a user whose roles or attributes satisfy the policy conditions
    2. **Navigate** to the protected resource -- you should see the application content
    3. **Verify** the request reached your upstream application (check application logs)
  </Step>

  <Step title="Verify denied access">
    Test with a user who should be denied:

    1. **Log in** as a user who does not meet the policy conditions
    2. **Navigate** to the same resource -- you should receive a 403 Forbidden response
    3. **Verify** the request did not reach your upstream application

    <Check>
      **Success!** Your Orchestrator is enforcing authorization policies.
      Requests from authorized users proceed to your applications, and
      unauthorized requests are blocked before they ever reach your
      upstream services.
    </Check>
  </Step>
</Steps>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Policy not being evaluated">
    If a policy does not seem to take effect, verify that it is attached to the
    correct application route. A defined but unattached policy is inert -- it
    exists in the configuration but does not affect any traffic. Also check
    that the route path pattern matches the URLs you are testing. The
    Orchestrator logs which policies are evaluated on each request when
    debug logging is enabled.
  </Accordion>

  <Accordion title="Unexpected access denied">
    If a user is denied access when you expect them to be allowed, check the
    user's claims from the identity provider. The most common cause is a
    mismatch between the role or attribute name in your policy and the actual
    claim value from the IdP. For example, your policy may check for a role
    called "admin" but the IdP sends "Admin" (case matters). Enable debug
    logging to see the exact claims the Orchestrator receives and the policy
    evaluation result.
  </Accordion>

  <Accordion title="Attribute not available for policy evaluation">
    If a policy references an attribute that the identity provider does not
    include in its token or assertion, the policy evaluation fails because the
    attribute is missing. Verify that your identity provider is configured to
    include the needed claims (roles, groups, department, or custom attributes)
    in the token it sends to the Orchestrator. Check the
    [Identity Fabric reference](/reference/orchestrator/identity-fabric)
    for details on claim mapping for your specific provider.
  </Accordion>
</AccordionGroup>

## Related Pages

<CardGroup cols={2}>
  <Card title="Security Overview" icon="shield-halved" href="/guides/security/overview">
    Return to the Security guides hub for TLS, secrets, policies, and compliance
  </Card>

  <Card title="Authorization Reference" icon="shield-check" href="/reference/orchestrator/authorization">
    Per-mode authorization differences, enforcement behavior, and OPA policy patterns
  </Card>

  <Card title="Applications Reference" icon="file-code" href="/reference/orchestrator/applications">
    Complete configuration reference for authorization rules, operators, and policy syntax
  </Card>

  <Card title="Authentication Guides" icon="key" href="/guides/authentication/overview">
    Configure SSO, SAML federation, and identity provider connections that policies build on
  </Card>

  <Card title="Service Extensions" icon="code" href="/reference/orchestrator/service-extensions">
    Custom authorization logic via isAuthorizedSE, handleUnauthorizedSE, and other SE hooks
  </Card>

  <Card title="Compliance and Audit" icon="clipboard-check" href="/guides/security/compliance">
    Log policy decisions and authorization events for compliance reporting
  </Card>
</CardGroup>
