Unable to call token endpoint using standard Node.js HTTPS module

I need some help, I couldn’t find a similar issue on SO or Auth0 community. I’m using the https module to connect to an Auth0 endpoint from localhost. It’s unlikely that the endpoint doesn’t exist but I’m getting a 404 response. I’ve checked my Auth0 config as well and seems fine.

Below are the request options I’ve tried (option 1 is an example from Auth0), the server/request code, and the response to each set of options. Could this be an issue with the certificates I’m using? I installed root CAs and used along with a self-signed certificate, but that wouldn’t result in a 404.

Server:

const https = require('https');
const fs = require('fs');
const path = require('path');

const express = require('express');
const rootCA = require('ssl-root-cas/latest').create().addFile(__dirname+'/cert/CA.pem');

const port = process.env.PORT || 443;
const app = express();
const httpsOptions = {
	key: fs.readFileSync(path.join(__dirname, './cert/localhost.key')),
	cert: fs.readFileSync(path.join(__dirname, './cert/localhost.crt'))
};
https.globalAgent.options.ca = rootCA;
app.use(require('cookie-parser')(process.env.COOKIE_SECRET));
app.use(express.json());
app.use('/auth', require('./Auth'));

const server = https.createServer(httpsOptions, app).listen(port, () => { console.log(`server running on port ${port}`); });

https.request options I’ve tried:

// Option #1
var options = {
	method: 'POST',
	url: 'https://mytenant.us.auth0.com/oauth/token',
	headers: { 'content-type': 'application/json' },
	body: '{"client_id":"myclientid","client_secret":"myclientsecret","audience":"https://localhost:443/user/search","grant_type":"client_credentials"}'
};

// Option #2
var options = {
	method: 'POST',
	host: 'mytenant.us.auth0.com',
	port: 443,
	url: 'https://mytenant.us.auth0.com/oauth/token',
	headers: { 'content-type': 'application/json' },
	body: '{"client_id":"myclientid","client_secret":"myclientsecret","audience":"https://localhost:443/user/search","grant_type":"client_credentials"}'
};

Auth module mounted on Express app:

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

router.post('/token', (req, res) => {
	var options = {
		// see above
	},
	r = https.request(options, (response) => {
		console.log('statusCode:', response.statusCode);
		console.log('headers:', response.headers);

		response.on('data', (d) => {
			console.log(d.toString());
		});
	});

	r.on('error', (e) => {
		console.error(e);
	});
	r.end();
});

module.exports = router;

Response to https.request Option #1:

statusCode: 404
headers: {
  'x-powered-by': 'Express',
  'x-ratelimit-limit': '100',
  'x-ratelimit-remaining': '99',
  date: 'Wed, 23 Sep 2020 12:31:14 GMT',
  'x-ratelimit-reset': '1600865142',
  'content-security-policy': "default-src 'none'",
  'x-content-type-options': 'nosniff',
  'content-type': 'text/html; charset=utf-8',
  'content-length': '140',
  connection: 'close'
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot POST /</pre>
</body>
</html>

Response to https.request Option #2:

statusCode: 404
headers: {
  date: 'Wed, 23 Sep 2020 16:28:37 GMT',
  'content-type': 'text/plain; charset=utf-8',
  'content-length': '10',
  connection: 'close',
  server: 'nginx',
  'ot-tracer-spanid': '08d8bd90433cda10',
  'ot-tracer-traceid': '07cd8fe13ee97d50',
  'ot-tracer-sampled': 'true',
  'ot-baggage-auth0-request-id': 'a6e2793b76752b11b4cad24a',
  'x-auth0-requestid': 'd724a321a207298ad58b',
  'set-cookie': [
    'did=s%3Av0%3Ad50c1f60-fdb9-11ea-a311-2b10561c1c2c.LAjk%2BY%2BXs2rIpTslhu5IzLiIlWSp6fNdZDtfbLRmJNY; Max-Age=31557600; Path=/; Expires=Thu, 23 Sep 2021 22:28:37 GMT; HttpOnly; Secure; SameSite=None',
    'did_compat=s%3Av0%3Ad50c1f60-fdb9-11ea-a311-2b10561c1c2c.LAjk%2BY%2BXs2rIpTslhu5IzLiIlWSp6fNdZDtfbLRmJNY; Max-Age=31557600; Path=/; Expires=Thu, 23 Sep 2021 22:28:37 GMT; HttpOnly; Secure'
  ],
  'x-auth0-not-found': '1',
  etag: 'W/"a-8RJARPvfYzJdDi+ZdXbdTOYnAfo"',
  'cache-control': 'private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, no-transform',
  'strict-transport-security': 'max-age=31536000'
}
Not found.

Hey @zchan,

I have the feeling that this is because you’re posting with the application/json content-type. This endpoint expects an content-type of application/x-www-form-urlencoded, and an example can be found here:

https://auth0.com/docs/api/authentication#client-credentials-flow

Can you give it a try with the correct content type/format and let me know if it helps?

Thanks!

Hi @joseantonio.rey,

Is your team able to provide an https module example of the call? I didn’t modify my options but switched to the deprecated request module and received a token. I don’t think request uses SSL without extra config.

Thanks!

Hello, @zchan,

I recommend that you take a look at our Quickstarts , where you will be able to find code examples for a lot of languages.

Hi @joseantonio.rey,

The code from my original post (with application/json content type is from an Auth0 quickstart). The only difference was I was making the call over SSL with the https module. The request module is now fully deprecated (please see their NPM page). Is Auth0 able to provide a https module example?

Thank you

Hey @zchan,

Sorry, I did not know about that! Let me see what I can find for you in the morning and I’ll get back to you :slight_smile:

1 Like

Hello, @zchan,

I have just checked our Quickstarts and neither our Node.js nor our Javascript Quickstart reference the request module. We also don’t have any examples using https.

As I mentioned, I recommend that you use whatever modules that you want to use, but reference the Authentication API Explorer in order to build a call according to specifications. You can use absolutely any module or package of your preference.

Finally, I would like to emphasize the fact that the Client Credentials grant is supposed to be used in a Machine to Machine scenario, and not when the application is authenticating on behalf of a user. It should only be used when a machine wants to authenticate on behalf of itself.