Error: No state in response using oidc-client-js - node.js

Good evening,
Im developing a web app in react where i wanted to use oidc-client-js to make authentication.
However,when i run this code below:
const userManager = new UserManager({
authority: "https://accounts.google.com/.well-known/openid-configuration",
client_id: "*myclientid*",
redirect_uri: "http://localhost:3000/projects",
});
const loginUser = (_) => {
userManager
.signinRedirectCallback()
.then((user) => {
alert(user);
})
.catch((error) => {
alert(error);
});
};
I get the Error: No state in response.
After some searching on this error i already tried to put the response_mode:'query' on the UserManager object but it didnt work out.
Thanks in advance.

I assume that this code is to handle the response (tokens) received from idp right?
if so, did you check the signin request? as part of the request, client needs to send state (in url) and idp returns the same state back to client, so that client side, it validates that it is receiving the response for the request the client is initiated (like a hand shake)
where ever you are initiating the sign in request, pass in state and verify the state value (some random 15 digit uuid) is being passed along with authroize request.
userManager.signinRedirect({ state: { bar: 15 } });

Related

React Native Expo Cli Facebook Authentication - unable to exchange Response type code for access token on server API

I am creating React Native app using Expo and used its inbuilt Facebook.useAuthRequest to generate a response when a user logs in. When I create a response type of Token I am able to take this token and send it to my backend API that successfully uses it to get the user details.
However I had hoped to implement a response type of code and use this on the backend API generate the access Token and then request the user details - as I believe this is the most secure option when sending the code to my server.
The issue that I'm facing is that I keep getting an error when trying to formulate the requst to Graph API and I dont understand why:
error: {
message: 'Missing client_id parameter.',
type: 'OAuthException',
code: 101,
fbtrace_id: 'ARHcoh260kBwj7l9yDHjU-n'
}
I just want to confirm that I believe I have inserted all the correct information into the request, so I am unsure of why this error is saying its missing the cliend_id. Here is my request from my API server:
const { data } = await axios({
url: https://graph.facebook.com/v12.0/oauth/access_token? client_id=${appId} &redirect_uri=${redirectUri} &client_secret=${appSecret} &code=${code},
method: 'get',
});
I just want to confirm that the client_id I have taken from app id I created on the facebook developer page, as well as the client_secret, redirect is the https:// used in the initial request and the code is the code initially received in my client side request.
Thanks in advance for any suggestions :)
Just a quick update on this, I was ablel to reformat the request as I believe it had some errors in the spacing and I moved to using the .env files so now my request looks like this:
const redirectUri = {MY_REDIRECT URL};
const appId = process.env.FACEBOOK_CLIENT_ID;
const appSecret = process.env.FACEBOOK_CLIENT_SECRET;
const { data } = await axios({
url: `https://graph.facebook.com/v12.0/oauth/access_token?client_id=${appId}&redirect_uri=${redirectUri}&client_secret=${appSecret}&code=${code}`,
method: 'get',
});
It seems I have moved onto a new error with the following:
error: {
message: 'Invalid code verifier. Code verifier should be a cryptographically random string using the characters A-Z, a-z, 0-9, and the punctuation characters -._~ (hyphen, period, underscore, and tilde), between 43 and 128 characters long.',
type: 'OAuthException',
code: 1,
fbtrace_id: 'AQKIUad5RRCitb6m977fnFW'
}
I'm a bit stumped for what this means as I have checked and all my values appear correct. My only thought is if I need to do something with the code initially received on the client side?
Ok so I finally figures it out - the issue was the I wasn't sending the code_verifier along with my request to exchange the Auth Code for a token. I ended up sending this code_verifier to my API server then adding this to the request so it looked something like this:
FB.api(
'oauth/access_token',
{
client_id: appId,
client_secret: appSecret,
redirect_uri: redirectUri,
code_verifier: code_verifier,
code: code,
},
function (response) {
if (!response || response.error) {
console.log(!response ? 'error occurred' : response.error);
return;
}
var accessToken = response.access_token;
This then finally gave me the accessToken I was looking for that I can then use to exchange for user details server side.
... and the code_verifier is obtained from request.codeVerifier.
const [request, response, promptAsync] = Facebook.useAuthRequest(...

decode firebase oobCode to user on server side

Having some issues regarding a custom email flow and how to decode users on the backend.
see flow:
user: (webapp)
enter email address and send request for a password reset
server:
generate oobCode using nodeJS SDK: admin.auth().generatePasswordResetLink(email);
send link in custom email to user
user: (webapp)
clicks link -> www.example.com?oobCode=XYZ
client SDK verify's token has not expired: firebase.auth().verifyPasswordResetCode("XYZ");
if valid send oobCode and new password back to server
Here is where the issue now occurs
I have sent the new password and the oobCode back to the server, below is the required functionality
server:
1. verify oobCode and get user uid
I see no methods to be able to decode this in the backend to verify the user how do I go about getting a user from this code on the backend? it seems we can generate but not decode
Try running this code to verify oobCode for password reset:
const passResetUrl = `https://identitytoolkit.googleapis.com/v1/accounts:resetPassword?key=${firebaseWebApiKey}`
return fetch(passResetUrl, { method: 'POST', body: JSON.stringify({ "oobCode": oobCode, newPassword: "newpass" }), headers: { "Content-Type": "application/json" } }).then(async (res) => {
const resJson = await res.json()
//const email = resJson.email
console.log(resJson)
return "ok"
}).catch((errVerifyingCode) => {
console.log(errVerifyingCode)
return "error"
})
The firebaseWebApiKey can be found in your web app's Firebase configuration.
If by decode you meant get user auth object then I don't think that's possible. Consider adding some sort of query param in your generated url that'll help your server identify the user.
If you verify the oob code on server instead of using Firebase Client SDK as shown above you'll get the user's email back in response.

How can I fix IPC error "Error invoking remote method, an object could not be cloned" in Electron?

The whole error message is the following:
Error: Error invoking remote method 'MY-IPC-CHANNEL': Error: An object
could not be cloned. at EventEmitter.o.invoke
(electron/js2c/renderer_init.js:71)
The electron/js2c/renderer_init.js:71 line is not my original line of code, but a compiled one.
I'm trying to send a POST request in order to get my Google access token, so that I can work with Google Drive's API. Currently I'm stuck trying to communicate between the renderer process and the main process by giving the main process the code I got from Google and making it send a POST request to the auth server. I have no problem establishing the connection but when I try to do it while sending an HTTP request I get the error above.
// ******* MAIN *******
function exchangeCodeForAccessToken(code: string) {
const clientID = "My Google client ID";
const clientSecret = "My Google client secret";
const body = {
code: code,
client_id: clientID,
client_secret: clientSecret,
redirect_uri: "http://localhost:4000",
grant_type: "authorization_code",
};
const body2 = `code=${code}&
client_id=${clientID}&
client_secret=${clientSecret}&
grant_type=authorization_code`;
// return fetch("https://oauth2.googleapis.com/token", {
// method: "POST",
// body: body
// });
return axios.post("https://oauth2.googleapis.com/token", body);
}
Here's the main handle:
// ******* MAIN *******
ipcMain.handle(
OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL,
async (event, code: string) => await exchangeCodeForAccessToken(code)
);
And the renderer invoke function:
// ******* RENDERER *******
function exchangeCodeForAccessToken(code: string) {
ipcRenderer.invoke(OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL, code).then((response) => {
console.log(response);
}).catch((error) => {
//TODO Improve error handling
console.log(error);
});
}
I tried sending the request through the net module from Electron. I also tried with the electron-fetch module, which is supposed to be an Electron integrated version of Node's fetch module. And finally I tried with the axios module, but it kept throwing the same error. I thought it had something to do with object serialization through IPC but then I tried just using the function without returning its promise and the same error kept popping up. Which means that the error is not only appearing when the promise is being returned but whenever the HTTP request function is being called. I also tried sending the request with both the object version of the request and its string version, hence the body and body2.
I don't know what I'm missing, and I'm so close to integrating Google login into my desktop app.
I thought it had something to do with object serialization through IPC but then I tried just using the function without returning its promise and the same error kept popping up.
It is an IPC error. You're returning the full response object, which presumably contains some properties with methods and/or other non-cloneable values. You need to make sure that the returned value can be cloned and sent to the renderer, for example:
ipcMain.handle(
OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL,
async (event, code) => {
const response = await exchangeCodeForAccessToken(code);
const {status, data} = response;
return {status, data};
}
);
I'm not sure how you called the function in your attempt to fix this, but I just ran this in Electron and it works without issues.
EDIT: Assuming response is coming from a fetch call (use response.json() if the data is JSON):
ipcMain.handle(
OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL,
async (event, code) => {
const response = await exchangeCodeForAccessToken(code);
const data = await response.text();
return data;
}
);

Node.js Axios HTTP request with bearer token returning undefined

I've seen many posts on Stack that are close to what I need, but don't fully answer my question (I'm pretty green with Node). I'm working on connecting a Twitch/Tiltify donation campaign to a Raspberry Pi via Node.js/Axios. I'd like the Pi to regularly check for new donations, then activate physical circuits (solenoid valves etc.) to be viewed live on the stream. Here's my code so far:
const axios = require('axios');
axios.get('URL_GOES_HERE', {
headers: {
'Authorization' : 'Bearer MY_TILTIFY_ACCESS_TOKEN'
}
})
.then(response => {
console.log(response.data.url);
console.log(response.data.explanation);
})
.catch(error => {
console.log(error);
});
I assume that MY_TILTIFY_ACCESS_TOKEN is the access token I generated from within my Tiltify account. I'm confused, however, about what value to put in URL_GOES_HERE. The somewhat sparse Tiltify API docs give two possible URLS: https://tiltify.com/oauth/authorize and https://tiltify.com/oauth/token. Or am I supposed to put my bearer credentials directly into the URL of a useful request, like https://tiltify.com/api/v3/user? I've tried all three, and I just get undefined undefined in the console.
A nudge in the right direction is appreciated! Thanks for your time.
#benstepp over on Github ultimately answered my question. Here's the code he provided:
const axios = require('axios');
axios.get('https://tiltify.com/api/v3/campaigns/MY_CAMPAIGN_ID/rewards', {
headers: {
'Authorization' : 'Bearer MY_API_TOKEN'
}
})
.then(response => { // this is an axios response object (https://github.com/axios/axios#response-schema)
//console.log(response.data); // this is the response body from tiltify (https://tiltify.github.io/api/endpoints/campaigns-id-donations.html)
//console.log(response.data.data); // this is the .data property of our responses
response.data.data.map((reward) => {
// the name/amount of the recent donations
console.log(`${reward.name}`)
})
})
.catch(error => {
console.log(error);
});
The /authorize endpoint is used for the Web Server OAuth Authentication Flow and User-Agent OAuth Authentication Flow.
The /token endpoint is used for the Username-Password OAuth Authentication Flow and the OAuth Refresh Token Process.
So first you need to get Authorized to be able to use Tiltify api. For that you need to use either of the flow
https://tiltify.com/oauth/authorize
https://tiltify.com/oauth/token
Assuming you used token route, you will get a response something like this:
{ "access_token":"token", "token_type":"bearer", "refresh_token":"refresh_token" }
Then using the access_token you got from the response you will call the api routes so in URL GOES HERE will be your api routes like
/campaigns/:id
causes/:id
with which you'll use Authorization: Bearer <access_token> in headers

How to include access-token in the HTTP header when requesting a new page from browser

The similar question was asked by someone else (here) but got no proper answer. Since this is basic and important for me (and maybe for someone else as well), I'm trying to ask here. I'm using Node.js+Express+EJS on the server side. I struggled to make the token authentication succeeded by using jsonwebtoken at the server and jQuery's ajax-jsonp at the web browser. Now after the token is granted and stored in the sessionStorage at the browser side, I can initiate another ajax request with the token included in the request header, to get the user's profile and display it somewhere in the 'current' page. But what I want is to display a new web page to show the user's profile instead of showing it in the 'current' page (the main/index page of the website). The question is:
How to initiate such an HTTP GET request, including the token in the HTTP header; and display the response as a new web page?
How the Node.js handle this? if I use res.render then where to put the js logic to verify the token and access the DB and generate the page contents?
Or, should we say the token mechanism is more suitable for API authentication than for normal web page authentication (where the web browser provides limited API)?
I think the answer to this question is important if we want to use the token mechanism as a general authentication since in the website scenario the contents are mostly organized as web pages at the server and the APIs at the client are provided by the browser.
By pure guess, there might be an alternative way, which the ajax success callback to create a new page from the current page with the response from the server, but I have no idea of how to realize that as well.
By calling bellow code successfully returned the HTML contents in customer_profile.ejs, but the client side ajax (obviously) rejected it.
exports.customer_profile = function (req, res) {
var token = req.headers.token;
var public_key = fs.readFileSync(path.resolve() + '/cert/public_key.pem');
var decoded = jwt.verify(token, public_key);
var sql = 'SELECT * FROM customer WHERE username = "' + decoded.sub + '"';
util.conn.query(sql, function (err, rows) {
if (!err) {
for (var i = 0; i < rows.length; i++) {
res.render('customer_profile', {customer_profile: rows[i]});
break;
}
}
});
};
I am trying to find a solution to this as well. Please note, I am using Firebase for some functionality, but I will try to document the logic as best as I can.
So far what I was able to figure out is the following:
Attach a custom header to the HTTP request client-side
// landing.js - main page script snippet
function loadPage(path) {
// Get current user's ID Token
firebase.auth().currentUser.getIdToken()
.then(token => {
// Make a fetch request to 'path'
return fetch(`${window.location.origin}/${document.documentElement.lang}/${path}`, {
method: 'GET',
headers: {'X-Firebase-ID-Token': token} // Adds unverified token to a custom header
});
})
.then(response => {
// As noted below, this part I haven't solved yet.
// TODO: Open response as new webpage instead of displaying as data in existing one
return response.text();
})
.then(text => {
console.log(text);
})
.catch(error => {
console.log(error);
});
}
Verify the token according to your logic by retrieving the corresponding header value server-side
// app.js - main Express application server-side file
// First of all, I set up middleware on my application (and all other setup).
// getLocale - language negotiation.
// getContext - auth token verification if it is available and appends it to Request object for convenience
app.use('/:lang([a-z]{2})?', middleware.getLocale, middleware.getContext, routes);
// Receives all requests on optional 2 character route, runs middleware then passes to router "routes"
// middleware/index.js - list of all custom middleware functions (only getContext shown for clarity)
getContext: function(req, res, next) {
const idToken = req.header('X-Firebase-ID-Token'); // Retrieves token from header
if(!idToken) {
return next(); // Passes to next middleware if no token, terminates further execution
}
admin.auth().verifyIdToken(idToken, true) // If token provided, verify authenticity (Firebase is kind enough to do it for you)
.then(token => {
req.decoded_token = token; // Append token to Request object for convenience in further middleware
return next(); // Pass on further
})
.catch(error => {
console.log('Request not authorized', 401, error)
return next(); // Log error to server console, pass to next middleware (not interested in failing the request here as app can still work without token)
});
}
Render and send back the data
// routes/index.js - main router for my application mounted on top of /:lang([a-z]{2})? - therefore routes are now relative to it
// here is the logic for displaying or not displaying the page to the user
router.get('/console', middleware.getTranslation('console'), (req, res) => {
if(req.decoded_token) { // if token was verified successfully and is appended to req
res.render('console', responseObject); // render the console.ejs with responseObject as the data source (assume for now that it contains desired DB data)
} else {
res.status(401).send('Not authorized'); // else send 401 to user
}
});
As you can see I was able to modularize the code and make it neat and clear bu use of custom middleware. It is right now a working API returning data from the server with the use of authentication and restricted access
What I have not solved yet:
As mentioned above, the solution uses fetch API and result of the request is data from server (html) and not a new page (i.e when following an anchor link). Meaning the only way with this code now is to use DOM manipulation and setting response as innerHTML to the page. MDN suggests that you can set 'Location' header which would display a new URL in the browser (the one you desire to indicate). This means that you practically achieved what both, you and I wanted, but I still can't wrap my head around how to show it the same way browser does when you follow a link if you know what I mean.
Anyways, please let me know what you think of this and whether or not you were able to solve it from the part that I haven't yet

Resources