firebase onAuthStateChanged infinite loop node js - 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 */)

Related

Check session is active in vue.js middleware

So basically I have an app that is separated between backend (node.js + express + express-sessions) and frontend (vue + node.js).
When the frontend calls my backend.host/login the backend creates a session and a session cookie is set to the user. The way I do it is the following:
req.session.userId = user._id;
In the frontend, I have a pinia store where I keep all my user state machine (login, logout,...). So a user logs in and the user object is stored in the store. Then all the pages can access that object.
But now, I would like to have something similar as the backend, so I can redirect the user when the session is not active or expired. So I would like to have a middleware that checks for the session cookie, and if it's not there, redirects the user to the index page. I can't rely on the store because every time I refresh the page it gets removed.
I saw some examples using JWT which they make a middleware like this:
export default function auth({ next, router }) {
if (!localStorage.getItem('jwt')) {
return router.push({ name: 'login' });
}
return next();
}
My problem is that I am not sure how to retrieve the session cookie from there, since nor next nor router parameters are the ones I need to.

how to use passport SAML with both node (express) as backend and vue as client

I have an app with node (express) backend, and vue client.
I'm trying to add SAML SSO using passport. (makes sense to do it on the server node app).
it works perfect when used in express app. but when I applied it to a structure of express backend and vue client - it fails to make the redirection to the Idp.
when user enters my login page, vue client (Login.vue) calls node backend for verifying the user. (api verifyuser)
node call passport.authenticate('saml', ...) and I expected a response I can send back to the vue function that called me, and there, in Login.vue - to make the redirection.
but here comes the problem:
in the backend node app, the redirect response is sent after my code is executed, inside passport strategy. So it is sent automatically to the browser, not returning to the vue script that called this node api.
So the redirection is done in the background, the user don't see any redirect. the original login page is still shown.
And my vue function gets the response back from the API - only after the browser sends the redirect (in the background) to the IDP, and gets the login html page response from the IDP.
So the data I get back - is an html of the IDP login page, instead of a redirection data.
How can I solve it?
I'm new to client technologies and js and node including, so I really don't know how such a flow should be handled. searching 3 days for solution.
Thanks a lot for you assistance!
here is my snippets of code:
Login.vue:
<input class="button wide cropBottom io-mango ae-5 margin-top-0 toRight" v-on:click="userLogin" type="button" value="Log In"/>
...
userLogin: function() {
...
...
$(".overlay").show();
this.$http.post(process.env.BASE_URL + "verifyuser", oUser) //call backend node express app
.then(function(data) {
...
here I gets only an html login page which the IDP sent as a response to the redirect with the SAML Request.
}
Backend node express app:
verifyuser.js:
module.exports = function (app) {
app.post('/verifyuser', (req, res, next) => {
var SamlStrategy = passportSaml.Strategy;
passport.use(new SamlStrategy(
{ ...
});
passport.authenticate('saml', {session: false}, function (err, user, info) {
...
})(req,res,next);
//tried to get the redirect here, but res still don't have it. only after this function is called, the SAML request is created from the req, and the location url is made.
});
I've found a solution.
I changed the Vue client:
instead of calling the server using ajax, and expecting a data response to come back,
I called the server using post of a form.
that way, the browser redirects to the server when I call it, and when the passport library in the server returns a redirect response- it is done in the forground, and the user can see it.
In Single logout, passport have done a better job:
the passport API just returns the logout request created.
then I can decide myself if I want redirect from the server, or I want to send the redirection request to the waiting client function - and do the redirection from there.

How can I redirect a page from an external Node/Express server

I have a React/Node/Express web app up and running with two separate servers. React is running on localhost:8080 and proxying API requests to my node server running on localhost:3000. This will be my setup for production, so if possible, I would like to leave this structure intact.
The problem I'm running into is part of my API needs to redirect the user to a page to grab a token from the Spotify API, and then redirect back to my correct page upon successful authentication.
The API call proxy is being done like this:
const axios = require('axios');
const axiosInstance = axios.create({
baseURL: process.env.NODE === 'production' ? '' : 'http://localhost:3000'
});
module.exports = axiosInstance;
Up to this point, this works great, as I can keep my React and Node server completely separate.
As I mentioned above, I need to redirect my React front-end through an API call on my back-end, to a different page (Spotify authentication) to authenticate users, which then Spotify redirects back to my site after a user allows or disallows access to their Spotify accounts.
On my front-end - localhost:8080, the user clicks a button which calls this function.
authenticate = () => {
axios.get('/login')
.then(data => console.log(data));
}
Which calls this endpoint on localhost:3000.
router.get('/', (req, res) => {
let scopes = 'user-read-private user-read-email';
let client = process.env.SPOTIFY_CLIENT;
let redirect = 'http://localhost:3000/login/redirect';
res.redirect(`https://accounts.spotify.com/authorize?response_type=code'
&client_id=${client}${scopes ? `&scope=${encodeURIComponent(scopes)}` : ''}
&redirect_uri=${encodeURIComponent(redirect)}&state=${state}`);
});
When I click the login button on my front-end, I get this response, which I believe to be from Spotify as I can successfully make cross-origin requests to other endpoints in my API.
Failed to load... No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
However, when I manually navigate to localhost:3000/login, the redirect works and I'm sent to the Spotify authentication page.
In addition, I tested trying to redirect my front-end to any url, and it doesn't work.
So my question is, how can I redirect my front-end through an API call from a server that doesn't also serve my static front-end content?
The reason this isn't working is because your axios.get('/login') is an AJAX request to your server which your server is then telling the AJAX request to be redirected to Spotify, but Spotify doesn't have your front-end server registered so you get the CORS error, since your request was initiated while on the front-end server URL.
Your AJAX request is following that redirect that was given, not trying to load your browser on http://localhost:3000, which would then redirect it to the Spotify authorization page.
Now, rather than doing:
authenticate = () => {
axios.get('/login')
.then(data => console.log(data));
}
You could do:
authenticate = () => {
window.location.href = "http://localhost:3000"
}
This would direct your front-end application to visit the URL of your API server and follow the redirect immediately to Spotify and then you could authorize and be redirected back to your API server, which could then redirect back to your front-end application.
Truthfully, this is a bit overkill, since presumably your React application and node application look like they would be running on the same domain outside of development, given the relative URL's you're using. If you're using create-react-app, you should follow their instructions on configuring your proxy via this or manually via this.
tl;dr you need to tinker with how your proxy is configured or change from doing an AJAX request there to actually going directly to /login (not doing an AJAX request).

Node Google OAuth2 redirect_uri_mismatch

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();
}

redirect to another app with session token (jwt) in AngularJS and NodeJS

I have a startup module in angularjs. This module is just to login and have public information (login, prices, newsletter...). I have many roles and for each role, i have an app (angular module). I made this architecture because i have complex module for each role and it was impossible to put all roles in one module.
So, for login, i use jsonwebtoken in node like this :
var token = jwt.sign(user, config.secureToken, { expiresInMinutes: 20*5});
res.json({ token: token, user: user });
It works perfectly. I can login into my app. After that, i have to propose a list of roles to redirect to the right module.
In angular, I have AuthHttp service that adds security headers (with token) to call rest service with $http.
How can i redirect to 'mydomain:port/anotherModule' with $location or $http ?
With this code in nodejs :
app.get('/secondModule', expressJwt({secret: config.secureToken}), function (req, res) {
res.render('restricted/secondModule/index.html');
});
NodeJs sends an html code in response and does'nt redirect...
And if i do this in my angular controller :
location.href = route;
i have this result on nodejs console :
Error: No Authorization header was found
I am not sure about the libraries you are using, but issue seems that you are loosing the token because you navigate to a altogether new page.
Based on your auth library you need to pass the token that you get after auth from one page to another.
The options here are to either use browser sessionStorage or querystring to pass the token along and at it back to the http header collection on the new page (module)
This is an old post but I recently took a long time to figure this out. I may be wrong but I believe nodeJS/expressJS can't read the token from the session storage. I believe you will need to pass the token via the request header using AngularJS.
This depends on the front end that you are using. For me, I am using AngularJS and I have to do something like this.
angular.module('AngularApp').factory('authFactory',
function($window){ //the window object will be able to access the token
var auth = {};
auth.saveToken = function(token){
$window.localStorage['token_name'] = token; //saving the token
}
auth.getToken = function(){
return $window.localStorage['token_name']; //retrieving the token
}
return auth;
}
.service('authInterceptor, function(authFactory){
return { headers: {Authorization: 'Bearer "+ authFactory.getToken()}
} //the last line gets the retrieved token and put it in req.header
Then, you just need to include 'authInterceptor' in all the http methods when you communicate with the backend. This way, nodeJS will be able to pick up the token.
You can see the Authorization field in req.header if you use the chrome developer tool and look at the Network tab. Hope this helps.

Resources