Edit

Share via


AD FS OpenID Connect/OAuth flows and application scenarios

Applies to Active Directory Federation Services (AD FS) 2019 and later

Scenario Scenario walkthrough with samples OAuth 2.0 flow/grant Client type
Single-page app Sample using MSAL Implicit Public
Web app that signs in users Authorization code Public, Confidential
Native app calls web API Sample using MSAL Authorization code Public
Web app calls web API Sample using MSAL Authorization code Confidential
PKCE implementation Authorization code Public
Web API calls another web API on behalf of (OBO) the user Sample using MSAL On-behalf-of Web app acts as Confidential
Daemon app calls web API Client credentials Confidential
Web app calls web API by using user credentials Resource owner password credentials Public, Confidential
Browserless app calls web API Device code Public, Confidential

Implicit grant flow

Note

Microsoft highly recommends migrating to Microsoft Entra ID instead of upgrading to a newer AD FS version. For more information on implicit grant flow in Microsoft Entra ID, see Implicit grant flow in Microsoft identity platform.

For single-page applications (AngularJS, Ember.js, React.js, and so on), AD FS supports the OAuth 2.0 implicit grant flow. The implicit flow is described in the OAuth 2.0 specification. Its primary benefit is that it allows the app to get tokens from AD FS without performing a backend server credential exchange. This flow allows the app to sign in the user, maintain session, and get tokens to other web APIs within the client JavaScript code. There are a few important security considerations to take into account when using the implicit flow, specifically around client.

If you want to use the implicit flow and AD FS to add authentication to your JavaScript app, follow the general steps in the following section.

Protocol diagram

The following diagram shows what the entire implicit sign-in flow looks like. The sections that follow describe each step in more detail.

Diagram that shows the implicit sign-in flow.

Request an ID token and access token

To initially sign the user in to your app, you can send an OpenID Connect authentication request and get an id_token and access token from the AD FS endpoint.

// Line breaks for legibility only

https://adfs.contoso.com/adfs/oauth2/authorize?
client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&response_type=id_token+token
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&scope=openid
&response_mode=fragment
&state=12345
Parameter Required/optional Description
client_id required The application (client) ID that AD FS assigned to your app.
response_type required Must include id_token for OpenID Connect sign-in. It can also include the response_type token. Using token here allows your app to receive an access token immediately from the authorize endpoint without having to make a second request to the token endpoint.
redirect_uri required The redirect_uri of your app, where authentication responses can be sent and received by your app. It must exactly match one of the redirect_uris you configured in AD FS.
nonce required A value included in the request, generated by the app that is to be included in the resulting id_token as a claim. The app can then verify this value to mitigate token replay attacks. The value is typically a randomized unique string that can be used to identify the origin of the request. Only required when an id_token is requested.
scope optional A space-separated list of scopes. For OpenID Connect, it must include the scope openid.
resource optional The URL of your web API.
Note – If using MSAL client library, the resource parameter isn't sent. Instead, the resource URL is sent as a part of the scope parameter: scope = [resource url]//[scope values, for example, openid]
If resource isn't passed here or in scope, AD FS uses a default resource urn:microsoft:userinfo. userinfo resource policies such as MFA, issuance, or authorization policies can't be customized.
response_mode optional Specifies the method that should be used to send the resulting token back to your app. Defaults to fragment.
state optional A value included in the request that is also to be returned in the token response. It can be a string of any content that you want. A randomly generated unique value is typically used for preventing cross-site request forgery attacks. The state is also used to encode information about the user's state in the app before the authentication request occurred, such as the page or view they were on.
prompt optional Indicates the type of user interaction that's required. The only valid values at this time are sign-in and none.
- prompt=login forces the user to enter their credentials on that request, negating single sign-on.
- prompt=none is the opposite - it ensures that the user isn't presented with any interactive prompt. If the request can't be completed silently via single sign-on, AD FS returns an interaction_required error.
login_hint optional Can be used to pre-fill the username/email address field of the sign-in page for the user, if you know their username ahead of time. Often apps use this parameter during reauthentication, having already extracted the username from a previous sign-in by using the upn claim from id_token.
domain_hint optional If this value is included, the ___domain-based discovery process that the user goes through on the sign-in page is skipped, leading to a slightly more streamlined user experience.

At this point, the user is asked to enter their credentials and complete the authentication. After the user authenticates, the AD FS authorization endpoint returns a response to your app at the indicated redirect_uri, using the method specified in the response_mode parameter.

Successful response

A successful response, when response_mode=fragment and response_type=id_token+token is used, looks like this:

// Line breaks for legibility only

GET https://localhost/myapp/#
access_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZEstZnl0aEV...
&token_type=Bearer
&expires_in=3599
&scope=openid
&id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZstZnl0aEV1Q...
&state=12345
Parameter Description
access_token Included if response_type includes token.
token_type Included if response_type includes token. Is always Bearer.
expires_in Included if response_type includes token. Indicates the number of seconds the token is valid, for caching purposes.
scope Indicates the scope(s) for which the access_token is valid.
id_token Included if response_type includes id_token. A signed JSON Web Token (JWT). The app can decode the segments of this token to request information about the user who signed in. The app can cache the values and display them, but it shouldn't rely on them for any authorization or security boundaries.
state If a state parameter is included in the request, the same value should appear in the response. The app should verify that the state values in the request and response are identical.

Refresh tokens

The implicit grant doesn't provide refresh tokens. Both id_tokens and access_tokens expire after a short period of time, so your app must be prepared to refresh these tokens periodically. To refresh either type of token, you can perform the same hidden iframe request as in the previous section, using the prompt=none parameter to control the identity platform's behavior. If you want to receive a new id_token, be sure to use response_type=id_token.

Authorization code grant flow

Note

Microsoft highly recommends migrating to Microsoft Entra ID instead of upgrading to a newer AD FS version. For more information on authorization code grant flow in Microsoft Entra ID, see Authorization code grant flow in Microsoft identity platform.

You can use the OAuth 2.0 authorization code grant in web apps to gain access to protected resources like web APIs. The OAuth 2.0 authorization code flow is described in section 4.1 of the OAuth 2.0 specification. It's used to perform authentication and authorization in most app types, including web apps and natively installed apps. The flow enables apps to securely acquire access_tokens that can be used to access resources that trust AD FS.

Protocol diagram

At a high level, the authentication flow for a native application looks a bit like this:

Diagram of the authorization code grant flow.

Request an authorization code

The authorization code flow begins with the client directing the user to the /authorize endpoint. In this request, the client indicates the permissions it needs to acquire from the user:

// Line breaks for legibility only

https://adfs.contoso.com/adfs/oauth2/authorize?
client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&response_type=code
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&response_mode=query
&resource=https://webapi.com/
&scope=openid
&state=12345
Parameter Required/optional Description
client_id required The application (client) ID that AD FS assigned to your app.
response_type required Must include code for the authorization code flow.
redirect_uri required The redirect_uri of your app, where authentication responses can be sent and received by your app. It must exactly match one of the redirect_uris you registered in AD FS for the client.
resource optional The URL of your web API.
Note – If using MSAL client library, the resource parameter isn't sent. Instead, the resource URL is sent as a part of the scope parameter: scope = [resource url]//[scope values, for example, openid]
If resource isn't passed here or in scope, AD FS uses a default resource urn:microsoft:userinfo. userinfo resource policies such as MFA, issuance, or authorization policies can't be customized.
scope optional A space-separated list of scopes.
response_mode optional Specifies the method that should be used to send the resulting token back to your app. Can be one of the following methods:
- query
- fragment
- form_post
query provides the code as a query string parameter on your redirect URI. If you're requesting the code, you can use query, fragment, or form_post. form_post executes a POST containing the code to your redirect URI.
state optional A value included in the request that is also to be returned in the token response. It can be a string of any content that you want. A randomly generated unique value is typically used for preventing cross-site request forgery attacks. The value can also encode information about the user's state in the app before the authentication request occurred, such as the page or view they were on.
prompt optional Indicates the type of user interaction that's required. The only valid values at this time are sign-in and none.
- prompt=login forces the user to enter their credentials on that request, negating single sign-on.
- prompt=none is the opposite - it ensures that the user isn't presented with any interactive prompt. If the request can't be completed silently via single sign-on, AD FS returns an interaction_required error.
login_hint optional Can be used to pre-fill the username/email address field of the sign-in page for the user, if you know their username ahead of time. Often apps use this parameter during reauthentication, having already extracted the username from a previous sign-in by using the upnclaim from id_token.
domain_hint optional If this value is included, the ___domain-based discovery process that user goes through on the sign-in page is skipped, leading to a slightly more streamlined user experience.
code_challenge_method optional The method used to encode the code_verifier for the code_challenge parameter. Can be one of the following values:
- plain
- S256
If this value is excluded, code_challenge is assumed to be plaintext if code_challenge is included. AD FS supports both plain and S256. For more information, see the PKCE RFC.
code_challenge optional Used to secure authorization code grants via Proof Key for Code Exchange (PKCE) from a native client. Required if code_challenge_method is included. For more information, see the PKCE RFC.

At this point, the user is asked to enter their credentials and complete the authentication. After the user authenticates, AD FS returns a response to your app at the indicated redirect_uri, using the method specified in the response_mode parameter.

Successful response

A successful response, when response_mode=query is used, looks like this:

GET https://adfs.contoso.com/common/oauth2/nativeclient?
code=AwABAAAAvPM1KaPlrEqdFSBzjqfTGBCmLdgfSTLEMPGYuNHSUYBrq...
&state=12345
Parameter Description
code The authorization_code that the app requested. The app can use the authorization code to request an access token for the target resource. authorization_codes are short lived. They typically expire after about 10 minutes.
state If a state parameter is included in the request, the same value should appear in the response. The app should verify that the state values in the request and response are identical.

Request an access token

Now that you've acquired an authorization_code and have been granted permission by the user, you can redeem the code for an access_token to the desired resource. Redeem the code by sending a POST request to the /token endpoint:

// Line breaks for legibility only

POST /adfs/oauth2/token HTTP/1.1
Host: https://adfs.contoso.com/
Content-Type: application/x-www-form-urlencoded

client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&code=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq3n8b2JRLk4OxVXr...
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&grant_type=authorization_code
&client_secret=JqQX2PNo9bpM0uEihUPzyrh    // NOTE: Only required for confidential clients (web apps)
Parameter Required/optional Description
client_id required The application (client) ID that AD FS assigned to your app.
grant_type required Must be authorization_code for the authorization code flow.
code required The authorization_code that you acquired in the first leg of the flow.
redirect_uri required The same redirect_uri value that was used to acquire the authorization_code.
client_secret required for web apps The application secret that you created during app registration in AD FS. You shouldn't use the application secret in a native app because client_secrets can't be reliably stored on devices. This parameter is required for web apps and web APIs, which have the ability to store the client_secret securely on the server side. The client secret must be URL-encoded before being sent. These apps can also use key-based authentication by signing a JWT and adding that as a client_assertion parameter.
code_verifier optional The same code_verifier that was used to obtain the authorization_code. Required if PKCE was used in the authorization code grant request. For more information, see the PKCE RFC. This option applies to AD FS 2019 and later.

Successful response

A successful token response looks like this:

{
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...",
    "token_type": "Bearer",
    "expires_in": 3599,
    "refresh_token": "AwABAAAAvPM1KaPlrEqdFSBzjqfTGAMxZGUTdM0t4B4...",
    "refresh_token_expires_in": 28800,
    "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctOD...",
}
Parameter Description
access_token The requested access token. The app can use this token to authenticate to the secured resource (web API).
token_type Indicates the token type value. The only type that AD FS supports is Bearer.
expires_in How long the access token is valid (in seconds).
refresh_token An OAuth 2.0 refresh token. The app can use this token to acquire more access tokens after the current access token expires. Refresh_tokens are long-lived and can be used to retain access to resources for extended periods of time.
refresh_token_expires_in How long the refresh token is valid (in seconds).
id_token A JWT. The app can decode the segments of this token to request information about the user who signed in. The app can cache the values and display them, but it shouldn't rely on them for any authorization or security boundaries.

Use the access token

GET /v1.0/me/messages
Host: https://webapi.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...

Refresh the token grant flow

Access_tokens are short lived, and you must refresh them after they expire to continue accessing resources. You can do so by submitting another POST request to the /token endpoint, this time providing the refresh_token instead of the code. Refresh tokens are valid for all permissions that your client has already received access tokens for.

Refresh tokens don't have specified lifetimes. Typically, the lifetimes of refresh tokens are relatively long. However, in some cases, refresh tokens expire, are revoked, or lack sufficient privileges for the desired action. Your application needs to expect and handle errors returned by the token issuance endpoint correctly.

Although refresh tokens aren't revoked when used to acquire new access tokens, you're expected to discard the old refresh token. The OAuth 2.0 specification says: "The authorization server MAY issue a new refresh token, in which case the client MUST discard the old refresh token and replace it with the new refresh token. The authorization server MAY revoke the old refresh token after issuing a new refresh token to the client." AD FS issues refresh tokens when the new refresh token lifetime is longer than previous refresh token lifetime. To view additional information on AD FS refresh token lifetimes, see AD FS single sign-on settings.

// Line breaks for legibility only

POST /adfs/oauth2/token HTTP/1.1
Host: https://adfs.contoso.com
Content-Type: application/x-www-form-urlencoded

client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&refresh_token=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq...
&grant_type=refresh_token
&client_secret=JqQX2PNo9bpM0uEihUPzyrh      // NOTE: Only required for confidential clients (web apps)
Parameter Required/optional Description
client_id required The application (client) ID that AD FS assigned to your app.
grant_type required Must be refresh_token for this leg of the authorization code flow.
resource optional The URL of your web API.
Note – If using MSAL client library, the resource parameter isn't sent. Instead, the resource URL is sent as a part of the scope parameter: scope = [resource url]//[scope values, for example, openid]
If resource isn't passed here or in scope, AD FS uses a default resource urn:microsoft:userinfo. userinfo resource policies such as MFA, issuance, or authorization policies can't be customized.
scope optional A space-separated list of scopes.
refresh_token required The refresh_token that you acquired in the second leg of the flow.
client_secret required for web apps The application secret that you created in the app registration portal for your app. It shouldn't be used in a native app, because client_secrets can't be reliably stored on devices. This parameter is required for web apps and web APIs, which have the ability to store the client_secret securely on the server side. These apps can also use key-based authentication by signing a JWT and adding that as a client_assertion parameter.

Successful response

A successful token response looks like this:

{
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...",
    "token_type": "Bearer",
    "expires_in": 3599,
    "refresh_token": "AwABAAAAvPM1KaPlrEqdFSBzjqfTGAMxZGUTdM0t4B4...",
    "refresh_token_expires_in": 28800,
    "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctOD...",
}
Parameter Description
access_token The requested access token. The app can use this token to authenticate to the secured resource, such as a web API.
token_type Indicates the token type value. The only type that AD FS supports is Bearer.
expires_in How long the access token is valid (in seconds).
scope The scopes that the access_token is valid for.
refresh_token An OAuth 2.0 refresh token. The app can use this token to acquire more access tokens after the current access token expires. Refresh_tokens are long-lived and can be used to retain access to resources for extended periods of time.
refresh_token_expires_in How long the refresh token is valid (in seconds).
id_token A JWT. The app can decode the segments of this token to request information about the user who signed in. The app can cache the values and display them, but it shouldn't rely on them for any authorization or security boundaries.

Proof Key for Code Exchange (PKCE) support for OAuth

OAuth public clients that use the authorization code grant are vulnerable to authorization code interception attacks, as described in RFC 7636. To mitigate these attacks, as of Windows Server 2019, AD FS supports Proof Key for Code Exchange (PKCE) for the OAuth authorization code grant flow.

The PKCE support specification adds more parameters to the OAuth 2.0 authorization and access token requests. The following diagram shows an outline of the PKCE process when a client contacts AD FS in Windows Server 2019.

Diagram of the PKCE relationship between the client and AD FS 2019.

In the section labeled A, the client creates and records a secret named code_verifier and derives a transformed version of the secret called t(code_verifier), also known as code_challenge. The client then sends the secret in the OAuth 2.0 authorization request along with the t_m transformation method.

In the section labeled B, the authorization endpoint responds as usual, but records the t(code_verifier) secret and the transformation method.

In the section labeled C, the client sends the authorization code in the access token request as usual, but includes the code_verifier secret generated in section A.

In the section labeled D, AD FS transforms the code_verifiersecret and compares it to the t(code_verifier) secret from section B. If their values aren't equal, AD FS denies access.

How to choose multiple authentication providers for the same rule policy in Windows Server 2019

AD FS already supports triggering extra authentication based on a claim rule policy (RP). You can set these policies for a particular RP or at a global level. You can set an extra authentication policy for a particular RP by opening PowerShell as an administrator and running the Set-AdfsRelyingPartyTrust cmdlet, passing either the AdditionalAuthenticationRules or AdditionalAuthenticationRulesFile parameter. To set it globally, an admin can use the Set-AdfsAdditionalAuthenticationRule cmdlet. Setting extra policies lets you use more than one authentication provider for the same application.

Claim rules provide the option to select the authentication provider for additional authentication, which proves beneficial in situations where customers are switching between providers or require a distinct provider for certain applications. As of Windows Server 2019, you can use claims rules to decide which other authentication provider to invoke for extra authentication. This feature is useful in two scenarios:

  • Users are transitioning from one authentication provider to another. As you onboard users to a newer authentication provider, they can use groups to control which extra authentication provider the service uses.

  • Users need a specific extra authentication provider for certain applications but also need to use a different method for other applications.

You can configure these settings by running the following command from other authentication policies:

Set-AdfsAdditionalAuthenticationRule -AdditionalAuthenticationRules 'c:[type == "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", value == "false"] => issue(type = "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", value = "http://schemas.microsoft.com/claims/multipleauthn" );' 

To set up this rule, you must issue the claim http://schemas.microsoft.com/claims/authnmethodsproviders from other authentication policies. The value of this claim should be the Name variable of the authentication provider.

You can also modify this rule configuration to help users transition from one authentication provider to another. For example, let's say that you want to modify one group that you manage to use Microsoft Entra multifactor authentication (MFA) and one group to use certificates as an extra authentication provider.

If you want to track how many people register for MFA and certificate authentication, you can run a command like this, with the values replaced with values that are relevant to your organization:

'c:[type == "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", Value == "false"] => issue(type = "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", Value = "http://schemas.microsoft.com/claims/multipleauthn" ); 

 c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid", Value == "S-1-5-21-608905689-872870963-3921916988-12345"] => issue(Type = "http://schemas.microsoft.com/claims/authnmethodsproviders", Value = "AzureMfaAuthentication"); 

not exists([Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid", Value=="S-1-5-21-608905689-872870963-3921916988-12345"]) => issue(Type = "http://schemas.microsoft.com/claims/authnmethodsproviders", Value = "CertificateAuthentication");’ 

Next, you can set the first application, called AppA, to use MFA as an extra authentication provider by running this command:

Set-AdfsRelyingPartyTrust -TargetName AppA -AdditionalAuthenticationRules 'c:[type == "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", Value == "false"] => issue(type = "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", Value = "http://schemas.microsoft.com/claims/multipleauthn" ); 

c:[] => issue(Type = "http://schemas.microsoft.com/claims/authnmethodsproviders", Value = "AzureMfaAuthentication");' 

Finally, you can set the second app, called AppB, to use certificates as an extra authentication provider by running this command:

Set-AdfsRelyingPartyTrust -TargetName AppB -AdditionalAuthenticationRules 'c:[type == "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", Value == "false"] => issue(type = "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", Value = "http://schemas.microsoft.com/claims/multipleauthn" ); 

c:[] => issue(Type = "http://schemas.microsoft.com/claims/authnmethodsproviders", Value = "CertificateAuthentication");' 

Admins can also make rules to allow more than one extra authentication provider. In this case, AD FS shows the issued authentication method providers, and a user can choose any of them. To allow multiple extra authentication providers, they should issue multiple claims by using the value http://schemas.microsoft.com/claims/authnmethodsproviders.

If the claim evaluation returns none of the authentication providers, AD FS rolls back and displays a list showing all the extra authentication providers configured by the admin on AD FS. The user must then manually select the appropriate authentication provider.

If your preferred authentication provider isn't on the list, you can run the following cmdlet to view all supported providers:

(Get-AdfsGlobalAuthenticationPolicy).AdditionalAuthenticationProvider

The value you use for the http://schemas.microsoft.com/claims/authnmethodsproviders claim should be one of the provider names returned by the list of providers that AD FS returned.

AD FS doesn't support triggering a particular extra authentication provider while the RP is using Access Control Policies in AD FS Windows Server 2016. When you move an application out of an access control policy, AD FS copies the corresponding policy from Access Control Policy to AdditionalAuthenticationRules and IssuanceAuthorizationRules. If an admin wants to use a specific authentication provider, they should stop using the access control policy and instead modify AdditionalAuthenticationRules.

On-Behalf-Of flow

Note

Microsoft highly recommends migrating to Microsoft Entra ID instead of upgrading to a newer AD FS version. For more information on On-Behalf-Of flow in Microsoft Entra ID, see On-Behalf-Of flow in Microsoft identity platform.

The OAuth 2.0 On-Behalf-Of flow (OBO) serves the use case where an application invokes a service/web API, which in turn needs to call another service/web API. The idea is to propagate the delegated user identity and permissions through the request chain. For the middle-tier service to make authenticated requests to the downstream service, it needs to secure an access token from AD FS, on behalf of the user.

Protocol diagram

Assume that a user is authenticated on an application using the OAuth 2.0 authorization code grant flow described in the previous section. At this point, the application has an access token for API A (token A) with the user's claims and consent to access the middle-tier web API (API A). Make sure the client requests user_impersonation scope in the token. Now, API A needs to make an authenticated request to the downstream web API (API B).

The steps that follow constitute the OBO flow and are explained with the help of the following diagram.

Diagram showing On-Behalf-Of flow.

  1. The client application makes a request to API A with token A. (When you configure OBO flow in AD FS, make sure scope user_impersonation is selected and that clients request user_impersonation scope in the request.)
  2. API A authenticates to the AD FS token issuance endpoint and requests a token to access API B. (When you configure this flow in AD FS, make sure API A is also registered as a server application with a client ID that has the same value as the resource ID in API A.)
  3. The AD FS token issuance endpoint validates API A's credentials with token A and issues the access token for API B (token B).
  4. Token B is set in the authorization header of the request to API B.
  5. Data from the secured resource is returned by API B.

Service-to-service access token request

To request an access token, make an HTTP POST to the AD FS token endpoint with the parameters described in the following sections.

First case: Access token request with a shared secret

For a shared secret, a service-to-service access token request contains the following parameters:

Parameter Required/optional Description
grant_type required The type of token request. For a request using a JWT, the value must be urn:ietf:params:oauth:grant-type:jwt-bearer.
client_id required The client ID that you configure when you register your first web API as a server app (middle-tier app). It should be the same as the resource ID used in the first leg, that is, the URL of the first web API.
client_secret required The application secret that you created during server app registration in AD FS.
assertion required The value of the token used in the request.
requested_token_use required Specifies how the request should be processed. In the OBO flow, the value must be set to on_behalf_of.
resource required The resource ID provided when you register the first web API as the server app (middle-tier App). The resource ID should be the URL of the second web API's middle-tier app calls on behalf of the client.
scope optional A space-separated list of scopes for the token request.
Example

The following HTTP POST requests an access token and a refresh token.

// Line breaks for legibility only

POST /adfs/oauth2/token HTTP/1.1
Host: adfs.contoso.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&client_id=https://webapi.com/
&client_secret=BYyVnAt56JpLwUcyo47XODd
&assertion=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIm…
&resource=https://secondwebapi.com/
&requested_token_use=on_behalf_of
&scope=openid

Second case: Access token request with a certificate

A service-to-service access token request with a certificate contains the following parameters:

Parameter Required/optional Description
grant_type required The type of token request. For a request using a JWT, the value must be urn:ietf:params:oauth:grant-type:jwt-bearer.
client_id required The client ID that you configure when you register your first web API as a server app (middle-tier app). It should be the same as the resource ID used in the first leg, that is, the URL of the first web API.
client_assertion_type required The value must be urn:ietf:params:oauth:client-assertion-type:jwt-bearer.
client_assertion required An assertion (a JWT) that you need to create and sign with the certificate you registered as credentials for your application.
assertion required The value of the token used in the request.
requested_token_use required Specifies how the request should be processed. In the OBO flow, the value must be set to on_behalf_of.
resource required The resource ID provided when you register the first web API as the server app (middle-tier App). The resource ID should be the URL of the second web API's middle-tier app calls on behalf of the client.
scope optional A space-separated list of scopes for the token request.

Notice that the parameters are almost the same. This example is similar to the request by shared secret except that the client_secret parameter is replaced by two parameters: client_assertion_type and client_assertion.

Example

The following HTTP POST requests an access token for the web API with a certificate.

// Line breaks for legibility only

POST /adfs/oauth2/token HTTP/1.1
Host: https://adfs.contoso.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&client_id= https://webapi.com/
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJSUzI1NiIsIng1dCI6Imd4OHRHeXN5amNS…
&resource=https://secondwebapi.com/
&requested_token_use=on_behalf_of
&scope= openid

Service-to-service access token response

A success response is a JSON OAuth 2.0 response that has the following parameters.

Parameter Description
token_type Indicates the token type value. The only type that AD FS supports is Bearer.
scope The scope of access granted in the token.
expires_in The length of time, in seconds, that the access token is valid.
access_token The requested access token. The calling service can use this token to authenticate to the receiving service.
id_token A JWT. The app can decode the segments of this token to request information about the user who signed in. The app can cache the values and display them, but it shouldn't rely on them for any authorization or security boundaries.
refresh_token The refresh token for the requested access token. The calling service can use this token to request another access token after the current access token expires.
Refresh_token_expires_in The length of time, in seconds, that the refresh token is valid.

Success response example

The following example shows a success response to a request for an access token for the web API.

{
  "token_type": "Bearer",
  "scope": openid,
  "expires_in": 3269,
  "access_token": "eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFCbmZpRy1t"
  "id_token": "aWRfdG9rZW49ZXlKMGVYQWlPaUpLVjFRaUxDSmhiR2NpT2lKU1V6STFOa"
  "refresh_token": "OAQABAAAAAABnfiG…"
  "refresh_token_expires_in": 28800,
}

Use the access token to access the secured resource

Now the middle-tier service can use the token acquired in the previous response example to make authenticated requests to the downstream web API. Set the token in the Authorization header.

Example

GET /v1.0/me HTTP/1.1
Host: https://secondwebapi.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFCbmZpRy1tQ…

Client credentials grant flow

Note

Microsoft highly recommends migrating to Microsoft Entra ID instead of upgrading to a newer AD FS version. For more information on client credentials grant flow in Microsoft Entra ID, see Client credentials grant flow in Microsoft identity platform.

You can use the OAuth 2.0 client credentials grant specified in RFC 6749 to access web-hosted resources by using the identity of an application. This type of grant is commonly used for server-to-server interactions that must run in the background, without immediate interaction with a user. These types of applications are often referred to as daemons or service accounts.

The OAuth 2.0 client credentials grant flow permits a web service (confidential client) to use its own credentials, instead of impersonating a user, to authenticate when calling another web service. In this scenario, the client is typically a middle-tier web service, a daemon service, or a web site. For a higher level of assurance, AD FS also allows the calling service to use a certificate (instead of a shared secret) as a credential.

Protocol diagram

The following diagram shows the client credentials grant flow.

Diagram of the client credentials flow.

Request a token

To get a token by using the client credentials grant, send a POST request to the /token AD FS endpoint, as shown in the following sections.

First case: Access token request with a shared secret

POST /adfs/oauth2/token HTTP/1.1
// Line breaks for legibility only

Host: https://adfs.contoso.com
Content-Type: application/x-www-form-urlencoded

client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&client_secret=qWgdYAmab0YSkuL1qKv5bPX
&grant_type=client_credentials
Parameter Required/optional Description
client_id required The application (client) ID that AD FS assigned to your app.
scope optional A space-separated list of scopes that you want the user to consent to.
client_secret required The client secret that you generated for your app in the app registration portal. The client secret must be URL-encoded before being sent.
grant_type required Must be set to client_credentials.

Second case: Access token request with a certificate

POST /adfs/oauth2/token HTTP/1.1

// Line breaks for legibility only

Host: https://adfs.contoso.com
Content-Type: application/x-www-form-urlencoded

&client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJSUzI1NiIsIng1dCI6Imd4OHRHeXN5amNScUtqRlBuZDdSRnd2d1pJMCJ9.eyJ{a lot of characters here}M8U3bSUKKJDEg
&grant_type=client_credentials
Parameter Required/optional Description
client_assertion_type required The value must be set to urn:ietf:params:oauth:client-assertion-type:jwt-bearer.
client_assertion required An assertion (a JWT) that you need to create and sign with the certificate you registered as credentials for your application.
grant_type required Must be set to client_credentials.
client_id optional The application (client) ID that AD FS assigned to your app. It's part of client_assertion, so it isn't required here.
scope optional A space-separated list of scopes that you want the user to consent to.

Use a token

Now that you've acquired a token, use the token to make requests to the resource. When the token expires, repeat the request to the /token endpoint to acquire a fresh access token.

GET /v1.0/me/messages
Host: https://webapi.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...

Note

Microsoft highly recommends migrating to Microsoft Entra ID instead of upgrading to a newer AD FS version. For more information on resource owner password credentials grant flow in Microsoft Entra ID, see Resource owner password credentials grant flow in Microsoft identity platform.

Resource owner password credential (ROPC) grant allows an application to sign in the user by directly handling their password. The ROPC flow requires a high degree of trust and user exposure. You should use this flow only when you can't use other flows that are more secure.

Protocol diagram

The following diagram shows the ROPC flow.

Diagram of the ROPC flow.

Authorization request

The ROPC flow is a single request—it sends the client identification and user's credentials to the IDP, and then receives tokens in return. The client must request the user's email address (UPN) and password before doing so. Immediately after a successful request, the client should securely release the user's credentials from memory. It must never save them.

// Line breaks and spaces are for legibility only

POST /adfs/oauth2/token HTTP/1.1
Host: https://adfs.contoso.com
Content-Type: application/x-www-form-urlencoded

client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&scope= openid
&username=myusername@contoso.com
&password=SuperS3cret
&grant_type=password
Parameter Required/optional Description
client_id required Client ID.
grant_type required Must be set to password.
username required The user's email address.
password required The user's password.
scope optional A space-separated list of scopes.

Successful authentication response

The following example shows a successful token response:

{
    "token_type": "Bearer",
    "scope": "openid",
    "expires_in": 3599,
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIn...",
    "refresh_token": "AwABAAAAvPM1KaPlrEqdFSBzjqfTGAMxZGUTdM0t4B4...",
    "refresh_token_expires_in": 28800,
    "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDR..."
}
Parameter Description
token_type Always set to Bearer.
scope If an access token was returned, this parameter lists the scopes the access token is valid for.
expires_in Number of seconds that the included access token is valid for.
access_token Issued for the scopes that were requested.
id_token A JWT. The app can decode the segments of this token to request information about the user who signed in. The app can cache the values and display them, but it shouldn't rely on them for any authorization or security boundaries.
refresh_token_expires_in Number of seconds that the included refresh token is valid for.
refresh_token Issued if the original scope parameter included offline_access.

You can use the refresh token to acquire new access tokens and refresh tokens by using the same flow described in the authentication code grant flow section of this article.

Device code flow

Note

Microsoft highly recommends migrating to Microsoft Entra ID instead of upgrading to a newer AD FS version. For more information on device code flow in Microsoft Entra ID, see Device code flow in Microsoft identity platform.

Device code grant allows users to sign in to input-constrained devices such as a smart TV, IoT device, or printer. To enable this flow, the device has the user visit a webpage in their browser on another device to sign in. After the user signs in, the device is able to get access tokens and refresh tokens as needed.

Protocol diagram

The entire device code flow is similar the flow shown in the next diagram. Each of the steps is described later in this article.

Diagram that shows the device code flow.

Device authorization request

The client must first check with the authentication server for a device and user code that's used to initiate authentication. The client collects this request from the /devicecode endpoint. In this request, the client should also include the permissions it needs to acquire from the user. From the time this request is sent, the user has only 15 minutes to sign in (the usual value for expires_in), so only make this request when the user has indicated they're ready to sign in.

// Line breaks are for legibility only

POST https://adfs.contoso.com/adfs/oauth2/devicecode
Content-Type: application/x-www-form-urlencoded

client_id=00001111-aaaa-2222-bbbb-3333cccc4444
scope=openid
Parameter Condition Description
client_id required The application (client) ID that AD FS assigned to your app.
scope optional A space-separated list of scopes.

Device authorization response

A successful response is a JSON object containing the required information to allow the user to sign in.

Parameter Description
device_code A long string used to verify the session between the client and the authorization server. The client uses this parameter to request the access token from the authorization server.
user_code A short string shown to the user that's used to identify the session on a secondary device.
verification_uri The URI the user should go to with the user_code in order to sign in.
verification_uri_complete The URI that the user should go to with the user_code in order to sign in. It's prefilled with user_code so that the user doesn't need to enter that value.
expires_in The number of seconds before the device_code and user_code expire.
interval The number of seconds the client should wait between polling requests.
message A human-readable string with instructions for the user. You can localize it by including a query parameter in the request in the form ?mkt=xx-XX, filling in the appropriate language culture code.

Authenticating the user

After the client receives the user_code and verification_uri, it displays these details to the user, instructing them to sign in by using their mobile phone or PC browser. Additionally, the client can use a QR code or similar mechanism to display the verfication_uri_complete, which takes the step of entering the user_code for the user. While the user is authenticating at the verification_uri, the client should be polling the /token endpoint for the requested token using the device_code.

POST https://adfs.contoso.com /adfs/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type: urn:ietf:params:oauth:grant-type:device_code
client_id: 00001111-aaaa-2222-bbbb-3333cccc4444
device_code: GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8
Parameter Required/optional Description
grant_type required Must be urn:ietf:params:oauth:grant-type:device_code.
client_id required Must match the client_id used in the initial request.
code required The device_code returned in the device authorization request.

Successful authentication response

A successful token response can include these parameters:

Parameter Description
token_type Always Bearer.
scope If an access token was returned, it lists the scopes the access token is valid for.
expires_in Number of seconds the included access token is valid for.
access_token Issued for the scopes that were requested.
id_token Issued if the original scope parameter included the openid scope.
refresh_token Issued if the original scope parameter included offline_access.
refresh_token_expires_in Number of seconds the included refresh token is valid for.

See AD FS Development for the complete list of walk-through articles, which provide step-by-step instructions on using the related flows.