Token lifetime policies for tokens issued by Azure Active Directory

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
0
Buy Me a Coffee at ko-fi.com
An error has occurred. This application may no longer respond until reloaded. Reload x