Respond to User with CSS after failed authentication. (Passport) - node.js

I have a site that allows users to login by clicking login which will popup a Bootstrap modal without redirecting the user. I see on many sites that if you fail to login you will remain on that route and all that will change is a message will appear notifying you of the failure to login. I would like to do this with passport local where after a failed authentication I can simply show some CSS while still having the Bootstrap modal up.
However, all that I can seem to do is show a req.flash and it seems that I have to redirect after a failed authentication or be shown an error.
router.post("/login", passport.authenticate("local",
{
successRedirect: "/profile",
failureRedirect: "/",
failureFlash : true
})
);
Currently, this is all I can do. Is it possible to do it the way I've mentioned without redirecting? Passport documentation shows no sight of this.

Of course, you can do whatever you want. From the official docs:
app.post('/login',
passport.authenticate('local'),
function(req, res) {
// If this function gets called, authentication was successful.
// req.user contains the authenticated user.
res.redirect('/users/' + req.user.username);
});
"By default, if authentication fails, Passport will respond with a 401 Unauthorized status, and any additional route handlers will not be invoked. If authentication succeeds, the next handler will be invoked and the req.user property will be set to the authenticated user."
So all you have to do is handle the 401 response in your client-side logic and then based on it show a message/CSS.

Related

when does the passport.js failure redirect gets hit?

I have been using passport.js to perform OAuth. So the redirect URI gets hit after successful authentication,
However for the failure scenarios. I am not able to trigger it anyway,
What happens in the following scenarios?
1. When the google/twitter or any OAuth provider opens their sign-in page but the user doesn't respond at all / closes the browser window /, how do I hit the failure URL/redirect URL after a timeout.
2. There isn't any cancel button to abort the Google OAuth sign-in, I expect at least then failure gets called.
Basically, all I wanted to know is, all possible use cases/scenarios do the failure URL gets called?
Sample code for Google OAuth redirect, I believe the failure scenarios are same for all the OAuth providers.
app.get('/oauth/failure', function (req, res, next){
console.log('Query params:', req.params.provider);
console.log('failure login called');
res.send("<script> window.close()</script>");
});
app.get('/oauth/redirect/google', passport.authenticate('google', { failureRedirect: '/oauth/failure?provider=google' }), (req, res) => {
if(req.user.id){
// gets executed for successful authentication
let redirectUrl = `http://localhost:8082/auth.html?pid=${req.user.id}`
res.redirect(redirectUrl)
}
});
The above is just an example from passport-google-oauth, I beleive same is the case for any passport strategy.
If authentication fails ( the user enters wrong email or password ), the user will be redirected back to the page of your choice for another attempt.
When a user grants OAuth access, He himself hits the redirect uri and as far as I know there is no way to know if the user rejected access. If you would like to implement a failure Redirect URI you can add a window.onbeforeunload event listener and hit your failure endpoint whenever a user is about to exit your page.

Passing OAuth user data back to React via Node (Passport) authentication

I'm a little confused about the flow of data in a React application that authorizes a user through a third party OAuth provider. Right now I have a login button in React that directs to a Node server.
Log In
This directs to an Express route...
router.get('/auth/google',
passport.authenticate('google', {
session: false,
scope: ['email']
}));
that uses Passport to verify the login request.
passport.use(new GoogleStrategy({
clientID: process.env.OAUTH_CLIENT_ID,
clientSecret: process.env.OAUTH_SECRET,
callbackURL: "http://localhost:5000/api/auth/google/callback"
},
function (accessToken, refreshToken, profile, cb) {
return cb(null, profile);
}
));
The callback is set up to redirect the user back to the React application.
router.get('/auth/google/callback',
passport.authenticate('google', {
session: false,
failureRedirect: 'http://localhost:3000'
}),
function (req, res) {
// Successful authentication, redirect home.
res.redirect('http://localhost:3000');
});
This method allows me to either add a user to my DB via my Node app, or confirm the existence of the user already in my DB, and that's as far as I've seen any tutorial take it, but it seems entirely useless without the ability to send this information back to React. The point of logging in, at least in my opinion, is to have my application function specific to a user.
What I'd like to be able to do is send back a signed JWT token. Either one signed manually with my server, or the access token that passport gets from Google, either will do. As well, I'd like to send a user ID. I need this so my app can make API calls with the user ID as part of the request to protected routes, thus the need for the JWT.
It's confusing to me because without this exchange of data, the purpose of OAuth seems essentially useless, so I must be missing a vital step in the process, or I must be looking at the problem from the wrong perspective. Where or how should I be informing my React application of the details of the logged in user, and give them a token for local storage? As far as I can tell, there's no way to send package of data back with the callback URL.
I found a way, you can use eventsource for this. Check this article for more information https://www.blog.tericcabrel.com/implement-server-sent-event-in-node-js/
You can send you token as query param in your frontend app
res.redirect(`http://YOUR_FRONTEND_HOST:3000/?token=` + "Your jwt token")
So in the frontend you can retrieve the token with a useEffect then make it disappear to don't make it avalible to the user.

Loging out of Azure Passport authentication Node js

I have a node js application in which we have used azure login with passport authentication.
I have successfully logged in using azure and the application is working fine.
But, when I logged out and give the url to a page - it checks for authentication and automatically go to that page without asking for login.
Once I logged in my url contains below query string
1. session_state
2. code
3. state
4. token
Log in Code:
app.get('/login', passport.authenticate('azuread-openidconnect', { failureRedirect: '/' }), function (req, res) {
res.sendFile(path.join(__dirname+'/index.html'));
});
Logout code:
app.get('/logout', function (req, res) {
req.session.destroy();
req.logout();
res.redirect('/');
});
When i logout the page redirects to my index page. Then when i give '/login' to the url it takes me to the page without going to logging in page
Please help to get out of this...
This issue is caused by the Authorization Code Grant Flow of OAuth 2.0. Something like that there are any session on Azure AD OAuth 2.0 service. It is not the problem of passportjs or expressjs.
We can have the following simple test, visit the authentication endpoint in browser, https://login.microsoftonline.com/common/oauth2/authorize?response_type=id_token%20code&client_id=<client_id>&redirect_uri=<redirect_uri>&response_mode=query&scope=openid
You will need to fill the email and password first, after you finishing the login flow, the second time you visit the endpoint, you will not longer need to fill the email or password anymore.
We can set the url param prompt to login in the authorize endpoint to force the users to re-authenticate every time.
You can refer https://msdn.microsoft.com/en-us/library/azure/dn645542.aspx#code-snippet-3 for the details.
But in the azure passport oidcstrategy, we should modify the source code for add the param into the endpoint.
After you install the passport-azure-ad module, open the file /node_modules/passport-azure-ad/lib/passport-azure-ad/oidcstrategy.js, at Line 545 (more or less), you can find the following code snippet:
var params = {};
if (self.authorizationParams) { params = self.authorizationParams(options); }
params['response_type'] = config.responseType;
log.info('We are sending the response_type: ', params['response_type']);
params['client_id'] = config.clientID;
params['redirect_uri'] = callbackURL;
...
We can add the sentence params['prompt'] = 'login'; following the code snippet to add the support.
Any further concern, please feel free to let me know.
edit
Is there any way to prompt login only when i logged out...
I am not sure that do you mean, you want to check the user is authenticated when he visit login route, if is, do not prompt login flow?
If so, you can custom a middleware to check the authenticated. E.G.:
function checkAuthenticatedOnLogin(req,res,next){
if (!req.isAuthenticated()) {
return next();
}else{
res.send('do not need login');
}
}
app.get('/login',checkAuthenticatedOnLogin,
passport.authenticate('azuread-openidconnect',{ failureRedirect: '/login' }),
function(req, res) {
log.info('Login was called in the Sample');
res.redirect('/');
});
Logging out of your application does not mean the user is logged out of Azure. When a user logs out of your application you simply destroy the session your application has for said user. When they go to login again it will redirect them to Azure (where they're still logged, and where your application still has permission) which then instantly redirects back to your application with a token for that user. You would need to have the user log out of Azure to have them prompted again for their credentials.
1) I had this same problem, as passpor-azure documented I was executing this function to logout:
logout: function(req, res) {
req.logout();
res.redirect('/');
}
But Azure login session keeps active, so when I re-enter into my website, the autentication request will automatically be valid and no login page will be showned.
I tried #Gary Liu - MSFT suggestion to configure prompt:login option, but has #user3211705 commented, this makes login page reappear (even if I don't do logout and I have a portal.azure.com tab open), for instance when I restart my server (note: this isn't wrong, but when we are developing, one restarts the server all the time, and it gets annoying to login all the time)
2) Part of my solution came from this post:
Which suggest to invoke this url to logout user at Azure AD:
https://login.microsoftonline.com/<tennantid>/oauth2/logout
But only doing this didn't do the job for all situations.
If I have my website open in more than one window/tab browser, the user gets invalid in Azure AD by invoking this url, but I can keep using my website at the other tab (it seems like the user in that tab session keeps still active).
This is similar to have my website in one tab and on another tab with the portal.azure.com open, and do logout in portal.azure.com which invalidate my user at Azure AD.
So my final solution was a mix of both
logout: function(req, res) {
req.logout();
res.redirect('https://login.microsoftonline.com/<tennantid>/oauth2/logout');
}
This does logout my user in the request and invokes logout authentication in Azure AD.
One can still add a redirect uri at logout url param ?post_logout_redirect_uri=
/oauth2/logout?post_logout_redirect_uri=<your website>
Looking at the code for Azure xplat they don't seem to call an explicit logout function, instead they simply delete all of the relevant client side tokens.
If there are no local tokens you can't be logged in!

How to redirect user back to the previous page using passport-spotify

I am using passport-spotify - https://github.com/jmperez/passport-spotify for authentication in my web app and I am unable to redirect the user back to the previous page.
I have looked into https://github.com/jaredhanson/connect-ensure-login which talks about storing the req.url in req.session.returnTo for later use. But this isn't working b/c by the time I receive the "/callback" from spotify, req.session.returnTo is undefined.
Would appreciate any assistance.
Passport takes few parameters for handling redirection:
successRedirect: After successful login, user will redirect to given URL
successReturnToOrRedirect: After successful login, if req.session.returnTo exists, then the user will be redirected to
req.session.returnTo else to the given URL
failureRedirect: After failed login, user will redirect to given URL
So use it like this:
passport.authenticate('spotify', {
failureRedirect: '/login',
successReturnToOrRedirect: '/profile'
})

How do I redirect from a passport.js authorize callback on success?

In my Node.JS application's server code I use passport.js (v0.2.1, the most recent release is v0.2.2 but that has no significant changes from v0.2.1 - link) for authentication and I also have a number of routes that allow users to connect their social network accounts like twitter and facebook.
Since the user is already logged in, the right passport API function to use is authorize, not authenticate:
router.get('/api/connect/facebook/callback', passport.authorize('facebook-connect', {
successRedirect: '/profile?message_code=fb_accept',
failureRedirect: '/profile?message_code=fb_decline',
}));
This route is called back by Facebook after the user authorises or declines permissions on the Facebook website:
- If the user declines permission then the failure redirect is called
- If the user grants permission then the success redirect is not called, instead the next middleware server is called instead
The passport.js documentation does not describe how to handle the success case. How do I redirect after a successful authorization?
Summary
A successful authorize passes control to the next middleware which should perform the redirect itself.
Detail
Digging into the passport code, in lib/authenticator.js, the authorize prototype sets assignProperty to account:
Authenticator.prototype.authorize = function(strategy, options, callback) {
options = options || {};
options.assignProperty = 'account';
var fn = this._framework.authorize || this._framework.authenticate;
return fn(this, strategy, options, callback);
};
Because authorize is not defined by Passport framework, the authorize code falls back to using authenticate, as defined in lib/middleware/authenticate.js. In this method, the success redirect occurs only inside the callback to req.logIn, which is not called when assignProperty is truthy.

Resources