Entra ID reference¶
Spec¶
For all possible configuration options, see the Nais application reference.
Runtime Variables & Credentials¶
Your application will automatically be injected with the following environment variables at runtime.
Environment Variable | Description |
---|---|
NAIS_TOKEN_ENDPOINT |
Used to |
NAIS_TOKEN_EXCHANGE_ENDPOINT |
Used to |
NAIS_TOKEN_INTROSPECTION_ENDPOINT |
Used to |
For further details about these endpoints, see the OpenAPI specification.
Variables for manually validating tokens¶
These variables are optional and should only be used for manually validating tokens when:
Name | Description |
---|---|
AZURE_APP_CLIENT_ID |
Client ID that uniquely identifies the application in Entra ID. |
AZURE_APP_WELL_KNOWN_URL |
The well-known URL for the metadata discovery document. |
AZURE_OPENID_CONFIG_ISSUER |
issuer from the metadata discovery document. |
AZURE_OPENID_CONFIG_JWKS_URI |
jwks_uri from the metadata discovery document. |
AZURE_APP_WELL_KNOWN_URL
is optional if you're using AZURE_OPENID_CONFIG_ISSUER
and AZURE_OPENID_CONFIG_JWKS_URI
directly.
Claims¶
Notable claims in tokens from Entra ID. For a complete list of claims, see the Access Token Claims Reference in Entra ID. We only use v2.0 tokens.
azp
(authorized party)-
The client ID of the application that requested the token (this would be your consumer).
azp_name
(authorized party name)-
Human-readable client name of the consumer application that requested the token. This complements the client ID found in the
azp
claim and is intended for display purposes only.Not guaranteed to be unique. Should not be used for authorization.
groups
-
JSON array of group identifiers that the user is a member of:
{ "groups": [ "43451d82-19cd-4828-918d-cbf7d5b572ec", "8f0bd3b2-9d3d-4f27-8543-5938db3e6a3e", "2d7..." ] }
Used to implement group-based authorization logic in your application.
Only appears in flows where an end-user is involved, i.e. either login or on-behalf-of.
In order for a group to appear in the claim, all the following conditions must be true:
- The given user is a direct member of the group.
- The group has been granted access to the application.
idtyp
(identity type)-
This is a special claim used to determine whether a token is a machine-to-machine (app-only) token or a on-behalf-of (user) token.
A token is a machine-to-machine token if and only if this claim exists and has the value equal to
app
. NAVident
-
Internal identifier for the employees in NAV.
Only appears in flows where an end-user is involved, i.e. either login or on-behalf-of.
roles
-
JSON array of roles that the application has access to:
Only appears in machine-to-machine tokens.
Consumers defined in the access policy are always assigned the default role named
access_as_application
. You can optionally define and grant them custom roles. scp
(scope)-
The value of this claim is a space-separated string that lists the scopes that the application has access to:
Only appears in on-behalf-of tokens.
Consumers defined in the access policy are always assigned the default scope named
defaultaccess
. You can optionally define and grant them custom scopes.
Access policies¶
Applications¶
By default, no applications have access to your API. You must explicitly grant access to consumer applications.
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 namespaceother-namespace
in the same cluster - application
app-c
running in the namespaceother-namespace
in the clusterother-cluster
Users¶
By default, no users have access to your application. You must explicitly grant access to either specific groups, all users, or both.
Groups¶
To only allow access for users that belong to a specific set of groups, you must do two things:
- specify the group identifiers. To find your group's identifier, see finding the group identifier.
- set the
allowAllUsers
property tofalse
spec:
azure:
application:
enabled: true
allowAllUsers: false
claims:
groups:
- id: "<group identifier>"
Entra ID will deny logins and on-behalf-of token requests for users that aren't direct members of the specified groups. Transitive membership through nested groups is not supported.
The groups
claim in JWTs will include matching groups identifiers that the user is a direct member of.
Warning
Invalid group identifiers are skipped and will not be granted access to your application. Ensure that they are correct and exist in Entra ID.
All users¶
To allow all users to access your application, set the allowAllUsers
property to true
:
This is assigns a set of extra groups to the application, which covers all users in the Entra ID tenant.
The groups
claim in JWTs will also include the extra groups that the user is a direct member of.
Groups and all users¶
If you want to implement custom group-based authorization logic in your application, combine the above two configurations:
spec:
azure:
application:
enabled: true
allowAllUsers: true
claims:
groups:
- id: "<group identifier>"
This has the following effects:
- All users are allowed to access your 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 theallowAllUsers
property.
Fine-grained permissions¶
You may define custom permissions for your application in Entra ID and grant them to other consumer applications. Permissions will appear as claims in the consumer's token. Your application can then use these claims to implement custom authorization logic.
Warning
Custom permissions only apply in the context of your own application. They are not global permissions.
All the following conditions must be met for the custom permission to appear:
- The token is acquired by a consumer of your application.
- The consumer has been granted a custom permission in your access policy definition.
- The target audience is your application.
Custom scopes¶
A scope only applies to tokens acquired on behalf of an employee.
Applications defined in the access policy are always assigned the default scope named defaultaccess
.
spec:
accessPolicy:
inbound:
rules:
- application: app-a
namespace: other-namespace
cluster: other-cluster
permissions:
scopes:
- "custom-scope"
The above configuration grants the application app-a
the scope custom-scope
.
Scopes will appear as a space separated string in the scp
claim within the user's token.
Example decoded on-behalf-of token (click to expand)
{
"aud": "8a5...",
"iss": "https://login.microsoftonline.com/.../v2.0",
"iat": 1624957183,
"nbf": 1624957183,
"exp": 1624961081,
"aio": "AXQ...",
"azp": "e37...",
"azpacr": "1",
"groups": [
"2d7..."
],
"name": "Navnesen, Navn",
"oid": "15c...",
"preferred_username": "Navn.Navnesen@nav.no",
"rh": "0.AS...",
"scp": "custom-scope defaultaccess",
"sub": "6OC...",
"tid": "623...",
"uti": "i03...",
"ver": "2.0"
}
Custom roles¶
A role only applies to tokens acquired as an application (machine-to-machine calls).
Applications defined in the access policy are always assigned the default role named access_as_application
.
spec:
accessPolicy:
inbound:
rules:
- application: app-a
namespace: other-namespace
cluster: other-cluster
permissions:
roles:
- "custom-role"
The above configuration grants the application app-a
the role custom-role
.
Roles will appear in the roles
claim as an array of strings within the application's token.
Example decoded client credentials token (click to expand)
{
"aud": "8a5...",
"iss": "https://login.microsoftonline.com/.../v2.0",
"iat": 1624957347,
"nbf": 1624957347,
"exp": 1624961247,
"aio": "E2Z...",
"azp": "e37...",
"azpacr": "1",
"oid": "933...",
"rh": "0.AS...",
"roles": [
"access_as_application",
"custom-role"
],
"sub": "933...",
"tid": "623...",
"uti": "kbG...",
"ver": "2.0"
}
Tenants¶
Available tenants in Entra ID. NAV has two tenants in Entra ID:
Tenant Name | Description |
---|---|
nav.no |
Production tenant |
trygdeetaten.no |
Development tenant |
Logging into the trygdeetaten.no
tenant
The trygdeetaten.no
tenant is used for development purposes.
To log in to this tenant, you will need a separate account other than your personal NAV account.
There are two types of accounts you can use:
- Synthetic test accounts: Visit the IDA self-service portal. See the #ida channel on Slack for details.
- Personal account: Visit navikt/devuser-check and see the "FAQ" section. Otherwise, consult the #tech-azure channel on Slack.
See also .spec.azure.application.tenant
.
Troubleshooting¶
This section lists common problems and solutions.
Missing application access policy¶
Your application (named your-application
in the examples below) attempts to consume another application (named target-application
).
When requesting a token from Entra ID, your application may receive a 400 Bad Request
with an invalid_grant
error response and the following message:
AADSTS501051:
Application
'<client ID>'
(<cluster>:<namespace>:<your-application>
) is not assigned to a role for the application 'api://<cluster>.<namespace>.<target-application>
'
Or the other variant:
AADSTS65001:
The user or administrator has not consented to use the application with ID '
<client ID>
' named '<cluster>:<namespace>:<your-application>
'.Send an interactive authorization request for this user and resource.
Solution / Answer
-
Ensure that the scope or
target
parameter for your request follows the correct format:api://<cluster>.<namespace>.<target-application>/.default
-
Ensure that the target application has configured an inbound access policy that grants your application access to the target application.
- If all else fails, request assistance in the
#nais
channel on Slack.
Missing user access policy¶
When attempting to sign-in or perform the on-behalf-of flow, an application may receive a 400 Bad Request
with an invalid_grant
error response and the following message:
AADSTS50105:
Your administrator has configured the application
<cluster>:<namespace>:<application>
('<client id>
') to block users unless they are specifically granted ('assigned') access to the application.The signed in user '{EmailHidden}' is blocked because they are not a direct member of a group with access, nor had access directly assigned by an administrator.
Please contact your administrator to assign access to this application
Solution / Answer
- Ensure that the application has configured appropriate user access policies.
- Ensure that the given user is a direct member of any configured groups.
- If all else fails, request assistance in the
#nais
channel on Slack.
Tenant mismatch for signed-in user¶
While attempting to log in, you may receive the following error message from Entra ID:
Selected user account does not exist in tenant '
some-tenant
' and cannot access the application '<client-id>
' in that tenant.The account needs to be added as an external user in the tenant first.
Please use a different account.
Solution / Answer
- Ensure that the user uses an account that matches your application's tenant when logging in.
- If all else fails, request assistance in the
#nais
channel on Slack.
Manual Token Validation¶
While we recommend using the NAIS_TOKEN_INTROSPECTION_ENDPOINT
endpoint for validating tokens,
you can alternatively validate tokens natively within your application.
Manual validation can be useful if you want to avoid the small overhead of an additional network call and rather depend on a native library within your ecosystem of choice. You should be familiar with the auth concepts.
Validating a JWT involves a number of steps. These steps are outlined and described below in a language- and framework-agnostic way.
Libraries for token validation
We recommend using a library in your language of choice to handle all the validation steps described below. Here are some recommended libraries:
- navikt/oasis (JavaScript)
- navikt/token-support (Java / Kotlin)
Validation is also supported by many popular frameworks:
- Ktor (Kotlin)
- Spring Security (Java / Kotlin)
To validate the token, start by validating the signature and standard time-related claims.
Additionally, perform the following validations:
Issuer Validation
Validate that the iss
claim has a value that is equal to either:
- the
AZURE_OPENID_CONFIG_ISSUER
environment variable, or - the
issuer
property from the metadata discovery document. The document is found at the endpoint pointed to by theAZURE_APP_WELL_KNOWN_URL
environment variable.
Audience Validation
Validate that the aud
claim is equal to the AZURE_APP_CLIENT_ID
environment variable.
Signature Validation
Validate that the token is signed with a public key published at the JWKS endpoint. This endpoint URI can be found in one of two ways:
- the
AZURE_OPENID_CONFIG_JWKS_URI
environment variable, or - the
jwks_uri
property from the metadata discovery document. The document is found at the endpoint pointed to by theAZURE_APP_WELL_KNOWN_URL
environment variable.
Claims Validation
Other claims may be present in the token. Validation of these claims is optional.