Azure APIM Proxy API - Policy Issue

79 Views Asked by At

The setup we have forbids a direct call to https://login.microsoftonline.com and hence I created a Proxy API in Azure APIM with a policy as follows:

Policy Type: Inbound
Get & set-variables from header (scope, client id, secret & grant type)
send-request to https://login.microsoftonline.com
set-method to POST
set-header Content-Type to x-www-form-urlencoded
set-body to return scope, client id, secret and grant type
get the response body
get the access token
set the token in a new body (liquid template)
set the body in return-response (convert body to string)

In APIM, I test this with all the required headers and I get an access token without any issues.
In Postman, I call my proxy API with all the required headers and I get an access token without any issues.

{
    "status": "ok",
    "token": "Bearer ASODIA@#$)(*ASDJASODNADSAOSDJ....PROPER TOKEN"
}

Now I do the same in my NodeJS code, call the same API with Headers (using Axios), I do get a proper response back BUT the Token returns empty!

{
    "status": "ok",
    "token": "Bearer "
}

Anybody has any idea on why this is happening and how I can solve this? If it was a CORS issue, I should have got an error instead of 200 OK and partial body! Or am I wrong in assuming that?

Thanks in advance...

SSG

Edit: NodeJS Code for the API Call

router.get("/test", async (req, res) => {
  let config = {
    headers: {
      client_id: process.env["BID"],
      client_secret: process.env["BSEC"],
      scope: process.env["BSCOPE"],
    },
  };

  let data = {
    "Content-Type": "application/json",
  };

  const apimUrl = "https://gateway-test.hapi.hmgroup.com/hapi-utils-auth/token";
  axios
    .get(apimUrl, data, config)
    .then((response) => {
      console.log(response.data);
      res.send(response.data);
    })
    .catch((error) => {
      console.log(error);
    });
});

1

There are 1 best solutions below

1
Ikhtesam Afrin On BEST ANSWER

I have added the below given policy to the Echo API which gets created by default when we create an Azure APIM instance. I am able to fetch the Bearer token using this policy.

<policies>
    <inbound>
        <base />
        <set-variable name="clientId" value="@(context.Request.Headers.GetValueOrDefault("client-id", ""))" />
        <set-variable name="clientSecret" value="@(context.Request.Headers.GetValueOrDefault("client-secret", ""))" />
        <set-variable name="grantType" value="@(context.Request.Headers.GetValueOrDefault("grant-type", ""))" />
        <set-variable name="scope" value="@(context.Request.Headers.GetValueOrDefault("scope", ""))" />
        <send-request mode="new" response-variable-name="tokenResponse" timeout="60" ignore-error="true">
            <set-url>https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token</set-url>
            <set-method>POST</set-method>
            <set-header name="Content-Type" exists-action="override">
                <value>application/x-www-form-urlencoded</value>
            </set-header>
            <set-body>@{
                var body = $"client_id={Uri.EscapeDataString((string)context.Variables["clientId"])}&client_secret={Uri.EscapeDataString((string)context.Variables["clientSecret"])}&grant_type={Uri.EscapeDataString((string)context.Variables["grantType"])}&scope={Uri.EscapeDataString((string)context.Variables["scope"])}";
                return body;
            }</set-body>
        </send-request>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
        <set-variable name="tokenResponseBody" value="@((String)((IResponse)context.Variables["tokenResponse"]).Body.As<string>(preserveContent: true))" />
        <set-variable name="accessToken" value="@((string)JObject.Parse((string)context.Variables["tokenResponseBody"])["access_token"])" />
        <return-response>
            <set-status code="200" reason="OK" />
            <set-header name="Content-Type" exists-action="override">
                <value>application/json</value>
            </set-header>
            <set-body>@{
                var responseBody = new {
                    access_token = (string)context.Variables["accessToken"]
                };
                return JsonConvert.SerializeObject(responseBody);
            }</set-body>
        </return-response>
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

enter image description here

I am able to get the bearer token as shown below-

enter image description here enter image description here

Now I am trying to call the APIM Url in the NodeJS code to get the token.

app.js-

const express = require('express');
const testRouter = require('./sample.js'); 

const app = express();
const port = 3000; 

app.use('/', testRouter);

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

sample.js-

const express = require('express');
const axios = require('axios');
const router = express.Router();

router.get("/test", async (req, res) => {
    const headers = {
        'client-id': '{client-id}',
        'client-secret': '{client-secret}',
        'scope': '{scope}',
        'grant-type': 'client_credentials',
        'ocp-apim-subscription-key': '{subscription-key}'
    };

    const apimUrl = "https://afreen-apimgmt.azure-api.net/echo/resource?param1=demo";
    axios
        .get(apimUrl, {
            headers: headers,
        })
        .then((response) => {
            console.log(response.data);
            res.send(response.data);
        })
        .catch((error) => {
            console.log(error);
            res.status(500).send("Internal Server Error");
        });
});

module.exports = router;

I am able to get the token successfully.

enter image description here

Follow my steps, you will get the response too.