Token lifetime policy
A token lifetime policy is a type of policy object that contains token lifetime rules. This policy controls how long a JWT access token, an ID token or a SAML 1.1/2.0 token issued by Azure Active Directory are considered valid.
Microsoft doesn't allow to set token lifetime policies for refresh and session tokens.
API resource
What is important to understand, the token lifetime policy is not based on the AAD application used to request the resource, but on the API resource your application is trying to access.
For example, when an application registered in Azure AD has API permissions defined for the Graph API, then the token lifetime policy must be assigned to the service principal related to the Graph API.
Rules for applying a token lifetime policy
You can set token lifetimes for all apps in your organization or for a multi-tenant (multi-organization) application and for service prinicipal except managed identity service principals.
Multiple policies might apply to a specific application. The token lifetime policy that takes effect follows these rules:
- If a policy is explicitly assigned to the organization, it's enforced
- If no policy is explicitly assigned to the organization, the policy assigned to the application is enforced
- If no policy has been assigned to the organization or the application object, the default values are enforced
The default lifetime value for tokens is 1 hour. Minimal lifetime to be set is 10 minutes, maximum lifetime is 24 hours.
Graph API
The Graph API defines a token lifetime policy by tokenLifetimePolicy resource type.
The tokenLifetimePolicy
resource has properties id
, displayName
, isOrganizationDefault
and definition
.
The isOrganizationDefault
property defines whether the policy is the default for the organization. Only one policy can have isOrganizationDefault
set to true
.
The definition
property contains a lifetime for the access tokens. It's a string collection containing a JSON string:
{
"TokeLifeTimePolicy" : {
"Version" : 1,
"AccessTokenLifetime" : "hh:mm:ss"
}
}
Range for AccessTokenLifetime is 00:10:00 - 23:59:59.
Time to show how it all works. For this purpose, I've registered custom applications in Azure AD.
App name | API permissions | Type of permission | Expose API | Certificates&secrets |
---|---|---|---|---|
PolicyTestApp1 | Application.Read.All | Application | - | secret |
HiringApp | - | - | Hiring.Read.All | secret |
PolicyTestApp2 | Hiring.Read.All | Application | - | secret |
The PolicyTestApp1 app is accessing API exposed by the Graph API service principal. The PolicyTestApp2 app is accessing API exposed by the custom application HiringApp.
To check the expiration of the access token for PolicyTestApp1 and PolicyTestApp2, I'm using the curl
command:
- Linux
curl -L -X POST https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'client_id={client_id}' \
-d 'scope={app_id_uri}/.default' \
-d 'client_secret={secret}' \
-d 'grant_type=client_credentials'
- Windows
curl -L -X POST https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token^
-H "Content-Type: application/x-www-form-urlencoded"^
-d "client_id={client_id}"^
-d "scope={app_id_uri}/.default"^
-d "client_secret={secret}"^
-d "grant_type=client_credentials"
The scope:
- for the PolicyTestApp1 is
https://graph.microsoft.com/.default
- for the PolicyTestApp2 is
api://{HiringApp_clientId}
because the app is using API permissions exposes by the HiringApp application.
The response from the token request for the PolicyTestApp1:
{
"token_type":"Bearer",
"expires_in":3599,
"ext_expires_in":3599,
"access_token":"..."
}
The response from the token request for the PolicyTestApp2:
{
"token_type":"Bearer",
"expires_in":3599,
"ext_expires_in":3599,
"access_token":"..."
}
The default Azure AD token lifetime policy was applied and both tokens have expiration 1 hour.
Create a policy
In the next step, I've created two token lifetime policies with the lifetime 30 minutes and 12 hours.
Request for the policy with the lifetime 30 minutes:
POST https://graph.microsoft.com/v1.0/policies/tokenLifetimePolicies
{
"definition": [
"{\"TokenLifetimePolicy\":{\"Version\":1,\"AccessTokenLifetime\":\"00:30:00\"}}"
],
"displayName": "30minutes policy",
"isOrganizationDefault": false
}
Response:
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#policies/tokenLifetimePolicies/$entity",
"id": "d2f869bd-4edd-4d33-b3c2-408551e100d1",
"deletedDateTime": null,
"definition": [
"{\"TokenLifetimePolicy\":{\"Version\":1,\"AccessTokenLifetime\":\"00:30:00\"}}"
],
"displayName": "30minutes policy",
"isOrganizationDefault": false
}
Request for the policy with the lifetime 12 hours:
POST https://graph.microsoft.com/v1.0/policies/tokenLifetimePolicies
{
"definition": [
"{\"TokenLifetimePolicy\":{\"Version\":1,\"AccessTokenLifetime\":\"12:00:00\"}}"
],
"displayName": "12hours policy",
"isOrganizationDefault": false
}
Response:
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#policies/tokenLifetimePolicies/$entity",
"id": "b41dd18a-20d9-44bc-87f2-5fdbaa3c92ee",
"deletedDateTime": null,
"definition": [
"{\"TokenLifetimePolicy\":{\"Version\":1,\"AccessTokenLifetime\":\"12:00:00\"}}"
],
"displayName": "12hours policy",
"isOrganizationDefault": false
}
No policy is marked as the default one for the organization. All generated access tokens have still the default lifetime 1 hour.
Assign policies to application
Assign the 30minutes policy to the service principal related to the Graph API
POST servicePrincipals/078fffe3-f47d-42e1-883d-086c55351da3/tokenLifetimePolicies/$ref
{
"@odata.id":"https://graph.microsoft.com/v1.0/policies/tokenLifetimePolicies/d2f869bd-4edd-4d33-b3c2-408551e100d1"
}
Assign the 12hours policy to the HiringApp app
POST https://graph.microsoft.com/v1.0/applications/0a601dba-ad15-4461-9034-5ba5700932e3/tokenLifetimePolicies/$ref
{
"@odata.id":"https://graph.microsoft.com/v1.0/policies/tokenLifetimePolicies/b41dd18a-20d9-44bc-87f2-5fdbaa3c92ee"
}
Checking the lifetime of the acess token for the PolicyTestApp1 app:
{
"token_type":"Bearer",
"expires_in":1799,
"ext_expires_in":1799,
"access_token":"..."
}
and for the PolicyTestApp2 app:
{
"token_type":"Bearer",
"expires_in":43199,
"ext_expires_in":43199,
"access_token":"..."
}
Default organization policy
Now set a new default token lifetime policy for the organization. Remove the assignment of the 30minutes policy from the Graph API.
DELETE https://graph.microsoft.com/v1.0/servicePrincipals/078fffe3-f47d-42e1-883d-086c55351da3/tokenLifetimePolicies/d2f869bd-4edd-4d33-b3c2-408551e100d1/$ref
Checking the lifetime of the acess token for the PolicyTestApp1 app. Default lifetime 1 hour applied.
{
"token_type":"Bearer",
"expires_in":3599,
"ext_expires_in":3599,
"access_token":"..."
}
Update the 30minutes policy and set it as the default policy for the organization
PATCH https://graph.microsoft.com/v1.0/policies/tokenLifetimePolicies/d2f869bd-4edd-4d33-b3c2-408551e100d1
{
"displayName": "Default policy",
"isOrganizationDefault": true
}
Now the all applications have access token with the same lifetime 30 minutes.
The lifetime of the access token for the PolicyTestApp1 app:
{
"token_type":"Bearer",
"expires_in":1799,
"ext_expires_in":1799,
"access_token":"..."
}
and for the PolicyTestApp2.
{
"token_type":"Bearer",
"expires_in":1799,
"ext_expires_in":1799,
"access_token":"..."
}
The organization default policy is enforced for all apps.
Remove policy
Removing a policy also removes the assignment from the application or service principal.
DELETE https://graph.microsoft.com/v1.0/policies/tokenLifetimePolicies/d2f869bd-4edd-4d33-b3c2-408551e100d1