Retrieving OAuth2 Token Via Node-Fetch

Viewed 3600

The issue is that I'm trying to retrieve a OAuth2 token. Since request has been deprecated, I'm using node-fetch for this. While I can get it to work with request, I cannot with node-fetch.

I have read numerous posts here about but none seem to actually give a consistent answer that actually works. I allow that I simply have not looked good enough. There may also be the complication that I'm wrapping this in a test, but it doesn't feel like that's the case due to the error message I'm getting.

Here is code that works (and note that I had to change details to protect internal URLs):

var request = require("request");

var options = {
  method: "POST",
  url: "https://some-url/realms/my-realm/protocol/openid-connect/token",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  form: {
    username: "JeffUser",
    password: "jeff-password",
    grant_type: "password",
    client_id: "jeff-client",
    client_secret: "jeff-client"
  }
};

request(options, function(error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
})

That works and I get the token. Here is what I'm trying in node-fetch (wrapped in a test) which is failing:

const assert = require("chai").assert;
const fetch = require("node-fetch")

describe("Test API", function() {
  let api_token = "";

  it("gets a token", async function() {
    api_token = await fetch("https://some-url/realms/my-realm/protocol/openid-connect/token", {
      method: "post",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
      form: {
        username: "JeffUser",
        password: "jeff-password",
        grant_type: "password",
        client_id: "jeff-client",
        client_secret: "jeff-client"
      }
    }).then (response => {
      return response.json();
    });

    console.log(token);
  });
});

What happens here is I get the following output from that test:

{
  error: 'invalid_request',
  error_description: 'Missing form parameter: grant_type'
}

I have tried changing form to body as some searching has suggested, but that doesn't seem to have any effect. I tried wrapping the form: { } data with a JSON.stringify() as some other searching has suggested but that, too, led to the same error being reported.

2 Answers

This is thanks to @CBroe who provided me the answer in comments. I'll put the solution here for others who come across this. Here is what I had to do:

const assert = require("chai").assert;
const fetch = require("node-fetch")

const params = new URLSearchParams();

params.append("grant_type", "password");
params.append("username", "JeffUser");
params.append("password", "jeff-password");
params.append("client_id", "jeff-client");
params.append("client_secret", "jeff-client");


describe("Test API", function() {
  let api_token = "";

  it("gets a token", async function() {
    api_token = await fetch("https://some-url/realms/my-realm/protocol/openid-connect/token", {
      method: "post",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body: params
    }).then (response => {
      return response.json();
    });

    console.log(token);
  });
});

I made sure to use body instead of form. I also used the URLSearchParams to create a series of parameters, passing that into the body parameter. And that did the trick! I now get the authorization token.

const buffer = require("buffer")
const fetch = require("node-fetch")

let url = 'www.exampl.com/token';
let client_id = '123';
let client_secret = '323';
let username = 'test';
let password = 'test';

let basicAuth = buffer.from(`${client_id}:${client_secret}`).toString("base64");

const response = await fetch(url, {
 method:"POST",
 headers:{ 
    "Content-Type": "application/x-www-form-urlencoded",
    Accept: "application/json; charset=UTF-8", 
     Authorization: `Basic ${basicAuth}`, 
    },
  body:`grant_type=password&username=${username}&password=${password}&scope=xfds`,
});

let token = await response.json();

console.log(token)
Related