I have a Sails JS application. I am trying to setup authentication using Passport.js authentication layer sails-generate-auth. I have configured my app by following the steps given in their documentation.
But when I lift my sails app, authentication is not working. I am able to access the controllers, even when I am not logged in (It's not redirecting to my login page).
I added a console.log statement in api/policies/passport.js as follows:
module.exports = function (req, res, next) {
passport.initialize()(req, res, function () {
passport.session()(req, res, function () {
res.locals.user = req.user;
console.log(req.user); // added by me
next();
});
});
};
Now, when I access controllers before login or after logout, its printing undefined. But when I am logged in, its printing my user data. Any idea why it is not checking for authentication?
I am using local authentication strategy and I have commented out all others (twitter, facebook...)
The above answer provides useful information. I want to elaborare on that.
sails-generate-auth, by default doesn't deny access to controllers if the user is not logged in. For that, you can create another policy in api/policies/. For example: create sessionAuth policy as follows:
module.exports = function(req, res, next) {
if (req.user) {
return next();
}
return res.forbidden('You are not permitted to perform this action.');
};
Instead of showing forbidden page, you can also render login page. For that you need access to AuthController.login. So, add the policies in config/policies as follows:
'*': ['passport', 'sessionAuth'],
'auth': {
'*': ['passport']
}
This helps to restrict access all the controllers except auth controllers such as login, logout and register, if the user is not logged in.
Passport doesn't have a policy to deny access to a controller. For this, you have to create another policy.
See this link for more details.
Related
If I wasn't using react and was using express(nodejs) in the backend, this is what I would do for [an extremely simplified] auth system:
//Auth Middleware
const auth = (req, res, next)=>{
if(req.session.loggin_in===true){ next()}
else{ res.redirect('/login')}
}
//Endpoints
app.get('/', auth, (res, req)=>{ res.render('homepage')})
app.get('/login', (req, res)=>{ res.render('login')})
I know that you can use react routing to redirect the user to different pages, but how can you use middleware and session variables?
Do you have to send an http request for authentication from the client side to see whether the user is logged in? If this was the case, and supposing I wasn't logged in and tried to access the home page, I would first go to the home page before being redirected to the login page.
Thanks.
I have in my backend server, which was built with Node JS, an authentication with the package "passport-azure-ad". I put the important elements of the code here in the post.
Basically, the process is that when I call the URI "localhost:3000/login", I start the authentication and (if you are not already logged in) the login window I get from Azure pops up. After the login, it routes to the callback URI.
I.e. I have the complete "login infrastructure" I need for my use case. However, now I built a frontend with Angular and unfortunately I have absolutely no idea how to best proceed to integrate the authentication I built in Node JS into my frontend (Angular)?
I want to check if there is a login when calling my frontend (localhost:4200), if not I want to route to 3000/login to trigger the Microsoft login window. I do this check in the backend with the ensureAuthenticated function. Now I wonder how to call all this from the frontend? It would be nice if someone could support me on this, any tips or any suitable sources would be greatly appreciated - thanks!
// session management
app.use(session({
resave: true,
saveUninitialized: true,
secret: process.env.COOKIE_SECRET
}));
app.use(passport.initialize());
app.use(passport.session());
// check if logged in
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login');
};
// call azures login page
app.get('/login',
passport.authenticate('azuread-openidconnect')
);
// callback uri after authentication
app.post('/auth/oauth/callback',
function (req, res, next) {
passport.authenticate('azuread-openidconnect',
{
failureRedirect: '/failure',
}
)(req, res, next);
},
function (req, res) {
res.redirect('/account');
});
// protected ressource
app.get('/account', ensureAuthenticated, function (req, res) {
res.redirect('Login succesfull!');
});
// logout
app.get('/logout', function (req, res) {
req.logout();
req.session.destroy();
res.send('Ausgeloggt!');
});
When using an SPA like Angular, you can't no longer use this kind of authentication flow by using passport strategies to authenticate to third party endpoints like Microsoft/Google.
Instead, you start the OAuth2.0 flow from your Angular application straight to the Microsoft authentication endpoint. Tokens will be used for further authentication.
Some links:
Microsoft identity platform and OAuth 2.0 authorization code flow
Sign in users and call the Microsoft Graph API from an Angular single-page application (SPA) using auth code flow
I am working on a NodeJS/express application using passportJS for sign in/sign up. I have defined a user model for all users, but want only certain users within my administration to have access to editing certain models. I was thinking of adding a boolean field, like isAdmin to determine this, but I don't know how I would verify admin users. To be specific, how would I determine when I need to generate a token for the admin user? How do I differentiate users in my administrations from ordinary users? I was thinking of having a separate locally hosted website that connects to the same database that I could use to manage models only from my computer. Would that work?
Any help would be greatly appreciated!
Thanks!
There are many option available. i can explain you some of them.
1) As you said you can define boolean field as is Admin true of false.
-> if you are using this option and you are using passport. You must get user in your request object.Before hitting api or particular endpoint you can set middleware to verify that requested user is admin or user.
file
Filename : ../services/auth.service.js
exports.isAdmin = async (req, res, next) => {
// req.user is object that you will get after successfull login. change accordingly
// Or you can check from db also. Get object logged in user from db by their email id.
// And check condition
// Check Role if admin or not
if(req.user.isAdmin) {
next(); // If verify it will redirect to next process
} else {
return res.status(401).json({
Error: true,
message: 'You are not authorized to perform this action.',
})
}
};
You can use this function as middleware.
const auth = require('../services/auth.service.js')
router.get('/*', auth.isAdmin, (req, res) => {
res.status(200).json({ message: "Hello from Admin side."})
});
Visit : https://github.com/mihir-kanzariya/Nodejs-CRUD
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.
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!