I would like to use basic authentication for my API routes but also allow users to access the API in the browser through the local authentication strategy. My middle-ware looks like this:
router.get("/Login", (req: Request, res: Response, next: NextFunction) => {
let test = req.flash("loginMessage");
res.render("Login", { message: test });
});
// Local authentication for user login
router.post(
"/Login",
passport.authenticate("local-login", {
failureRedirect: config.urlExtension + "/Login", // redirect back to the signup page if there is an error
failureFlash: true, // allow flash messages
})
);
// Basic authentication for API routes
router.all("/api/*", passport.authenticate("basic", { session: false }));
router.all("*", connectensurelogin.ensureLoggedIn(`${config.urlExtension}/Login`))
So for the API authentication route I'd like to bypass basic authentication if local authentication has already been achieved by login.
I found that you can return a call to the authentication route from inside a outer route with this conditional like so:
// Basic authentication for API routes
router.all("/api/*", (req, res, next) =>
req.isAuthenticated()
? next()
: passport.authenticate("basic", { session: false })(req, res, next),
);
Related
I'm working on a notes app , where people can keep their notes save (using express).
I want to add google authentication and for that I'm using passport.
My routs are -
/notes/:userId => for home page
http://localhost:8080/login/google/redirect => Authorized redirect URL
I'm also using a middleware isLoggedIn for checking if the user is loged in or not.
My middleware code -
module.exports.isLoggedIn = (req, res, next) => {
if(!req.user ){
req.flash('error', 'User must be signed-In');
return res.redirect('/login');
}
next();
}
In this I'm checking if the req have user property which passport atomatically adds while login using passport.autheticate() .
But now when i'm login using Google I need to use a fixed redirect URL. So how i redirect user to notes/:userId after authentication.
I tried using req.redirect in my redirect URL
router.get("/login/google/redirect", passport.authenticate('google', {failureRedirect: '/register'}),
async (req, res) => {
let userId = req.user._id;
res.redirect(`/notes/${userId}`);
});
but can't able to pass my middleware isLoggedIn.
How can I make this possible ?
Use isLoggedIn as /notes/:id route's middleware.
router.get("/notes/:id" , isLoggedIn, async (req, res) => {
// your logics code
});
I am developing a nodejs application in which I want to use Microsoft AD oath2 flow. I looked into Microsoft's official documentation here and found that they list two libraries -
msal-node
passport-azure-ad
I need help selecting the library which will best serve my needs. I need to -
perform authentication
protect some apis via token verification
expose all the apis so that they can be tested externally
call external web apis within my app
Also, the documentation says that "Acces web apis" is not possible with passport-azure-ad. What do they mean by this?
I've used passport-azure-ad to handle Azure SSO in a complex login service which involved many different authentication protocols.
It worked perfectly fine to establish the client credential flow, in which the session is managed by the back-end.
The msal-node library is better to implement an implicit flow, in which an SPA gains access to a token which can then be used to obtain data from the identity provider.
Another way around it would be to use passport-azure-ad to handle the login, and then tie that in to a custom JWT generation system.
That would end up looking something like this:
const passport = require('passport');
const OIDCStrategy = require('passport-azure-ad').OIDCStrategy;
const Token = require('Token.js');
settings = {
azureDirectoryId: '',
azureClientId: '',
azureClientSecret: '',
callbackUrl: 'https://example.com/auth/callback',
}
passport.use(new OIDCStrategy(
{
identityMetadata: `https://login.microsoftonline.com/${settings.azureDirectoryId}/.well-known/openid-configuration`,
clientID: settings.azureClientId,
responseType: 'code id_token',
responseMode: 'form_post',
redirectUrl: settings.callbackUrl,
clientSecret: settings.azureClientSecret,
// ...additional settings if needed
},
(req, iss, sub, profile, accessToken, refreshToken, done) => {
process.nextTick(async () => {
profile.accessToken = accessToken;
profile.refreshToken = refreshToken;
profile.token = Token.generate(profile);
done(null, profile);
});
},
));
router.get('/auth/login', (req, res, next) => {
passport.authenticate('azuread-openidconnect', {
response: res,
resourceURL: null,
customState: Math.random().toString(36).substr(2, 10),
failureRedirect: '/failure',
})(req, res, next);
});
router.use('/auth/callback', (req, res, next) => {
passport.authenticate('azuread-openidconnect', {
response: res,
successRedirect: '/success',
failureRedirect: '/failure',
})(req, res, next);
});
router.get('/auth/success', (req, res, next) => {
if (req.user.token) {
res.cookie('token', req.user.token);
return res.redirect('https://example.com');
}
failure(req, res);
});
const failure = (req, res) => {
req.session.destroy(() => {
res.clearCookie('token', cookieSettings);
res.redirect('https://example.com');
});
};
router.get('/auth/failure', failure);
This way, sending the user to https://example.com/auth/login would start off the process of authentication via passport. Once completed, you'd generate a JWT with the information returned from the identity provider (Token.generate(profile);), store that token as a cookie, and redirect the user back to the homepage.
I am developing a web app where after login according to type of user the page should redirect. type of user is defined by user at the time of sign-up. passport local authentication is working fine but what I have to do for redirecting the page according to usertype.. please guide
If the passport is working you could probably get that information off of the request and then use res.redirect('/whereYouWant'). Post some code? check out this answer for info on looking at the cookie: How to access Cookie set with Passport.js
Why not redirect to a page which takes of any redirections for you? Example:
router.get('/login', function(req, res) {
res.render('login', { user : req.user, status: req.param('status') });
});
router.post('/login', passport.authenticate('local', {
successRedirect: '/redirect',
failureRedirect: '/login?status=fail',
}));
router.get('/redirect', function(req, res) {
if (req.isAuthenticated()) {
if (req.user.usertype === "admin") {
res.redirect('/adminpage');
}
if (req.user.usertype === "marketing") {
res.redirect('/marketingpage');
}
......
}
});
I'm building an API with Node.js, and I have some endpoints I want to secure.
For simplicity let's assume I'm using HTTP basic authentication (passport-http) for all of my endpoints.
What I'd like to do on top of that, is to make sure that a route like this: api.example.com/users/:uid/ is only accessible by a user with that ID.
I can do it with something like this:
app.get('/users/:uid',
passport.authenticate('basic', {
session: false
}),
function (req, res, next) {
if (req.params.uid !== user.id) {
return next(new Error('Unauthorized'));
}
return next();
},
function (req, res, next) {
// do secret stuff
}
);
But I wonder if there's a way to do this without adding additional middleware, by using Passport itself:
app.get('/users/:uid',
passport.authenticate( ??? ),
function (req, res, next) {
// do secret stuff
}
);
Is it possible? If not, is there a better way?
You can try something perhaps like this. General description: authenticate all requests that hit anything under the /users route as requiring authentication. On your specific route, use some middleware that makes sure that the user trying to access the specific route is the one in the route itself via that uid.
function authorizeUser(req, res, next) {
if (req.user.uid !== req.params.uid) next(new Error('Not your profile!'));
next();
}
// Require login for entire /users section
app.use('/users', passport.authenticate('basic', { session: false }));
// Authorize /users/:uid section to one user
app.use('/users/:uid', authorizeUser);
// Nested routes will all be secured by the middleware above.
app.get('/users/:uid', function (req, res) {
// Secret stuff
});
app.get('/users/:uid/foo/bar', function (req, res) {
// Also secret
});
If you're only securing one endpoint, you can just put it all on the same route.
I'm using Express and Passport OpenID Google strategy and I would like to set returnURL on each auth request to be able to return to the page that initiated that auth.
The situation is that I have HTML5 slides application with Node.js backend (and with social stuff and editor and Portal and extensions... https://github.com/bubersson/humla) and I want be able to log in user on some slide (via slide menu...) but then I want him to get back to same slide easily.
So I would need something like this?
app.get('/auth/google', function(req,res) {
var cust = "http://localhost:1338/"+req.params.xxx;
passport.authenticate('google', returnURL:cust, function ...
}
I've read Passport's guide, but still don't know how to do that. I know this wouldn't be safe, but how else could I do it?
Or how can I make the application to return to the page from where the login has been initiated? Or is there a way to make OpenID authentication using AJAX (and still be able to use passport as well)?
I've figured this out for my apps Twitter authentication, I am sure that the GoogleStrategy is quite similar. Try a variant of this:
Assuming you have defined the route for the callback from the authentication service like so (from the passport guide):
app.get('/auth/twitter/callback',
passport.authenticate('twitter', {
successRedirect: authenticationRedirect(req, '/account')
, failureRedirect: '/'
})
);
Just change that block to this:
app.get('/auth/twitter/callback', function(req, res, next){
passport.authenticate('twitter', function(err, user, info){
// This is the default destination upon successful login.
var redirectUrl = '/account';
if (err) { return next(err); }
if (!user) { return res.redirect('/'); }
// If we have previously stored a redirectUrl, use that,
// otherwise, use the default.
if (req.session.redirectUrl) {
redirectUrl = req.session.redirectUrl;
req.session.redirectUrl = null;
}
req.logIn(user, function(err){
if (err) { return next(err); }
});
res.redirect(redirectUrl);
})(req, res, next);
});
Now, define your middleware for authenticated routes to store the original URL in the session like this:
ensureAuthenticated = function (req, res, next) {
if (req.isAuthenticated()) { return next(); }
// If the user is not authenticated, then we will start the authentication
// process. Before we do, let's store this originally requested URL in the
// session so we know where to return the user later.
req.session.redirectUrl = req.url;
// Resume normal authentication...
logger.info('User is not authenticated.');
req.flash("warn", "You must be logged-in to do that.");
res.redirect('/');
}
Works!
Wherever you have your login button, append the request's current URL as a
query parameter (adjust for whatever templating system you use):
<a href='/auth/google?redirect=<%= req.url %>'>Log In</a>
Then, add middleware to your GET /auth/google handler that stores this value in
req.session:
app.get('/auth/google', function(req, res, next) {
req.session.redirect = req.query.redirect;
next();
}, passport.authenticate('google'));
Finally, in your callback handler, redirect to the URL stored in the session:
app.get('/auth/google/callback', passport.authenticate('google',
failureRedirect: '/'
), function (req, res) {
res.redirect(req.session.redirect || '/');
delete req.session.redirect;
});
Try res.redirect('back'); in the callback for passport.authenticate
According to the author this isn't possible with OpenID strategies. We managed to update these dynamically by directly accessing the variables:
app.get('/auth/google', function(req, res, next) {
passport._strategies['google']._relyingParty.returnUrl = 'http://localhost:3000/test';
passport._strategies['google']._relyingParty.realm = 'http://localhost:3000';
passport.authenticate('google')(req, res, next);
});