Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
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.
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:
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 upn claim 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_code s 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.
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_verifier
secret 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.
- 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 requestuser_impersonation
scope in the request.) - 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.)
- The AD FS token issuance endpoint validates API A's credentials with token A and issues the access token for API B (token B).
- Token B is set in the authorization header of the request to API B.
- 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.
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...
Resource owner password credentials grant flow (not recommended)
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.
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.
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. |
Related content
See AD FS Development for the complete list of walk-through articles, which provide step-by-step instructions on using the related flows.