Node Google OAuth2 redirect_uri_mismatch - node.js

Attached is a screenshot of my google credentials window with my redirect URIs and on the right the redirectURI registered by the Nodejs Google OAuth2 client that it is sending in the request yet my the response still returns redirect_uri_mismatch
Any idea?

Just to add another reason for the uri mismatch error. If you are generating the token on the client side and trying to call getToken on the server side, the REDIRECT_URI must match the client side domain and be added to console APIs.

Turns out the problem arose because I was trying to exchange a non offline access_type provided to me via the frontend google plus js api. I could not find a way to retrieve a full offline access code from google via their popup to send to my server to exchange for a long living token.
I built my own popup redirect flow. I could not find any resources for the correct way to do a full auth with a popup without redirecting the main page so I improvised. I'm open to improvements but this works for me, anyways if anyone wants to do this also here's my workflow.
How to Set up your own popup oauth authentication flow
You will need to pass the auth url to the front end which the nodejs googleapis library makes easy with:
url = this.oauth2Client.generateAuthUrl(access_type: 'offline', scope: myScope)
Pass that to the front-end somehow, next when the user initiates the oauth button on the website it opens the popup (request 1), and at the same time sends a request (request 2) which waits for the final result
// Front end Angular function to initiate the popup
signup: function(callback) {
this.$window.open(googleOauthURL, 'Authenticate Google', 'width=600, height=600');
return this.$http.get('auth/google/awaiting').success(function(res) {
return callback(res);
});
}
On the backend here's the coffeescript code which responds to request 2. I added a 150 second timeout, which says: if we don't get a response from the user with his auth credentials within 150 seconds then close the connection. (we don't want hanging connections)
exports.awaiting = (req, res) ->
# Build a unique listener for the correct ip address
listener = 'oauthAwait-' + req.ip
# Clear any possible pre-existing listener
google.removeAllListeners(listener)
timeoutProtect = setTimeout ->
google.removeAllListeners(listener)
timeoutProtect = null
res.json
success: false
error: 'Timedout'
data: null
, timeoutLength
google.once listener, (result) ->
if timeoutProtect
clearTimeout(timeoutProtect)
res.json(result)# return the data
Next we wait on the user to authenticate the popup. When they do it will redirect to the redirect uri we specified in our dev console. Our server will get the code, make a request to google for the long living access_token, with that we have what we need.
exports.oauthCallback = (req, res) ->
listener = 'oauthAwait-' + req.ip
successCallback = (user) ->
user.success = true
# trigger the request 2 listener with the data
google.emit(listener, user)
failCallback = (err) ->
error.success = false
google.emit(listener, error)
exchangeToken(req, req.query.sessionState)
.then(doSomethingWithToken, failCallback)
.then(successCallback, failCallback)
.fin ->
# return a page that closes itself to the popup (request 1)
res.render 'oauthPopupCallback'
Here the we exchange the token then do something with it. Once we get the user we trigger the event emitter listener that we binded in the exports.awaiting section which will return our data to the application, and finally we send a simple html page to the popup with a javascript one liner that says window.close()
Then there we have a fully authenticated user access_token on the back end. This could be improved by using the web sockets if the user supported it, to replace the hanging request.
Edit
Found out there's this dandy method available on popups called window.opener which gives access to the window that opened the popup. Essentially this replaces the need for request 2 that hangs and waits for the response of the popup.
In your popup, you can have a javascript that passes the data along to the main window like:
var service = ResponseData,
callback = 'on' + service.name + 'Auth';
window.onload = function () {
window.opener[callback](service.data);
window.close();
}

Related

Page not changing onClick using useNavigate in React?

I have a very basic UI for a login page:
Upon clicking the LOGIN button, the following methods gets called:
async function loginPatient(){
let item ={username:userName, password};
let result = await fetch("http://localhost:8000/users/login",{
method:'POST',
headers:{
"Content-Type":"application/json",
"Accept":"application/json"
},
body: JSON.stringify(item)
});
alert(result);
alert("breakpoint")
result = await result.json();
localStorage.setItem("user-info",JSON.stringify(result));
nav('/patient')
}
At this point I simply want it to change the page when the button is clicked. My API returns the following information from the database:
To test I did console.log("hello world") in the first line of the function and it works
However, If I run console.log("hello world") after the let result = await fetch(...) part it does not work. How can I test this to see why it's not working ?
Here are the errors from the console:
I did not write the API and do not know how Node works yet, I am just doing the front end for this
The issue is code is never reaching after fetch line, basically request is failing, the error on console is saying the due to CORS issue, the request failed, and in your loginPatient function, you have not handled the failed case, if you just wrap your fetch call inside try/catch block, you will see your code will fall into fail block, as api failed.
You need to enable CORS on your server or backend, Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources.
You can read more about cors at:
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
Looks like your client is on some other domain or port(if your are developing locally) than your server. You need to enable CORS permission for your client url.
And if you are using express for your backend, you can check the following url to enable cors.
https://expressjs.com/en/resources/middleware/cors.html
And last thing why Postman is getting success response, because it is by passing this cors check, as Postman is making request from it's server to your direct server instead of browser.
First initialize you navigation variable as follows
const navigate =useNavigate()
then navigate to you specific route by returning you navigation variable as follows.
return navigation("/");
Happy Coding!

firebase onAuthStateChanged infinite loop node js

I'm using firebase to sign in my users on my node js app. I would like to see if the user is authentificated or not and after it redirect to the page I want (login if it not logged or dashboard).
But when I redirect user (if it not logged previously or session expires) it's looping on the same page (send redirect of the login page everytime when I'm on login page).
My function that I use actually :
function authenficated (req, res, next) {
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
console.log("connected" + " " + user.uid);
next()
} else {
console.log("disconnected")
res.redirect('/') //loop on / page
next()
}
});
}
I would like a function that provides if my user is logged or not, if it logged my node backend return to /dashboard or other pages that I want and if not it cannot access to dashboard and it return automatically to / or /login
I specify I don't use React or Vue, I use simply EJS to display my pages
Thanks for all
This function/sdk is meant for frontend applications and not backend apps. You need to the admin sdk for that. You can use cookies and the admin sdk provides a function to create cookies. After a signin you attach the cookie to the headers and it will be send by the browser on every request. If the cookie header is empty than you know the user isn't signed in. To logout a user you can add a head method to clear the cookie.
To use backend function you need to use the admin sdk. This function is a front end function (web sdk ).
You can use onAuthStateChanged on the front end and redirect them from the front end. Remember onAuthStateChanged will fire on every page load.
OR implement cookies like the previous comments.
OR
Send the id token from the client via http request (fetch or axios) and verify server side using the admin sdk. Here is the specific link. This solution would require you to load something on the front end though and then send a http request to the backend, verify, then send protected resources after.
Cookies on the other hand are sent to the backend with every request, so if no cookie is present on the page load request then obviously there is no user. Or if the below function fails then server wont send protected resources. (this is explained in the link above for cookies)
getAuth().verifySessionCookie(sessionCookie, true /** checkRevoked */)

Trying to use oauth flow in Electron desktop app (with spotify API)?

I have a React app in Electron, and I'm trying to access the spotify API using the spotify-web-api-node library. However, I'm not sure exactly how the oauth flow is meant to work inside of an Electron app... Firstly, for the redirect URL, I used this question and added a registerFileProtocol call to my file. Then I added a specific ipcMain.on handler for receiving the spotify login call from a page, which I've confirmed works with console logs. However, when I get to actually calling the authorizeURL, nothing happens?
This is part of my main.js:
app.whenReady().then(() => {
...
protocol.registerFileProtocol(
"oauthdesktop",
(request, callback) => {
console.log("oauthdesktop stuff: ", request, callback);
//parse authorization code from request
},
(error) => {
if (error) console.error("Failed to register protocol");
}
);
});
ipcMain.on("spotify-login", (e, arg) => {
const credentials = {
clientId: arg.spotifyClientId,
clientSecret: arg.spotifySecret,
redirectUri: "oauthdesktop://test",
};
const spotifyApi = new SpotifyWebApi(credentials);
console.log("spapi: ", spotifyApi);
const authorizeURL = spotifyApi.createAuthorizeURL(
["user-read-recently-played", "playlist-modify-private"],
"waffles"
);
console.log("spurl: ", authorizeURL);
axios.get(authorizeURL);
}
I'd expect the typical spotify login page popup to show up, but that doesn't happen. I'd also expect (possibly) the registerFileProtocol callback to log something, but it doesn't. What am I meant to be doing here? The authorization guide specifically mentions doing a GET request on the auth url, which is what I'm doing here...
In a desktop app it is recommended to open the system browser, and the Spotify login page will render there, as part of creating a promise. The opener library can be used to invoke the browser.
When the user has finished logging in, the technique is to receive the response via a Private URI Scheme / File Protocol, then to resolve the promise, get an authorization code, then swap it for tokens. It is tricky though.
RESOURCES OF MINE
I have some blog posts on this, which you may be able to borrow some ideas from, and a couple of code samples you can run on your PC:
Initial Desktop Sample
Final Desktop Sample
The second of these is a React app and uses a Private URI scheme, so is fairly similar to yours. I use the AppAuth-JS library and not Spotify though.

get json from electron BrowserWindow

I am trying to create a login for my app using discord's Oauth2 currently I am displaying a separate BrowserWindow for the API call since discords Oauth2 requires that the user clicks authorize. my API call returns the raw JSON of the acess_tokens. In my app's current state the separate window only displays the JSON. I need a way to get the JSON from within the window or from the request in a variable. I can't seem to find any way to access the raw content.
function createAuthWindow(){
var authWindow = new BrowserWindow({
width: 400,
height: 600,
show: false,
'node-integration': false,
'web-security': false,
icon: getFile('f','/src/asset/instance.png'),
});
// This is just an example url - follow the guide for whatever service you are using
var authUrl = 'http://localhost:3001/api/discord/login'
authWindow.loadURL(authUrl, (res) => {
console.log(res)
console.log(authWindow);
});
authWindow.show();
// 'will-navigate' is an event emitted when the window.location changes
// newUrl should contain the tokens you need
authWindow.webContents.on('will-navigate', function (event, newUrl) {
// More complex code to handle tokens goes here
console.log(event.code);
authWindow.webContents.session.webRequest.onCompleted({ urls: [newUrl] }, (details) => {
// Access request headers via details.requestHeaders
// Access response headers via details.responseHeaders
console.log(authWindow.webContents.code)
});
});
Sounds like your auth URL is not correct, and that you should be sending an Authorization Code Flow message, so that you can get tokens back to your app.
The usual technique for a desktop app is to:
Format the Authorization Redirect URL
Open this URL in the System Browser, which will handle redirects for you
Receive the response via a Private URI Scheme or Loopback Notification
Swap the authorization code for tokens, which your app can then use to call APIs
The redirect URL will be a value something like this, though I have not used Discord as a provider, so this may not be 100% right:
https://login.discord.com/oauth2/v2.0/authorize?
client_id=y792f434f
&response_type=code
&redirect_uri=com.mycompany.myapp:/callback
&scope=...
&state=...
&code_challenge=...
&code_challenge_method=S256
If it helps I have a couple of blog posts on OAuth for Electron Desktop Apps. It is a tricky flow to implement though ...
Initial Desktop Code Sample
Final Desktop Code Sample

Using spotify-web-api-node to generate an authentication token

I am new to using nodejs and am working on a project where I can make a custom playlist by adding one song at a time via a search. I've been able to get the code to do the searching and grabbing the proper ids done, but when trying to add to the playlist, I'm getting an error about the scope being wrong. Long story short, I was doing the wrong type of authentication.
So I read up on the spotify-web-api-node documents, but I'm getting lost between generating the authorization url and then getting the response, which is then used by another method to get the authorization token. I'm not sure if there is another method I'm not seeing that will make the request, or if I'm just supposed to do a regular request out via normal node methods.
The code I'm using is pretty much a copy-paste from the following link (https://github.com/thelinmichael/spotify-web-api-node#authorization), where the second box with the header "The below uses a hardcoded authorization code..." is where I'm lost... I need to get that code from the response, but I'm not sure how I'm to send the request to even get the response, the createAuthorizeURL method just seems to make the actual url but not send it.
I believe the confusion stems from the way the Authorization Code flow works, and the way I've written the documentation for the node wrapper. The purpose of the createAuthorizeURL method is to help you create the URL that you need to forward the user to.
From the same piece of documentation that you linked to:
In order to get permissions, you need to direct the user to our Accounts service.
Generate the URL by using the wrapper's authorization URL method.
So let's say that the user starts out by entering your site, http://www.jd.example.com. It'll have a Spotify styled button that says Login here. The button links to the URL that the createAuthorizeURL has generated. One very important part of the URL is the redirect_uri query parameter. For example, the URL that you would generate would look something like
https://accounts.spotify.com:443/authorize?client_id=5fe01282e44241328a84e7c5cc169165&
response_type=code&redirect_uri=https://www.jd.example.com/callback&
scope=playlist-modify-public
When the user clicks the button they will be taken through the authentication and authorization flow on Spotify's site (accounts.spotify.com/). However, when they've finished this flow, they will be directed by Spotify to the same redirect_uri that you gave in the createAuthorizeURL, e.g. https://www.jd.example.com/callback.
This means that your web server (e.g. Express) needs to be able to handle a request to the redirect_uri. If your web server was indeed Express, it may look like this.
/* Some express.js setup here */
/* Some spotify-web-api-node setup here */
/* Handle authorization callback from Spotify */
app.get('/callback', function(req, res) {
/* Read query parameters */
var code = req.query.code; // Read the authorization code from the query parameters
var state = req.query.state; // (Optional) Read the state from the query parameter
/* Get the access token! */
spotifyApi.authorizationCodeGrant(code)
.then(function(data) {
console.log('The token expires in ' + data['expires_in']);
console.log('The access token is ' + data['access_token']);
console.log('The refresh token is ' + data['refresh_token']);
/* Ok. We've got the access token!
Save the access token for this user somewhere so that you can use it again.
Cookie? Local storage?
*/
/* Redirecting back to the main page! :-) */
res.redirect('/');
}, function(err) {
res.status(err.code);
res.send(err.message);
}
});
});
Hope this helps!

Resources