AD FS OpenID Connect/OAuth flows and Application Scenarios
Applies to AD FS 2019 and later
Scenario | Scenario walkthrough using samples | OAuth 2.0 Flow/Grant | Client Type |
---|---|---|---|
Single-page app | Sample using MSAL | Implicit | Public |
Web App that signs in users | Sample using OWIN | 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 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 and the sections that follow describe each step in more detail.
Request ID Token and Access Token
To initially sign the user into your app, you can send an OpenID Connect authentication request and get 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 the AD FS assigned to your app. |
response_type | required | Must include id_token for OpenID Connect sign-in. It may 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, then resource parameter isn't sent. Instead the resource url is sent as a part of the scope parameter: scope = [resource url]//[scope values e.g., 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 policy, 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 wish. 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 is 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 whatsoever. 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 using the upn claim from id_token . |
domain_hint | optional | If included, it skips the domain-based discovery process that user goes through on the sign-in page, leading to a slightly more streamlined user experience. |
At this point, the user is asked to enter their credentials and complete the authentication. Once 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 using response_mode=fragment and response_type=id_token+token
looks like the following
// 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
will 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 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.
The OAuth 2.0 authorization code grant can be used in web apps to gain access to protected resources, such as 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 the 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 the AD FS for the client. |
resource | optional | The url of your Web API. Note – If using MSAL client library, then resource parameter isn't sent. Instead the resource url is sent as a part of the scope parameter: scope = [resource url]//[scope values e.g., 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 policy, 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 wish. 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 is 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 whatsoever. 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 using the upn claim from id_token . |
domain_hint | optional | If included, it skips the domain-based discovery process that user goes through on the sign-in page, 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 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. Once the user authenticates, the 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 using response_mode=query looks like:
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, typically they 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 the 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. It's 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 a key based authentication by signing a JWT and adding that as 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:
{
"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 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. |
Use the access token
GET /v1.0/me/messages
Host: https://webapi.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...
Refresh 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 token 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. As per the OAuth 2.0 spec 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 token when the new refresh token lifetime is longer than previous refresh token lifetime. To view additional information on AD FS refresh token lifetimes, visit 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 the 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, then resource parameter isn't sent. Instead the resource url is sent as a part of the scope parameter: scope = [resource url]//[scope values e.g., 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 policy, 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. It's 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 a key based authentication by signing a JWT and adding that as client_assertion parameter. |
Successful response
A successful token response looks like:
{
"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 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. |
Proof Key for Code Exchange (PKCE) support for OAuth
OAuth public clients using 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 now 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 a visual 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 then 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 auth providers for the same rule policy in Windows Server 2019
AD FS already supports triggering extra authentication based on a claim rule policy (RP). These policies You can set them for a particular RP or at global level. You can set an extra auth policy for a particular RP by opening PowerShell as an administrator and running the Set-AdfsRelyingPartyTrust cmdlet by 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 now use claims rules to decide which other authentication provider to invoke for extra authentication. This feature is useful for two scenarios:
Users are transitioning from one other 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 Azure AD MFA, and one group to use certificates as an extra authentication provider.
If you want to track how many people register for Azure AD MFA and certificate authentication, you would run a command like this with the values replaced with ones 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 Azure AD Multi-Factor Authentication 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 Certificate as an extra auth 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 auth methods providers, and a user can choose any of them. To allow multiple extra authentication providers, they should issue multiple claims 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 auth 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 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 the AD FS, on behalf of the user.
Protocol diagram
Assume that the user has been 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 for 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.
Note: While configuring OBO flow in AD FS, make sure scope
user_impersonation
is selected and client do requestuser_impersonation
scope in the request. - API A authenticates to the AD FS token issuance endpoint and requests a token to access API B. Note: While configuring this flow in AD FS, make sure API A is also registered as a server application with clientID having 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 following parameters.
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 registering 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, 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 while registering the first Web API as the server app (middle tier App). The resource ID should be the url of second Web API 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 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 registering 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, 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 JSON web token) 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 while registering the first Web API as the server app (middle tier App). The resource ID should be the url of second Web API 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 with 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 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. |
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, by setting 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, the 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:
First case: Access token request with a shared secret
POST /adfs/oauth2/token HTTP/1.1
//Line breaks for clarity
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 the 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 clarity
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 JSON web token) 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 the AD FS assigned to your app. It's part of client_assertion, so it isn't required to be passed in 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 and you should only use this flow when other, more secure, flows can't be used.
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 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. |
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 using the same flow described in the auth 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. Once the user signs in, the device is able to get access tokens and refresh tokens as needed.
Protocol diagram
The entire device code flow looks similar to the next diagram. We describe each of the steps 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 moment 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 the 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 the user should go to with the user_code in order to sign in. It's prefilled with user_code so that user doesn't need to enter user_code |
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. It can be localized by including a query parameter in the request of 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 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 | 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 looks like:
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 before 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 before 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.