> ## Documentation Index
> Fetch the complete documentation index at: https://tyk-tt17611-iam-auth.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Microsoft Entra ID On-Behalf-Of

> Configure Tyk token exchange to use Microsoft Entra ID's On-Behalf-Of (OBO) flow via the RFC 7523 jwt-bearer grant, including multi-tenant Entra applications.

## Availability

| Feature        | Editions   |
| :------------- | :--------- |
| Token exchange | Enterprise |

**Available from Tyk 5.15.0.**

## Introduction

Microsoft Entra ID solves delegated, on-behalf-of authentication with its **On-Behalf-Of (OBO)** flow. Entra rejects the [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693) token exchange grant outright, failing with `unsupported_grant_type` (`AADSTS70003`), so Entra cannot be configured as a standard [token exchange](/api-management/authentication/token-exchange) provider.

On the wire, OBO is the standard [RFC 7523](https://www.rfc-editor.org/rfc/rfc7523) JWT bearer grant plus a small number of Microsoft conventions, each of which maps onto existing token exchange configuration: the [`grantType: jwt-bearer`](/api-management/authentication/token-exchange#grant-type) provider setting, `customParams`, and the `defaultTarget` audience and scopes fields.

Token exchange runs under the [OAuth 2.0 (External IdP)](/api-management/authentication/oauth2-authentication) security scheme.

## Prerequisites

Before configuring token exchange against Entra, you need:

* An Entra app registration for the middle-tier API, which is the application Tyk Gateway authenticates as when it calls the downstream API on the caller's behalf.
* That app registration's **Application (client) ID**.
* Either a **client secret**, or a **certificate** uploaded to Tyk Gateway's certificate store if you want to authenticate with `private_key_jwt` instead of a shared secret.
* The downstream API's exposed scopes, as they appear on the app registration's **Expose an API** page (for example `Orders.Read`).

## Configure the Provider

Set `grantType: jwt-bearer` on the provider and point it at your tenant's Entra endpoints:

```yaml expandable highlight={12} theme={null}
x-tyk-api-gateway:
  server:
    authentication:
      enabled: true
      securitySchemes:
        idpAuth:
          enabled: true
          tokenExchange:
            enabled: true
            providers:
              - name: entra-obo
                grantType: jwt-bearer
                issuers:
                  - https://login.microsoftonline.com/<TENANT_ID>/v2.0
                tokenEndpoint: https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token
                clientAuth:
                  method: client_secret_post
                  clientId: <MIDDLE_TIER_APP_ID>
                  clientSecret: env://ENTRA_CLIENT_SECRET
                defaultTarget:
                  audience: api://orders-api
                  scopes:
                    - Orders.Read
                customParams:
                  requested_token_use: on_behalf_of
                cache:
                  enabled: true
```

A few fields are worth calling out:

* `clientAuth.method: client_secret_post` is the simplest path. For production, Microsoft recommends certificate-based authentication: set `method: private_key_jwt` and `certId` to a certificate already uploaded to Tyk Gateway's certificate store instead of `clientSecret`. See [Client Authentication](/api-management/authentication/token-exchange#client-authentication) for the `private_key_jwt` fields and its RSA-only constraint.
* `customParams.requested_token_use: on_behalf_of` is Entra's OBO switch. Any IdP-specific form parameter travels through `customParams` the same way.
* `defaultTarget.audience` and `defaultTarget.scopes` are the same fields RFC 8693 providers use. Under `jwt-bearer`, Tyk Gateway renders them into the wire request differently, see [Scope Rendering](/api-management/authentication/token-exchange#scope-rendering).

### The Request Entra Receives

Given the configuration above, Tyk Gateway sends:

```
POST /oauth2/v2.0/token
grant_type            = urn:ietf:params:oauth:grant-type:jwt-bearer
assertion             = <inbound user access token>
requested_token_use   = on_behalf_of
scope                 = api://orders-api/Orders.Read
client_id             = <MIDDLE_TIER_APP_ID>
client_secret         = <resolved secret>
```

This is the request described in Microsoft's [On-Behalf-Of flow](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-on-behalf-of-flow) documentation.

### Requesting All Consented Permissions

To request every permission your application has been statically granted on an API, rather than naming scopes individually, write the fully-qualified `.default` scope directly:

```yaml theme={null}
defaultTarget:
  scopes:
    - https://graph.microsoft.com/.default
```

Because this scope already contains a `/`, Tyk's [scope rendering rule](/api-management/authentication/token-exchange#scope-rendering) passes it through verbatim instead of prefixing it with `audience/`.

### Scope Grammar Constraints

Entra allows only one downstream resource per token request, and rejects a request that mixes `.default` with named permissions. Tyk Gateway does not validate Entra's scope grammar. A configuration that violates one of these rules fails at runtime with an `AADSTS` error, for example `AADSTS70011`, reported through the `idp_error` outcome. Check the [structured logs](/api-management/authentication/token-exchange#structured-logs) for the relayed error code and description.

## Conditional Access and Step-Up Authentication

If the downstream API sits behind a Conditional Access policy or requires MFA, Entra responds to the exchange with `interaction_required`. Tyk's [step-up authentication relay](/api-management/authentication/token-exchange#step-up-authentication) is always active for the `jwt-bearer` grant and needs no configuration. It returns a `401` challenge to the original caller instead of failing the exchange, so an MSAL-based (or similar) client can complete the step-up and retry.

## Multi-Tenant Applications

If your API serves users from multiple Entra tenants and you cannot enumerate every tenant ID up front, use a `regex:` issuer entry and a claim variable in `tokenEndpoint` instead of listing each tenant explicitly:

```yaml expandable theme={null}
providers:
  - name: entra-obo
    grantType: jwt-bearer
    issuers:
      - 'regex:^https://login\.microsoftonline\.com/[^/]+/v2\.0$'
    tokenEndpoint: "https://login.microsoftonline.com/$tyk_context.jwt_claims_tid/oauth2/v2.0/token"
    clientAuth:
      method: client_secret_post
      clientId: <MIDDLE_TIER_APP_ID>
      clientSecret: env://ENTRA_CLIENT_SECRET
    defaultTarget:
      audience: api://orders-api
      scopes:
        - Orders.Read
    customParams:
      requested_token_use: on_behalf_of
```

See [Multi-Tenant Issuer Matching](/api-management/authentication/token-exchange#multi-tenant-issuer-matching) for how the `regex:` prefix and the `$tyk_context.jwt_claims_tid` token endpoint variable work.

<Note>
  A `regex:` issuer entry controls which provider a token is routed to, not which tenants are trusted. Trust is decided by the JWT authentication scheme's `allowedIssuers` list. Pairing a permissive `regex:` entry with an empty or permissive `allowedIssuers` accepts any Entra tenant, so set `allowedIssuers` deliberately to the tenants you want to trust.
</Note>
