Decoding firebase id token failed, auth/argument error - node.js

I'm trying to authenticate using user requests and it appears that I'm it is failing even though I have the correct token (and in fact, from the decoded token, I can pull the uid, which I thought means it succeeded. On the client side, this is where my token is generated
const getIdToken = async() => {
if (firebaseAuth.currentUser) {
const token = await firebaseAuth.currentUser.getIdToken(true);
return token;
}
}
The tokens are added to headers in my axios requests
const header = await createHeader(userIdToken);
try {
await axios.post(SERVICE_PATH + '/gardens/addNew', garden, {headers: header})
via this functin
export const createHeader = async function(userIdToken) {
const payloadHeader = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${userIdToken}`,
},
};
return payloadHeader;
}
On the server-side, I decode
async function decodeIdToken(req, res, next) {
const header = req.headers?.authorization;
if (header !== 'Bearer null' && req.headers?.authorization?.startsWith('Bearer ')) {
const idToken = req.headers.authorization.split('Bearer ')[1];
admin
.auth()
.verifyIdToken(idToken)
.then(function (decodedToken) {
return next();
})
.catch(function (error) {
console.log(error);
return res.sendStatus(403);
});
} else {
res.sendStatus(401);
}
}
This is just in my server.js file (the last loaded middleware, after express, cors, and json)
app.use(decodeIdToken);
I've compared the tokens on the client and server and they appear correct. It even seems like the token is in fact decoded, since I can print the uid, but I may not be understanding that correctly. I'm very confused why I'm getting an argument error in particular.
After reviewing the headers, I've realized I confounded myself by having two separate requests with different formats. I'm posting both here, because both need to work, and I'm not seeing what I expected in the axios documentation on how to do this.
The first request takes this format:
await axios.post(SERVICE_PATH + '/gardens/addNew', garden, {headers: header})
And results in the following for req.headers on the server
{
host: 'localhost:5000',
connection: 'keep-alive',
'sec-ch-ua': '" Not;A Brand";v="99", "Microsoft Edge";v="97", "Chromium";v="97"',
accept: 'application/json, text/plain, */*',
authorization: 'Bearer undefined',
'sec-ch-ua-mobile': '?0',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 Edg/97.0.1072.69',
'sec-ch-ua-platform': '"Windows"',
origin: 'http://localhost:3000',
'sec-fetch-site': 'same-site',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
referer: 'http://localhost:3000/',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'en-US,en;q=0.9'
}
So it seems like the headers aren't going through, even though I explicitly added them the way I expected I should.
The second request type has no body, only params, so I pass the header as the second argument (which was what I was trying NOT to do, because I thought this was wrong, so I was phasing this out)
const res = await axios.get(SERVICE_PATH + '/plants/', header);
Which results in a header that looks the same as above, but with a defined token where it said undefined before. I don't believe it is throwing any firebase errors.
After seeing this, I think my question should actually have been, what is the right way to pass headers in an axios request? Should I be building them into the body if I need to pass additional objects? The examples I found largely seemed to have headers or a body rather than both, and the docs said to pass headers as the 3rd parameter (from my understanding), so now I'm confused :(

As it turns out, there were two problems I was running into. The first was not understanding how to properly send headers in axios (requiring the { headers } object containing key value pairs, not another object) and the second was not understanding how to properly set data into the header in my middleware. My updated code looks like
client (for requests with a body)
await axios.post(SERVICE_PATH + '/gardens/addNew', garden, { headers: header.headers})
And on the server I updated the try block for my token decoding to
admin
.auth()
.verifyIdToken(idToken)
.then(function (decodedToken) {
req['decodedToken'] = decodedToken;
return next();
})

Related

Fetching data using other runtimes (python, .NET, Java) as well as cURL works fine, but on NodeJS, returns Error 404

So I was trying to fetch data by reverse engineering Twitter's API. Fetching the data using cURL or even in other languages and runtimes such as .NET (using RestSharp), python (using requests) works fine and returns the tweets as expected.
When I do the same in NodeJS, irrespective of the library (axios, https, requests), it always returns Error 404
The request I'm using is:
cURL Code
curl --location --request GET 'https://api.twitter.com/2/search/adaptive.json?q=(from%3Aelonmusk)&query_source=typed_query&count=40' --header 'sec-ch-ua: "Not_A Brand";v="99", "Microsoft Edge";v="109", "Chromium";v="109"' --header 'x-twitter-client-language: en' --header 'x-csrf-token: <csrf_token>' --header 'sec-ch-ua-mobile: ?0' --header 'authorization: Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA' --header 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.61' --header 'x-twitter-auth-type: OAuth2Session' --header 'x-twitter-active-user: yes' --header 'sec-ch-ua-platform: "Windows"' --header 'Accept: */*' --header 'host: api.twitter.com' --header 'Cookie: <cookie>'
NodeJS Axios Code
var axios = require('axios');
var config = {
method: 'get',
maxBodyLength: Infinity,
url: 'https://api.twitter.com/2/search/adaptive.json?q=(from%3Aelonmusk)&query_source=typed_query&count=40',
headers: {
'sec-ch-ua': '"Not_A Brand";v="99", "Microsoft Edge";v="109", "Chromium";v="109"',
'x-twitter-client-language': 'en',
'x-csrf-token': '<csrf_token>',
'sec-ch-ua-mobile': '?0',
'authorization': 'Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.61',
'x-twitter-auth-type': 'OAuth2Session',
'x-twitter-active-user': 'yes',
'sec-ch-ua-platform': '"Windows"',
'Accept': '*/*',
'host': 'api.twitter.com',
'Cookie': '<cookie>'
}
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
The response I'm getting is:
AxiosError: Request failed with status code 404
If I use node-libcurl library to fetch data instead of axios or https, and disable ssl verification while using node-libcurl library, the data is fetched as intended and I am getting the list of tweets.
Also, executing the same request in Postman works alright too.
The problem only occurs in NodeJS
Bearer token has expiry, after expired time, it will not works.
So you need to call get token first with API key/secret,
Detail for get access-token in here
Then search API will remove time dependency.
detail about search in here
This code will works
const axios = require("axios")
const getToken = async () => {
try {
const API_KEY = '<your API Key>'
const API_KEY_SECRET = '<your API SECRET>'
const response = await axios.post(
'https://api.twitter.com/oauth2/token',
'',
{
params: {
'grant_type': 'client_credentials'
},
auth: {
username: API_KEY,
password: API_KEY_SECRET
}
}
);
return Promise.resolve(response.data.access_token);
} catch (error) {
return Promise.reject(error);
}
}
const searchTweet = async (token, query, limit) => {
try {
const siteUrl = `https://api.twitter.com/2/tweets/search/recent?tweet.fields=created_at,geo&query=${query}&max_results=${limit}`
const response = await axios.get(siteUrl, {
headers: {
'Accept-Encoding': 'application/json',
Authorization: `Bearer ${token}`
}
});
return Promise.resolve(response.data);
} catch (error) {
return Promise.reject(error);
}
}
getToken().then(token => {
// console.log("access token: " + token)
searchTweet(token, 'elonmusk', 10).then(tweets => {
for (tweet of tweets.data) {
console.log(JSON.stringify(tweet))
}
}).catch(error => console.log(error));
}).catch(error => console.log(error));
Result
$ node get-tweet.js
{"created_at":"2023-02-01T16:29:26.000Z","edit_history_tweet_ids":["1620821639058563072"],"id":"1620821639058563072","text":"RT #ThierryBreton: I take note of the path that Twitter is committed to take in Europe to comply with #DSA rules.\n\nNext few months will be…"}
{"created_at":"2023-02-01T16:29:26.000Z","edit_history_tweet_ids":["1620821638102278145"],"id":"1620821638102278145","text":"#Tessa430 #elonmusk He explained that. He wants to see how the code effects his account personally"}
{"created_at":"2023-02-01T16:29:26.000Z","edit_history_tweet_ids":["1620821637305352192"],"id":"1620821637305352192","text":"#kucoincom It's OK to have your eggs in one basket as long as you control what happens to that basket” ~ #ElonMusk; Listen to him and invest in #TechTrees #TTC $TTC"}
{"created_at":"2023-02-01T16:29:26.000Z","edit_history_tweet_ids":["1620821636370014210"],"id":"1620821636370014210","text":"#Hauwah_omar0 #elonmusk plz remove this kind of bitchsss😪"}
{"created_at":"2023-02-01T16:29:26.000Z","edit_history_tweet_ids":["1620821636332290049"],"id":"1620821636332290049","text":"RT #GermanosGhassan: #GermanosPeter #Twitter After acquiring twitter, Elon musk promised to block spams and fakes. Twitter for Lebanese pol…"}
{"created_at":"2023-02-01T16:29:25.000Z","edit_history_tweet_ids":["1620821635262464000"],"id":"1620821635262464000","text":"#elonmusk #TaraBull808 Don’t worry, twitter is dropping faster than a nuclear bomb."}
{"created_at":"2023-02-01T16:29:25.000Z","edit_history_tweet_ids":["1620821634511949828"],"id":"1620821634511949828","text":"#elonmusk #ElijahSchaffer AKA hard-coded."}
{"created_at":"2023-02-01T16:29:25.000Z","edit_history_tweet_ids":["1620821634436464640"],"id":"1620821634436464640","text":"RT #SidneyPowell1: Thank you #elonmusk \nI'm back!! Freedom of speech is crucial to our survival as the beacon of freedom for the world. I &…"}
{"created_at":"2023-02-01T16:29:25.000Z","edit_history_tweet_ids":["1620821632918118401"],"id":"1620821632918118401","text":"#elonmusk 👏👏👏👏"}
{"created_at":"2023-02-01T16:29:25.000Z","edit_history_tweet_ids":["1620821632615940096"],"id":"1620821632615940096","text":"#libsoftiktok #elonmusk How do we change to private?"}

403 forbidden when I send a POST request (axios) on specific API

Good evening everyone. I've been playing with HTTPs request for a while and I've been reading the axios documentation. I've been trying to send a request to a specific API (Zalando) but I got struck with 403 forbidden. Modules like puppeteer and chromium works fine because they do sit in a web browser and so they don't get easily detected by Akamai. Here below is the code I'm using. I've been doing this ONLY for educational purpose. Feel free to comment. Have a nice day all.
const axios = require('axios')
const params = [{
id: "e7f9dfd05f6b992d05ec8d79803ce6a6bcfb0a10972d4d9731c6b94f6ec75033",
variables: {
addToCartInput: {
productId: "PU111A0P1-C110035000",
clientMutationId: "addToCartMutation"
}
}
}]
const instance = axios.create({
withCredentials: true,
baseURL: 'https://www.zalando.it'
})
instance.post('/api/graphql/add-to-cart/', params, {
headers: {
'accept': '*/*',
'content-Type': 'application/json',
'user-agent': 'Mozilla/5.0 (X11; CrOS x86_64 8172.45.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.64 Safari/537.36',
},
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
}
Response: [AxiosError: Request failed with status code 403]

Connecting to api using basic auth Angular 8 Express js

I have set up an angular 8 app, that connects to an express API.
I'm running it locally, to test.
My front end app connects to http://localhost:4200/ and backend to http://localhost:3000/
I've set up an express route to connect to https://api.podbean.com/v1/podcasts?access_token=baee9cb65384a814e704adc626dc969bb019f84d
which works fine, returning all podcasts
But the debugToken endpoint never works via the express route, if I use https://api.podbean.com/v1/oauth/debugToken?access_token=baee9cb65384a814e704adc626dc969bb019f84d
Using postman with basic auth clientId = '7faf9a7ad38a01c7d900c' client_secret = 'a7a3825f02be39c57ff44' it works ok, but never when connecting via localhost
I'm using GET
It must be connecting because I get an object returned, although it's an error
In Angular:
debug() {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
Authorization: 'Basic ' + btoa('7faf9a7ad38a01c7d900c:a7a3825f02be39c57ff44')
})
};
console.log(httpOptions);
return this.http.get(`${this.configUrl}/debug/`, httpOptions);
}
Express:
router.get('/debug', function (req, res, next) {
var options = {
url: `https://api.podbean.com/v1/oauth/debugToken?access_token=${accessToken}`
}
request(options, function (err, response, body) {
console.log( req.headers);
if(err){
return res.status(500).json({
title: 'An error has occured',
error: err
})
}
res.json(JSON.parse(body));
next();
})
});
When I log the request headers in the express/node side
{host: 'localhost:3000',
connection: 'keep-alive',
pragma: 'no-cache',
'cache-control': 'no-cache',
'sec-fetch-mode': 'cors',
origin: 'http://localhost:4200',
authorization: 'Basic N2ZhZjlhN2FkMzhhMDFjN2Q5MDBjOmE3YTM4MjVmMDJiZTM5YzU3ZmY0NA==',
'content-type': 'application/json',
accept: 'application/json, text/plain, */*',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36',
dnt: '1',
'sec-fetch-site': 'same-site',
referer: 'http://localhost:4200/',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'en-GB,en;q=0.9,en-US;q=0.8,it;q=0.7,es;q=0.6' }
Returned object:
{"error":"invalid_token","error_description":""}
Which tells me I'm connecting, just not correctly
Looks like you just confused endpoints. You are sending basic auth from Angular page to your Express endpoint,
which doesn't make much sense, because it's https://api.podbean.com who requires authorization, not your Express server.
Try adding basic auth credentials to the request which goes from your Express server to api.podbean.com
router.get('/debug', function (req, res, next) {
var options = {
url: `https://api.podbean.com/v1/oauth/debugToken?access_token=${accessToken}`,
headers: {
'Authorization': 'Basic ' + new Buffer("7faf9a7ad38a01c7d900c:a7a3825f02be39c57ff44").toString('base64')
}
}
request(options, function (err, response, body) {
...

Node server unable to retrive the value of react axios request header parameter

I need to access axios header authorization token in server side(Node), showing undefined. Please help..
Client side(React) request:
var config = {
headers: {
'cache-control':'no-cache',
'content-type': 'application/x-www-form-urlencoded',
'authorization' :'bearer '+Auth.getToken()
}
};
axios.get(ApiConfig.API_BASE+'api/admin/profile/', config).then(function(response) {
this.setState({status:'success', profile: response.data.data});
}).catch(function(response) {
console.log(response);
});
Server side(Node):
module.exports = (req, res, next) => {
console.log(req.headers.authorization);
if(!req.headers.authorization) {
return res.status(401).end();
}
};
Log showing undefined. I also console the entire header, but their output is:
{ host: 'localhost:8027',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:55.0) Gecko/20100101 Firefox/55.0',
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'accept-language': 'en-US,en;q=0.5',
'accept-encoding': 'gzip, deflate',
'access-control-request-method': 'GET',
'access-control-request-headers': 'authorization,cache-control',
origin: 'http://localhost:3001',
connection: 'keep-alive' }
How do I retrieve the authorization token value?
Thank you.
I'm assuming you are using express. If so, instead of getting the header value as req.headers.authorization, try req.get('authorization').
http://expressjs.com/en/api.html#req.get
If you are making a cross-origin HTTP request, please make sure CORS has been enabled in your server. If you are using express cors middleware can be used.
I guess your problem here is that since CORS has not been enabled, your server will receive a OPTIONS request first, so the entire header you console is from the OPTIONS request not the GET request as you desired. You can use console.log(req.method) to verify. BTW req.headers.authorization is ok to receive the header.

Set Express response headers before redirect

I'm implementing a site login that takes in an email/password combo, retrieves an API token, and returns it to the user to get stored (encrypted) in localStorage.
Currently, on successful POSTing to /login, the app redirects the user to the index page, with the token attached as a query, like so (as suggested here):
login.post('/', function(req, res) {
...checking password...
Auth.getToken(user, function(err, token) {
res.redirect('/?token=' + token);
});
});
This works fine, but I'd prefer to keep my URLs as clean as possible and set the token as a header instead:
login.post('/', function(req, res) {
...checking password...
Auth.getToken(user, function(err, token) {
res.set('x-access-token', token);
console.log(res._headers);
// --> {'x-powered-by': 'Express', 'x-access-token': <token>}
res.redirect('/');
});
});
console.log-ing res._headers shows that the headers are set as expected, but when I log req.headers on the request to the index page, it's not showing up:
{ host: 'localhost:3000',
connection: 'keep-alive',
'cache-control': 'max-age=0',
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36',
referer: 'http://localhost:3000/login',
'accept-encoding': 'gzip, deflate, sdch',
'accept-language': 'en-US,en;q=0.8',
cookie: 'ifusr=crwj; _ga=GA1.1.1933420201.1409901705',
'if-none-match': '"1195161647"' }
Any suggestions appreciated!
Setting headers wouldn't work here because a redirect will execute a new http request, you can use express-session to store the auth token and fetch it when you need it
req.session.accessToken = token

Resources