Skip to content

Secure your API with Entra ID

This how-to guides you through the steps required to secure your API using Entra ID:

Grant access to consumers

Depending on who your consumers are, you must grant access to either applications, users, or both.

Application access

Consumer applications may request tokens from Entra ID that targets your API application. Before issuing a token, Entra ID will check that the consumer is authorized to access your API application.

Consumers are not authorized by default. To authorize consumers, specify inbound access policies in your application's configuration:

app.yaml
spec:
  azure:
    application:
      enabled: true
  accessPolicy:
    inbound:
      rules:
        - application: app-a  # same namespace and cluster

        - application: app-b  # same cluster
          namespace: other-namespace

        - application: app-c
          namespace: other-namespace
          cluster: other-cluster

The above configuration authorizes the following applications:

  • application app-a running in the same namespace and same cluster as your application
  • application app-b running in the namespace other-namespace in the same cluster
  • application app-c running in the namespace other-namespace in the cluster other-cluster

User access

When performing logins, end-users are redirected to Entra ID to authenticate themselves. After logging in, Entra ID will check that the user is authorized to access your API application. Unauthorized users are stopped in Entra ID during the login flow.

Consumer applications acting on behalf of a user may also request tokens from Entra ID that targets your API application. Before issuing a token, Entra ID will check that the user is authorized to access your API application.

Users are not authorized by default. To authorize users, specify access for either specific groups, all users, or both.

Groups

To authorize users that belong to a specific set of groups, you must do two things:

app.yaml
spec:
  azure:
    application:
      enabled: true
      allowAllUsers: false
      claims:
        groups:
          - id: "<group identifier>"

Only direct members of the specified groups are authorized. Transitive membership through nested groups is not supported.

The groups claim in JWTs will include all matching group identifiers that the user is a direct member of.

Warning

Invalid group identifiers are skipped and will not be authorized to access the application in Entra ID. Ensure that the identifiers are correct and that the groups exist in Entra ID.

All users

To authorize all users, set the allowAllUsers property to true:

app.yaml
spec:
  azure:
    application:
      enabled: true
      allowAllUsers: true

In practice, the property is equivalent to specifying a set of extra groups which covers all users in the Entra ID tenant.

The groups claim in JWTs will also include these extra groups that the user is a direct member of.

Groups and all users

You can also combine the above two configurations to authorize both specific groups and all users:

app.yaml
spec:
  azure:
    application:
      enabled: true
      allowAllUsers: true
      claims:
        groups:
          - id: "<group identifier>"

This has the following effects:

  • All users are authorized to access your Entra ID application, i.e. through logins or on-behalf-of token requests.
  • The groups claim in JWTs will include matching groups identifiers that the user is a direct member of. This also includes the extra groups added by the allowAllUsers property.

The combined configuration is useful if you want to authorize all users through Entra ID and additionally use the groups claim in your application code to implement custom authorization logic.


Now that you have granted access to your consumers, they can now acquire tokens that target your application, either:

You will need to validate these tokens in your application.

Validate tokens

Verify incoming requests from consumers by validating the JWT Bearer token in the Authorization header.

The steps below describe how to validate a token using the token introspection endpoint.

What is the token introspection endpoint?

The token introspection endpoint simplifies the token validation process, but does require a network call.

If your application uses a library or framework that supports validering JWTs, you can alternatively let these handle the validation instead. See the reference page for manually validating tokens.

Send a HTTP POST request to the endpoint found in the NAIS_TOKEN_INTROSPECTION_ENDPOINT environment variable. The request must have a Content-Type header set to either:

  • application/json or
  • application/x-www-form-urlencoded

The body of the request should contain the following parameters:

Parameter Example Value Description
identity_provider azuread Always azuread.
token eyJra... The access token you wish to validate.
Token request
POST ${NAIS_TOKEN_INTROSPECTION_ENDPOINT} HTTP/1.1
Content-Type: application/json

{
    "identity_provider": "azuread",
    "token": "eyJra..."
}
Token request
POST ${NAIS_TOKEN_INTROSPECTION_ENDPOINT} HTTP/1.1
Content-Type: application/x-www-form-urlencoded

identity_provider=azuread&
token=eyJra...

The response is always a HTTP 200 OK response with a JSON body.

It always contains the active field, which is a boolean value that indicates whether the token is valid or not.

Success response

If the token is valid, the response will additionally contain all the token's claims:

Valid token
{
    "active": true,
    "exp": 1730980893,
    "iat": 1730977293,
    ...
}

Claims are copied verbatim from the token to the response.

Which claims are validated by the endpoint?

The endpoint only validates the token's signature and its standard claims.

Other claims are included in the response, but are not validated. Your application must validate these other claims according to your own requirements.

Error response

If the token is invalid, the only additional field in the response is the error field:

Invalid token
{
    "active": false,
    "error": "token is expired"
}

The error field contains a human-readable error message that describes why the token is invalid.