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.
To follow the steps in this article, you must have:
API Management supports other mechanisms for securing access to APIs, including the following examples:
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.
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.
In this Diagram we can see the OAUTH flow with API Management in which:
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) |
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:
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:
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.
In the Azure portal, search for and select App registrations.
Choose your client app. Then in the list of pages for the app, select API permissions.
Select Add a Permission.
Under Select an API, select My APIs, and then find and select your backend-app.
Select Delegated Permissions, then select the appropriate permissions to your backend-app.
Select Add permissions.
Optionally:
Navigate to your client app's API permissions page.
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.
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:
Browse to the App registrations page again and select Endpoints.
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.
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.
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.
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 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...
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.
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.
After the OAuth 2.0 server configuration, The next step is to enable OAuth 2.0 user authorization for your API under APIs Blade :
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:
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.
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.
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,
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...
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.
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 :
You can also specify the Ad User Credentials in the Resource owner password credentials section:
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.
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
Please note that the validate jwt policy should be configured for preauthorizing the request for Resource owner password credential flow also.
Error Snapshot:
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”
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:
<claim name="aud">
<value>api://b293-9f6b-4165-xxxxxxxxxxx</value>
</claim>
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.
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" />
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
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.