Finding hidden gems vol. 1: forging OAuth tokens using discovered client id and client secret

Mateusz Olejarka
Jul 23, 2018 · 2 min read

I love sensitive information exposure bugs. They are getting more attention at last. Below a short story about leaked Node.js code and OAuth client id and client secret which I found in there.


One of my bug bounty recon tools discovered a package.json file which looked interesing. The package.json file is a description of a Node.js module. This particular one looked like below:

"name": "[…redacted…]",
"description": "[…redacted…]",
"version": "[…redacted…]",
"private": true,
"engines": {
"node": ">=6.9.1"
"scripts": {
"start": "node server/[…redacted…].js"

Setting scripts contained a path to some Node.js code to execute. I was curious what’s in that file, therefore I immediately downloaded the content. It was a huge file, containing Node.js code.


I found there four variables which looked very promissing.

const devClientId = '11e7f9d3–9bbf-4a01-a23e-c9c58e3acb1d';
const prodClientId = 'a2ae2727-aa6a-4197–823e-40e6d4e503a7';
const devClientSecret = 'd[…redacted…]=';
const prodClientSecret = 'b[…redacted…]=';

Disclaimer: all values were replaced or redacted.

It ringed a distant bell related to one of the OAuth grant types — the client credentials. To quote the specs:

Client credentials are used as an authorization grant
typically when the client is acting on its own behalf (the client is
also the resource owner) or is requesting access to protected
resources based on an authorization previously arranged with the
authorization server.

From other recon results I knew that this company is built on top of Microsoft Azure therefore I searched for OAuth integration documentation. Now I just wanted to check if prodClientId and prodClientSecret are still valid:

POST /[redacted] HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 190
HTTP/1.1 200 OK

I got back a JWT token and just one thing left — to actually use it and try to get some data. I’ve selected an API call which returns users:

GET /v1.0/users HTTP/1.0
Authorization: Bearer eyJ[…redacted…]
HTTP/1.1 200 OK
“businessPhones”:[],”displayName”:”Stuart […redacted…]”,
“displayName”:”Ashley […redacted…],

It worked! This was found in a public program on Bugcrowd and they gave me 3133.7$ for this finding.


The company fixed it like below — now those variables are taken out of Consul:

class Consul {
constructor(url) {
this.consulHost = url;
getClientId() {
return this.getConsulValue(‘v1/[redacted]/CLIENT_ID’);
getClientSecret() {
return this.getConsulValue(‘v1/[redacted]/APP_KEY’);
getConsulValue(consulKey) {
return request({ url: `${this.consulHost}/${consulKey}`, json: true }).promise()
.then((res) => res[0].Value)
.then((val) => (new global.Buffer(val, ‘base64’)).toString(‘utf8’));
exports.Consul = Consul;

Where consulHost in taken from an environmental variable:

const consulHost = process.env.CONSUL

Lessons learned:

  • For myself — don’t give up, make a break, return to the problem next day and solve it easily.
  • Bug hunters — definitely check for package.json files.
  • Developers — check what is in your package.json file and make sure you don’t leak anything interesing.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store