Azure API Management with OAuth 2.0

 Azure API Management with OAuth 2.0

Enabling OAuth2 in Azure API Management (APIM) ensures that all external consumers authenticate using a secure, industry-standard token-based model, regardless of how the backend systems (e.g., website, mobile apps, SaaS APIs) handle authentication. This creates a centralized security layer that abstracts backend differences, enforces consistent access control, supports fine-grained authorization (via scopes/claims), and provides detailed auditing and monitoring for compliance. In short, it improves security, governance, and partner onboarding while simplifying backend integration.

In an OAuth2-enabled Azure API Management (APIM) setup, a client app first requests an access token from an Identity Provider (such as Azure AD), which validates the credentials and issues an access token (and possibly a refresh token). The client then sends its API request to APIM, including the token in the authorization header. APIM validates the token by checking its signature, claims, issuer, audience, and expiry against the Identity Provider. If the token is expired, APIM can use the refresh token to request a new one. Once the token is validated, APIM enforces policies such as throttling, logging, or data transformation before forwarding the request to the backend API. Depending on the backend’ s requirements, APIM can pass the client’s token directly or perform a token exchange to provide a different credential format. The backend API then processes the request and returns the response to APIM, which in turn delivers it back to the client. This flow ensures secure, controlled, and policy-driven access to backend services while centralizing authentication and authorization in APIM.

Create APIM Instance:

For this Demo I’m using APIM developer version which support Azure AD Integration. This is a prerequisite to enable Azure APIM with oAuth2.0

Once your APIM has been created, Add API >> click Add API and select HTTP (manual define an HTTP API) like below settings:

Create TWO API endpoints:

-        PO Confirmation: Accessible to Vendor A

-        Payment confirmation: Accessible to Vendor B

 

Now you can add operation, this will create final API endpoint to test. Click Add operation >>

Click save to create endpoint. Now its time to configure inbound policy.

Which should look like:

    <inbound>

        <base />

            <return-response>

        <set-status code="200" reason="OK" />

        <set-header name="Content-Type" exists-action="override">

            <value>application/json</value>

        </set-header>

        <set-body>

            {

              "po": "12345",

              "name": "Sangita Shaw",

              "status": "active"

            }

        </set-body>

    </return-response>

 

Now test your API from APIM Portal itself. Click on the test tab, select the product and send it. The request and response should look like this:

 

 

Let’s create Payment confirmation API endpoint. Same like previous steps, app operation and name it “Mock Payment”

After adding the API, click on plus sign of Add policy and select “mock response” with default values.

The inbound policy should look like:

    <inbound>

        <base />

        <mock-response status-code="200" content-type="application/json" />

    </inbound>

 

Now Once again, sent Frontend edit but and click on response tab. Add response.

And Save. Now it’s time to test.

 

So far so good, both endpoint works fine. You can test them from postman as well only one extra setting to provide i.e. subscription key in the postman. Like:

 

 

The sequent steps are actually involved to enable your API endpoints with oAuth2.0.

 

 

App Registration

We are going to add 2 separate applications pointing to each Api endpoints and one more app for accessing backend process.

App1 Name: APIMResourcePO

App2 Name: APIMResourcePay

App3 Name: APIMVendorAccess

Except the names all other steps are identical for App1 & 2.

Step 1: Create an app with name “APIMResourcePO” with all default values.

Step 2: Select menu item “Expose an API” from the left panel and then click “Application ID UR”. Press save with default value.

Note the “Application ID URI” for app APIMResourcePO in configuration table below. We need this later.

Step 3: Select menu item “App roles” from the left panel and then click “Create app role” from top left. Press save with display name “readerPO” and value “ReaderPO”.

Step 4: Select menu item “Manifest” from the left panel and then Open 2nd tab “AAD Graph App Manifest (Deprecating Soon)” and Change the “accessTokenAcceptedVersion” value null to 2. Save it.

Repeat the same steps for app registration of “APIMResourcePay”.

Let’s create 3rd app with name “APIMVendorAccess” and configure it.

Step 1: Create app with default values. Once its create get the client ID, tenant ID from overview menu option.

 

Step 2: Select menu item “Certificates & secrets” from the left panel and then click “New client secrets” Plus button. Caution save the secret it won’t visitable again.

Step 3: Assign permission to this app

Select the ‘API permissions’ menu option from left panel and Add a permission. This step is a bit tricky since you should have the privilege to grant consents; otherwise, approach to your Azure Admin to “Grant Admin consent”, after you configure this step.

Action1: Add permission to the role we defined for other 2 apps inthe  previous steps (2 app we registered in previous steps..)

Action2: Add permission to Graph API for read & readAll (real all might be missing).

Action3: Grant Admin consent to the permissions. If the button is disabled, please check with your Azure Admins.

Once consent provided status would be green.

 

App registration Configuration

Source

APIMResourcePO (App Scope) ID URI:

api://8f0c359e-553d-470d-a8da-30b3db8953b7

App 1

APIMResourcePay (App Scope) ID URI:

api://9f99dfe2-dd41-45bf-8e3b-8f4efb85f6ad

App 2

Client ID

ab2d512d-2c18-4627-abd4-12a5dff8b5ac

App 3

Tenant ID

1daa2d13-d914-45ab-bd4f-1d5b11e7788d

App 3

Secret

WFy8Q~GRY5i~DN4ZIV1K.sA4CyJmQpYjwxXYeaaD

App 3

 

Step 4: Select menu item “Manifest” from the left panel and then Open 2nd tab “Microsoft Graph App Manifest (New)” and Change the “requestedAccessTokenVersion” value null to 2. Save it.

APIM Policy Configuration

This is the final step to do, open again your APIM and select the endpoint you want to enable with oAuth2.

In this case Select Mock Payment Get operation and App Policy (+ button). Select “Validate JWT” inbound policy.

And do the following configuration.

Key

Value

Header name

Authorization

Failed validation HTTP code

401 Unauthorized

Failed validation error message

Access Token invalid

Required claims -1

 

Name

aud

Match

All Claims

Values

9f99dfe2-dd41-45bf-8e3b-8f4efb85f6ad (i.e. Scope of APIMResourcePay)

Required claims -2

 

Name

iss

Match

All Claims

Values

https://login.microsoftonline.com/{{azTenantID}}/v2.0

https://login.microsoftonline.com/1daa2d13-d914-45ab-bd4f-1d5b11e7788d/v2.0

Open ID URLs 

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

https://login.microsoftonline.com/1daa2d13-d914-45ab-bd4f-1d5b11e7788d /v2.0/.well-known/openid-configuration

 

 

You need to put “validate jwt” policy before mock-response policy.

And finally, your policy should look like:

<policies>

    <!-- Throttle, authorize, validate, cache, or transform the requests -->

    <inbound>

        <base />

        <validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="DK::Access Token is invalid">

            <openid-config url="https://login.microsoftonline.com/1daa2d13-d914-45ab-bd4f-1d5b11e7788d /v2.0/.well-known/openid-configuration" />

            <required-claims>

                <claim name="aud" match="all">

                    <value>9f99dfe2-dd41-45bf-8e3b-8f4efb85f6ad</value>

                </claim>

                <claim name="iss" match="all">

                    <value>https://login.microsoftonline.com/1daa2d13-d914-45ab-bd4f-1d5b11e7788d/v2.0</value>

                </claim>

            </required-claims>

        </validate-jwt>

        <mock-response status-code="200" content-type="application/json" />

    </inbound>

    <!-- Control if and how the requests are forwarded to services  -->

    <backend>

        <base />

    </backend>

    <!-- Customize the responses -->

    <outbound>

        <base />

    </outbound>

    <!-- Handle exceptions and customize error responses  -->

    <on-error>

        <base />

    </on-error>

</policies>

 

Note: you can hide the values such as Tenant ID, App Scope and so on in the Named values and access them as variable in the policy. This approach would reduce the chance of typo errors.

And then modify the inbound policy with assigning the variables.

<policies>

    <!-- Throttle, authorize, validate, cache, or transform the requests -->

    <inbound>

        <base />

        <validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="DK::Access Token is invalid">

            <openid-config url="https://login.microsoftonline.com/{{azTenantID}}/v2.0/.well-known/openid-configuration" />

            <required-claims>

                <claim name="aud" match="all">

                    <value>{{appScopePay}}</value>

                </claim>

                <claim name="iss" match="all">

                    <value>https://login.microsoftonline.com/{{azTenantID}}/v2.0</value>

                </claim>

            </required-claims>

        </validate-jwt>

        <mock-response status-code="200" content-type="application/json" />

    </inbound>

    <!-- Control if and how the requests are forwarded to services  -->

    <backend>

        <base />

    </backend>

    <!-- Customize the responses -->

    <outbound>

        <base />

    </outbound>

    <!-- Handle exceptions and customize error responses  -->

    <on-error>

        <base />

    </on-error>

</policies>

 

Next Configure Mock PO API endpoint.

The Validate JWT token policty would be the identical except the scope should be assign to “APIMResourcePO

And the policy should look like:

<policies>

    <!-- Throttle, authorize, validate, cache, or transform the requests -->

    <inbound>

        <base />

        <validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="DK::Access Token is invalid">

            <openid-config url="https://login.microsoftonline.com/{{azTenantID}}/v2.0/.well-known/openid-configuration" />

            <required-claims>

                <claim name="aud" match="all">

                    <value>{{appScopePO}}</value>

                </claim>

                <claim name="iss" match="all">

                    <value>https://login.microsoftonline.com/{{azTenantID}}/v2.0</value>

                </claim>

            </required-claims>

        </validate-jwt>

        <return-response>

            <set-status code="200" reason="OK" />

            <set-header name="Content-Type" exists-action="override">

                <value>application/json</value>

            </set-header>

            <set-body>{

              "po": "12345",

              "name": "Sangita Shaw",

              "status": "active"

            }</set-body>

        </return-response>

    </inbound>

    <!-- Control if and how the requests are forwarded to services  -->

    <backend>

        <base />

    </backend>

    <!-- Customize the responses -->

    <outbound>

        <base />

    </outbound>

    <!-- Handle exceptions and customize error responses  -->

    <on-error>

        <base />

    </on-error>

</policies>

 

Test though Postman

Test Step1: Get the token for Payment

To get a valid token using this curl:

curl --location 'https://login.microsoftonline.com/1daa2d13-d914-45ab-bd4f-1d5b11e7788d/oauth2/v2.0/token' \

--header 'Content-Type: application/x-www-form-urlencoded' \

--data-urlencode 'grant_type=client_credentials' \

--data-urlencode 'client_id=ab2d512d-2c18-4627-abd4-12a5dff8b5ac' \

--data-urlencode 'client_secret=WFy8Q~GRY5i~DN4ZIV1K.sA4CyJmQpYjwxXYeaaD' \

--data-urlencode 'scope=api://9f99dfe2-dd41-45bf-8e3b-8f4efb85f6ad/.default'

 

Use this token to invoke Payment api endpoint and you would get success response

Use this same token to invoke PO api endpoint and you won’t get successful response

 

Test Step2: Get the token for PO

In order to get valid token for PO (purchase order) change the scope while creating the jwt token. And the curl should look like:

curl --location 'https://login.microsoftonline.com/1daa2d13-d914-45ab-bd4f-1d5b11e7788d/oauth2/v2.0/token' \

--header 'Content-Type: application/x-www-form-urlencoded' \

--data-urlencode 'grant_type=client_credentials' \

--data-urlencode 'client_id=ab2d512d-2c18-4627-abd4-12a5dff8b5ac' \

--data-urlencode 'client_secret=WFy8Q~GRY5i~DN4ZIV1K.sA4CyJmQpYjwxXYeaaD' \

--data-urlencode 'scope=api://8f0c359e-553d-470d-a8da-30b3db8953b7/.default'

Use this token to call PO api endpoint and you would get a success response.

And if you use the same token to call payment api, the response wound be invalid.

 

 

Reference:

https://azure.microsoft.com/en-in/pricing/details/api-management/

https://learn.microsoft.com/en-us/azure/api-management/import-and-publish (https://learn.microsoft.com/en-us/azure/api-management/mock-api-responses)

 

 

Comments

Popular posts from this blog

Azure Integration Service: Event Driven Architecture with sFTP and SMB

Azure Integration Service: Event Driven Architecture with sFTP and SMB - Part2

Logic App Storage Table CRUD Operations