Protect API's using OAuth 2.0 in APIM
Published May 02 2021 06:20 AM 36.4K Views
Microsoft

The API Management is a proxy to the backend APIs, it’s a good practice to implement security mechanism to provide an extra layer of security to avoid unauthorized access to APIs.

 

 

Configuring OAuth 2.0 Server in APIM merely enables the Developer Portal’s test console as APIM’s client to acquire a token from Azure Active Directory. In the real world, customer will have a different client app that will need to be configured in AAD to get a valid OAuth token that APIM can validate.

 

Prerequisites

To follow the steps in this article, you must have:

  • Azure subscription
  • Azure API Management
  • An Azure AD tenant

API Management supports other mechanisms for securing access to APIs, including the following examples:

  • Subscription keys
    End-users who need to consume the APIs must include a valid subscription key in HTTP requests when they make calls to those APIs.
  • Client Certificate
    The second option is to use Client Certificates. In API Management you can configure to send the client certificates while making the API calls and validate incoming certificate and check certificate properties against desired values using policy expressions.
  • Restrict caller Ips
    The third option is Restrict caller Ips - It (allows/denies) calls from specific IP addresses and/or address ranges which is applied in the <ip-filter>Policy.

  • Securing the Back End API using OAuth2.0
    Another option is using OAuth 2.0, Users/services will acquire an access token from an authorization server via different grant methods and send the token in the authorization header. In the inbound policy the token can be validated.  

Azure AD OAUTH2.0 authorization in APIM

OAUTH 2.0 is the open standard for access delegation which provides client a secure delegated access to the resources on behalf of the resource owner.

 

SherrySahni_0-1619682546932.png

 

 

Note: In the real world, you will have a different client app that will need to be configured in AAD to get a valid OAuth token that APIM can validate. 

The below diagram depicts different client applications like Web application/SPA, Mobile App and a server process that may need to obtain a token in Non-Interactive mode. So you must create a different App Registration for the respective client application and use them to obtain the token.

 

 

 

SherrySahni_45-1619684106433.png

 

In this Diagram we can see the OAUTH flow with API Management in which:

  1. The Developer Portal requests a token from Azure AD using app registration client id and client secret.
  2. In the second step, the user is challenged to prove their identity by supplying User Credentials.
  3. After successful validation, Azure AD issues the access/refresh token.
  4. User makes an API call with the authorization header and the token gets validated by using validate-jwt policy in APIM by Azure AD.
  5. Based on the validation result, the user will receive the response in the developer portal.

Different OAuth Grant Types​:

Grant Flow

 

Description

 

Use Case

 

Authorization Code

 

It is the most used grant type to authorize the Client to access protected data from a Resource Server.

 

Used by the secure client like a web server.

 

Implicit

 

It is intended for user-based clients who can’t keep a client secret because all the application code and storage is easily accessible.

 

Used by the client that can’t protect a client secret/token, such as a mobile app or single page application.

 

Client Credentials

 

This grant type is non interactive way for obtaining an access token outside of the context of a user.

 

It is suitable for machine-to-machine authentication where a specific user’s permission to access data is not required.

 

Resource Owner password Credentials

 

It uses the username and the password credentials of a Resource Owner (user) to authorize and access protected data from a Resource Server.

 

For logging in with a username and password (only for first-party apps)

 

High-level steps required to configure OAUTH

To configure Oauth2 with APIM the following needs to be created:

  • Register an application (backend-app) in Azure AD to represent the protected API resource.​
  • Register another application (client-app) in Azure AD which represent a client that wants to access the protected API resource.​
  • In Azure AD, grant permissions to client(client-app) to access the protected resource (backend-app).​
  • Configure the Developer Console to call the API using OAuth 2.0 user authorization.​
  • Add the validate-jwt policy to validate the OAuth token for every incoming request.​​

Register an application (backend-app) in Azure AD to represent the API.​

To protect an API with Azure AD, first register an application in Azure AD that represents the API. The following steps use the Azure portal to register the application.

 

Search for Azure Active Directory and select App registrations under Azure Portal to register an application:

  1. Select New registration.
    SherrySahni_1-1619682546940.png

     

  2. In the Name section, enter a meaningful application name that will be displayed to users of the app.
  3. In the Supported account types section, select an option that suits your scenario.
  4. Leave the Redirect URI section empty.
  5. Select Register to create the application.
    SherrySahni_2-1619682546959.png
  6. On the app Overview page, find the Application (client) ID value and record it for later.

  SherrySahni_3-1619682546969.png

 

  1. Select Expose an API and set the Application ID URI with the default value. Record this value for later.
  2. Select the Add a scope button to display the Add a scope page. Then create a new scope that's supported by the API (for example, Files.Read).
  3. Select the Add scope button to create the scope. Repeat this step to add all scopes supported by your API.
  4. When the scopes are created, make a note of them for use in a subsequent step.
    SherrySahni_4-1619682546978.png

     

Register another application (client-app) in Azure AD to represent a client application that needs to call the API.​

Every client application that calls the API needs to be registered as an application in Azure AD. In this example, the client application is the Developer Console in the API Management developer portal.

To register another application in Azure AD to represent the Developer Console:

  1. Follow the steps 1 – 6. mentioned in the previous section for registering backend app.
  1. Once the App registered, On the app Overview page, find the Application (client) ID value and record it for later.
  2. Create a client secret for this application to use in a subsequent step.
    1. From the list of pages for your client app, select Certificates & secrets, and select New client secret.
    2. Under Add a client secret, provide a Description. Choose when the key should expire and select Add. When the secret is created, note the key value for use in a subsequent step.

SherrySahni_5-1619682546986.png

 

Grant permissions in Azure AD

Now that you have registered two applications to represent the API and the Developer Console, grant permissions to allow the client-app to call the backend-app.

  1. In the Azure portal, search for and select App registrations.

  2. Choose your client app. Then in the list of pages for the app, select API permissions.

  3. Select Add a Permission.

  4. Under Select an API, select My APIs, and then find and select your backend-app.

  5. Select Delegated Permissions, then select the appropriate permissions to your backend-app.

  6. Select Add permissions.

Optionally:

  1. Navigate to your client app's API permissions page.

  2. Select Grant admin consent for <your-tenant-name> to grant consent on behalf of all users in this directory.

Authorization Code​:

 

In Authorization code grant type, User is challenged to prove their identity providing user credentials.
Upon successful authorization, the token end point is used to obtain an access token.

The obtained token is sent to the resource server and gets validated before sending the secured data to the client application.

SherrySahni_6-1619682546999.png

 

Enable OAuth 2.0 in the Developer Console for Authorization Code Grant type

At this point, we have created the applications in Azure AD, and granted proper permissions to allow the client-app to call the backend-app.

In this demo, the Developer Console is the client-app and has a walk through on how to enable OAuth 2.0 user authorization in the Developer Console.
Steps mentioned below:

  1. In Azure portal, browse to your API Management instance and Select OAuth 2.0 > Add.
  2. Provide a Display name and Description.
  3. For the Client registration page URL, enter a placeholder value, such as http://localhost.
  4. For Authorization grant types, select Authorization code.

    SherrySahni_7-1619682547010.png

     

  5. Specify the Authorization endpoint URL and Token endpoint URL. These values can be retrieved from the Endpoints page in your Azure AD tenant.

        Browse to the App registrations page again and select Endpoints.

SherrySahni_8-1619682547021.png

 

 

SherrySahni_9-1619682547028.png

 

 Important

  1. Use either v1 or v2 endpoints. However, depending on which version you choose, the below step will be different. We recommend using v2 endpoints.
  2. If you use v1 endpoints, add a body parameter named resource. For the value of this parameter, use Application ID of the back-end app.
  3. If you use v2 endpoints, use the scope you created for the backend-app in the Default scope field. Also, make sure to set the value for the accessTokenAcceptedVersion property to 2 in your application manifest in Azure AD Client APP and Backend app.
    SherrySahni_10-1619682547091.png

     

  4. Next, specify the client credentials. These are the credentials for the client-app.
  5. For Client ID, use the Application ID of the client-app.
    SherrySahni_11-1619682547097.png

     

  6. For Client secret, use the key you created for the client-app earlier.
  7. Immediately following the client secret is the redirect_urls
    SherrySahni_12-1619682547104.png

     

  8. Go back to your client-app registration in Azure Active Directory under Authentication.
  9. .paste the redirect_url under Redirect URI, and  check the issuer tokens then click on Configure button to save.
    SherrySahni_13-1619682547124.png

     

Now that you have configured an OAuth 2.0 authorization server, the Developer Console can obtain access tokens from Azure AD.

The next step is to enable OAuth 2.0 user authorization for your API. This enables the Developer Console to know that it needs to obtain an access token on behalf of the user, before making calls to your API.

  1. Go to APIs menu under the APIM
  2. Select the API you want to protect and Go to Settings.
  3. Under Security, choose OAuth 2.0, select the OAuth 2.0 server you configured earlier and select save.

 

SherrySahni_14-1619682547139.png

 

 

Calling the API from the Developer Portal:


Now that the OAuth 2.0 user authorization is enabled on your API, the Developer Console will obtain an access token on behalf of the user, before calling the API.

  1. Copy the developer portal url from the overview blade of apim
    SherrySahni_15-1619682547148.png

     

  2. Browse to any operation under the API in the developer portal and select Try it. This brings you to the Developer Console.
  3. Note a new item in the Authorization section, corresponding to the authorization server you just added.
    SherrySahni_16-1619682547161.png

     

  4. Select Authorization code from the authorization drop-down list, and you are prompted to sign in to the Azure AD tenant. If you are already signed in with the account, you might not be prompted.
    SherrySahni_17-1619682547169.png

     

  5. After successful sign-in, an Authorization header is added to the request, with an access token from Azure AD. The following is a sample token (Base64 encoded):
    SherrySahni_18-1619682547177.png

     

  6. Select Send to call the API successfully with 200 ok response.
    SherrySahni_19-1619682547184.png

     

Validate-jwt policy to pre-authorize requests with AD token:


Why JWT VALIDATE TOKEN?


At this point we can call the APIs with the obtained bearer token.

However, what if someone calls your API without a token or with an invalid token? For example, try to call the API without the Authorization header, the call will still go through.

This is because the API Management does not validate the access token, It simply passes the Authorization header to the back-end API.

To pre-Authorize requests, we can use <validate-jwt> Policy by validating the access tokens of each incoming request. If a request does not have a valid token, API Management blocks it.

We will now configure the Validate JWT policy to pre-authorize requests in API Management, by validating the access tokens of each incoming request. If a request does not have a valid token, API Management blocks it.

  1. Browser to the APIs from the left menu of APIM
  2. Click on “ALL APIS” and open the inbound policy to add the validate-jwt policy(It checks the audience claim in an access token and returns an error message if the token is not valid.) and save it.
    SherrySahni_20-1619682547199.png

     

  3. Go back to the developer portal and send the api with invalid token.
  4. You would observe the 401 unauthorized.
    SherrySahni_21-1619682547244.png

     

  5. Modify the token from authorization header to the valid token and send the api again to observe the 200-ok response.

Understanding <validate-jwt> Policy

SherrySahni_22-1619682547272.png

 

In this section, we will be focusing on understanding how <validate-jwt> policy works (the image in the right side is the decoded JWT Token)

  • The validate-jwt policy supports the validation of JWT tokens from the security viewpoint, It validates a JWT (JSON Web Token) passed via the HTTP Authorization header
    If the validation fails, a 401 code is returned.
  •  The policy requires an openid-config endpoint to be specified via an openid-config element. API Management expects to browse this endpoint when evaluating the policy as it has information which is used internally to validate the token.
    Please Note : OpenID config URL differs for the v1 and v2 endpoints.
  • The required-claims section contains a list of claims expected to be present on the token for it to be considered valid. The specified claim value in the policy must be present in the token for validation to succeed.

    The claim value should be the Application ID of the Registered Azure AD Backend-APP.

Reference Article : https://docs.microsoft.com/en-us/azure/api-management/api-management-access-restriction-policies#Val...


OAuth2 implicit grant flow:

SherrySahni_23-1619682547283.png

 

The following diagram shows what the entire implicit sign-in flow looks like.

As mentioned, Implicit grant type is more suitable for the single page applications. In this grant type, The user is requested to signin by providing the user credentials

Once the credentials are validated the token is returned directly from the authorization endpoint instead of the token endpoint.

The token are short lived, and a fresh token will be obtained through a hidden request as user is already signed in.

NOTE : To successfully request an ID token and/or an access token, the app registration in the Azure portal - App registrations page must have the corresponding implicit grant flow enabled, by selecting ID tokens and access tokens in the Implicit grant and hybrid flows section. 

Implicit Flow – DEMO

The configuration for the implicit grant flow is similar to the authorization code, we would just need to change the Authorization Grant Type to “Implict Flow” in the OAuth2.0 tab in APIM as shown below.

SherrySahni_24-1619682547292.png

 

After the OAuth 2.0 server configuration, The next step is to enable OAuth 2.0 user authorization for your API under APIs Blade :

SherrySahni_25-1619682547301.png

 


Now that the OAuth 2.0 user authorization is enabled on your API, we can test the API operation in the Developer Portal for the Authorization type : “Implict”.

Once after choosing the Authorization type as Implicit, you should be prompted to sign into the Azure AD tenant. After successful sign-in, an Authorization header is added to the request, with an access token from Azure AD and APIs should successfully return the 200-ok response:

SherrySahni_26-1619682547315.png

Client Credentials flow

SherrySahni_27-1619682547330.png

 

The entire client credentials flow looks like the following diagram.


In the client credentials flow, permissions are granted directly to the application itself by an administrator.

Token endpoint is used to obtain a token using client ID and Client secret, the resource server receives the server and validates it before sending to the client.

Client Credentials – Demo

  • In Client Credential flow, The OAuth2.0 configuration in APIM should have Authorization Grant Type as “Client Credentials”

SherrySahni_28-1619682547339.png

 

  • Specify the Authorization endpoint URL and Token endpoint URL with the tenant ID
    SherrySahni_29-1619682547355.png

     

  • The value passed for the scope parameter in this request should be (application ID URI) of the backend app, affixed with the .default suffix : ”API://<Backend-APP ID>/.default”

    SherrySahni_30-1619682547376.png

     



Now that you have configured an OAuth 2.0 authorization server, The next step is to enable OAuth 2.0 user authorization for your API.

SherrySahni_31-1619682547396.png

 

Now that the OAuth 2.0 user authorization is enabled on your API, we can test the API operation in the Developer Portal for the Authorization type : “Client Credentials”.

Once after choosing the Authorization type as Client Credentials in the Developer Portal,

  1.  The sign in would happen internally with client secret and client ID without the user credentials.
  2. After successful sign-in, an Authorization header is added to the request, with an access token from Azure AD.

SherrySahni_32-1619682547416.png

 

  1. Select Send to call the API successfully.

SherrySahni_33-1619682547459.png

 

Detailing about Client Credential Flow:
https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow

About .default scope : https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#the-defau...

 

Resource Owner Password Credentials

 

SherrySahni_34-1619682547469.png

 

 The Resource Owner Password Credential (ROPC) flow allows an application to sign in users by directly handling their password.

The ROPC flow is a single request: it sends the client identification and user's credentials to the Identity Provided, and then receives tokens in return.

The client must request the user's email address and password before doing so. Immediately after a successful request, the client should securely release the user's credentials from memory.

 

Resource Owner Password Credentials – Demo

The OAuth2.0 server configuration would be similar to the other grant types, we would need to select the Authorization grant types as  Resource Owner Password :

SherrySahni_35-1619682547479.png

 

SherrySahni_36-1619682547492.png

 

You can also specify the Ad User Credentials in the Resource owner password credentials section:

 

SherrySahni_37-1619682547501.png

 

Please note that it’s not a recommended flow as it requires a very high degree of trust in the application and carries risks which are not present in other grant types.

Now that you have configured an OAuth 2.0 authorization server, the next step is to enable OAuth 2.0 user authorization for your API.

SherrySahni_38-1619682547516.png

 

Now that the OAuth 2.0 user authorization is enabled on your API, we will be browsing to the  developer portal and maneuver to the API operation

  • Select Resource Owner Password from the authorization drop-down list
  • You will get a popup to pass the credentials with the option to “use test user” if you check this option it will be allowing the portal to sign in the user by directly handling their password added during the Oauth2.0 configuration and generate the token after clicking on Authorize button :
    SherrySahni_39-1619682547518.png

     

  • Another option is to uncheck the “test user” and Add the username and password to generate the token for different AD User and hit the authorize button
    SherrySahni_40-1619682547536.png

     

  • The access token would be added using the credentials supplied:
    SherrySahni_41-1619682547560.png

     

  • Select Send to call the API successfully.

SherrySahni_42-1619682547575.png

 

Please note that the validate jwt policy should be configured for preauthorizing the request for Resource owner password credential flow also.

 

Things to remember

 

Common issues when OAuth2.0 is integrated with API Management: 

 

  • Problem faced while obtaining a token with Client Credential Grant Type:

       Error Snapshot:

SherrySahni_43-1619682547599.png

 

Solution:

This error indicated that scope api://b29e6a33-9xxxxxxxxx/Files.Read is invalid.

As client_credentials flow requires application permission to work, but you may be passing the scope as Files.Read which is a delegated permission(user permission) and hence it rejected the scope.

To make it work, we would need to use default application scope as  “api://backendappID/.default”

  

  •  Receiving “401 Unauthorized” response

     Solution:

    You may be observing 401 unauthorized response returned by validate-jwt policy, its is recommended to look at the aud claims in the passed token and validate-jwt policy.


You can decode the token at  https://jwt.io/ and reverify it with the validate-jwt policy used in inbound section:
For example:

The Audience in the decoded token payload should match to the claim section of the validate-jwt policy:

SherrySahni_44-1619682547610.png

 


<claim name="aud">

      <value>api://b293-9f6b-4165-xxxxxxxxxxx</value>

</claim>

 

  •  Validate-JWT policy fails with IDX10511: Signature validation failed:

When we go to test the API and provide a JWT token in the Authorization header the policy may fail with the following error:  

  IDX10511: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.X509SecurityKey , KeyId: CtTuhMJmD5M7DLdzD2v2x3QKSRY

 

Solution :
If you look at the metadata for the config url (https://login.microsoftonline.com/common/.well-known/openid-configuration)you will find a jwks_uri property inside the resulting json.

This uri will point to a set of certificates used to sign and validate the jwt's.  You may find that the keyId (in this sample "CtTuhMJmD5M7DLdzD2v2x3QKSRY") does exist there. 

 

Something like this:

 

{

"keys": [{

"kty": "RSA",

"use": "sig",

"kid": "CtTuhMJmD5M7DLdzD2v2x3QKSRY",

"x5t": "CtTuhMJmD5M7DLdzD2v2x3QKSRY",

"n": "18uZ3P3IgOySln……",

"e": "AQAB",

"x5c": ["MII….."]

 

So it seems that it should be able to validate the signature.

 

If you look at the decoded jwt you may see something like this:

 

{

"typ": "JWT",

"alg": "RS256",

"x5t": "CtTuhMJmD5M7DLdzD2v2x3QKSRY",

"kid": "CtTuhMJmD5M7DLdzD2v2x3QKSRY"

}

.{

"aud": "00000003-0000-0000-c000-000000000000",

"iss": "https://sts.windows.net/<tenantID>/",

"appid": "1950a258-227b-4e31-a9cf-717495945fc2",

"nonce": "da3d8159-f9f6-4fa8-bbf8-9a2cd108a261",

 

 

There's a nonce in play here.  

This requires extra checking that validate-jwt does not do.  Getting a token for the Graph api and Sharepoint may emit a nonce property.  A token used to make calls to the Azure management api, however, will not have the nonce property.

 

The 'nonce' is a mechanism, that allows the receiver to determine if the token was forwarded. The signature is over the transformed nonce and requires special processing, so if you try and validate it directly, the signature validation will fail.

 

The validate jwt policy is not meant to validate tokens targeted for the Graph api or Sharepoint.  The best thing to do here is either remove the validate jwt policy and let the backend service validate it or use a token targeted for a different audience.

 

  • Validate-JWT policy fails with IDX10205: Issuer validation failed

Here is an example configuration a user might have added to their policy:

 

<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid.">

    <openid-config url="https://login.microsoftonline.com/72f988bf-86af-91ab-2d7cd011db47/.well-known/openid-configuration" />

    <required-claims>

       <claim name="Aud" match="any">

           <value>api://72f988bf-86af-91ab-2d7cd011db47</value>

       </claim>

    </required-claims>

</validate-jwt>

 

When a we go to test that API and provide a JWT token in the Authorization header the policy may fail with the following error:

 

IDX10205: Issuer validation failed. Issuer: 'https://login.microsoftonline.com/72f988bf-86af-91ab-2d7cd011db47/v2.0'. Did not match: validationParameters.ValidIssuer: '' or validationParameters.ValidIssuers: 'https://sts.windows.net/72f988bf-86af-91ab-2d7cd011db47/'.

  

Solution:

This error message gets thrown when the Issuer ("iss") claim in the JWT token does not match the trusted issuer in the policy configuration.

 

Azure Active Directory offers two versions of the token endpoint, to support two different implementations. AAD also exposes two different metadata documents to describe its endpoints.

The OpenID Config files contains details about the AAD tenant endpoints and links to its signing key that APIM will use to verify the signature of the token. Here are the details of those two endpoints and documents (for the MSFT AAD tenant):

                                            

Azure AD Token Endpoint V1: https://login.microsoftonline.com/<tenantID>/oauth2/token

Azure AD OpenID Config V1: https://login.microsoftonline.com/<tenantID>/.well-known/openid-configuration

 

Azure AD Token Endpoint V2: https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token

Azure AD OpenID Config V2: https://login.microsoftonline.com/<tenantID>/v2.0/.well-known/openid-configuration

 

Error Details:

 

The error usually occurs because the user is using a mix between V1 and V2. So they request a token from V1 endpoint but configured <openid-config> setting pointing to V2 endpoint,  or vice versa.

 

The Azure AD V1 endpoint uses an issuer value of https://sts.windows.net/{tenant-id-guid}/

The Azure AD V2 endpoint uses an issuer value of https://login.microsoftonline.com/{tenant-id-guid}/v2.0

 

 

To resolve this issue you just need to make sure the <validate-jwt> policy is loading up the matching openid-config file to match the token. The easiest way is to just toggle the open-id config url within the policy and then it will move beyond this part of the validation logic.

 

<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid.">

    <openid-config url="https://login.microsoftonline.com/{tenant-id-guid}/.well-known/openid-configuration" />

 

  1. Find the <openid-config> setting in their policy
  2. Just switch out the openid-config url between the two formats, replace {tenant-id-guid} with the Azure AD Tenant ID which you can collect from the Azure AD Overview tab within the Azure Portal

 

             https://login.microsoftonline.com/{tenant-id-guid}/.well-known/openid-configuration

             --- or ----

             https://login.microsoftonline.com/{tenant-id-guid}/v2.0/.well-known/openid-configuration

 

4 Comments
Microsoft

Great material!

Copper Contributor

Thanks for the excellent Article. I got stucked in one cases for last three week can you please guide- 

I need to call this APIM from logic app with Bearer token .Do i need to generate the token first? If yes can you please tell me the process to call APIM from Azure Logic APP with bearer token

Copper Contributor

What is the client application is a desktop app. I've created a dedicated app registration in AAD with platform set for "Mobile and desktop applications" but it is a bit unclear what should be put in redirect URIs? Currently, I have http://localhost.

 

The application registration representing client app is configured with delegated permissions so that the APIs can be called on behalf of the users.

 

 

 

Copper Contributor

Can this example be applied to OIDC?

Co-Authors
Version history
Last update:
‎Jan 04 2022 02:44 AM
Updated by: