node.jsazureaxiosapim

Azure APIM Proxy API - Policy Issue


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);
    });
});


Solution

  • 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.