I am trying to add SAML authentication using passport-saml for my application. I am using Angular for routing. On loading of the homepage, I check for a user through a GET request to my node server "/auth" to check req.user, if it's true, I send back logged in user data; if false, I send back an error message. I have data binding on this user data response so in the event of no user data, I still show "Log in" button.
On click of the "Log in" button, I perform a redirect to the node server "/login". This does passport.authenticate function which runs and then ties in with "/assert" when it's complete to perform a redirect.** It is at this point I am running into a problem.** My redirect always goes to "/" because I am performing a redirect and using angular routing so I dont know how to store this route say "/myPage1" or "/myPage2". NOTE: I do not want to always send back to "/myPage1", I want to be able to "Login" from any view in the SPA.
Am I going about this in the wrong way? Is this a limitation of using Angular routing?
Angular
$scope.login = function () {
window.location.href = 'http://example.com/login';
};
function getCreds() {
$http.get('http://example.com/auth', { withCredentials: true })
.then(function (response) {
$scope.creds = response.data;
});
}
Node.js
app.get('/login', passport.authenticate('saml', { failureRedirect: '/login' }));
app.post("/assert", passport.authenticate('saml', { failureRedirect: '/login' }), function (req, res) {
// console.log('session' + JSON.stringify(req.session));
// console.log("original url " + req.session.originalUrl);
if (req.session.originalUrl) {
res.redirect(req.session.originalUrl);
}
else {
res.redirect('/');
}
});
app.get('/auth', function (req, res) {
var authenticated = req.user;
if (authenticated) {
res.send(req.user);
} else {
res.send({statusCode: 401, message: 'User session is not alive'});
}
});
I have found a way to achieve the desired effect.
Before making the "Login" call, I make another api call to "/setSessionRedirectUrl" which passes the current window.location.href from angular to node server for session storage.
app.post('/setSessionRedirectUrl', function (req, res) {
req.session.samlLoginRedirectUrl = req.body.redirectUrl;
res.send();
});
Then when the assert function is being run after login, it is looking for the req.session.samlLoginRedirectUrl to redirect to. It works!
Related
I am using the library auth0/nextjs. I am trying to handle the email verification. I have access to the email_verification variable. If not verified, it will redirect to the page /please-verifiy-your-email.
At the moment I am using handleCallback method which is provided by auth0/nextjs.
Code:
const afterCallback = (req, res, session, state) => {
if (!session.user.email_verified) {
res.status(200).redirect('/please-verifiy-your-email')
}
return session;
};
export default auth0.handleAuth({
async login(req, res) {
try {
await auth0.handleLogin(req, res, {
authorizationParams: {
audience: 'https://dev-okz2bacx.us.auth0.com/api/v2/',
scope: 'openid profile email read:branding'
},
returnTo: "/dashboard"
});
} catch (error) {
res.status(error.status || 400).end(error.message);
}
},
async callback(req, res) {
try {
await auth0.handleCallback(req, res, { afterCallback });
} catch (error) {
res.status(error.status || 500).end(error.message);
}
}
});
How can I make sure, the user is still logged in, to get his email for a resend or the possibility to change his email.
He also needs the chance to do a new sign up , because when I will call api/auth/login after login with the unverified email, it will redirect automatically to /please-verifiy-your-email. I guess the session is not killed and auth0 goes back redirecting even tho I didn’t had the chance to sign up again.
Would be really cool if I get a few inputs.
It would solve the problem if I could log in the user and then it would redirect to /please-verifiy-your-email. I would be able to get his email address, name for further functions like resend verification. Right now I call api/auth/me I get:
{"error":"not_authenticated","description":"The user does not have an
active session or is not authenticated"}
I am running into an issue with Passport.js in which I want to get the currently logged in users information from a Post request and process some stuff. When I console.log(req.user) it comes up as 'undefined'. The set up and authentication all works, I can also retreive user's info using a Get request as seen from the first code snippet.
router.get('/', function(req , res){
console.log("The current logged in user is: " + req.user.first_name);
res.render('index.ejs' , {
user: req.user
});
});
^ returns user's name as expected
router.post('/testPost' ,function(req , res){
console.log("The current logged in user is: " + req.user);
res.json({
status: "success"
});
});
^returns undefined even when the user is logged in.
I have seen the same question raised here How to get req.user in POST request using passport js two years ago but there was no answer.
It's because the user might not be logged in at the time you're checking it.
To ensure that a user is logged in when accessing a route you should have a middleware that checks it for you.
You can write it as a separate module and import it in every single one of your routes if it's required.
The module:
module.exports = {
EnsureAuthenticated: (req, res, next) => {
if (req.isAuthenticated()) {
return next();
} else {
res.sendStatus(401);
}
}
};
The routes:
//Destructuring | EnsureAuth Function
const {
EnsureAuthenticated
} = require('../path/to/the/module');
//You should get your user here
router.get('/', EnsureAuthenticated, (req, res) => {
console.log(req.user)
});
My flow on front end is login page -> profile page
On the back I get the token from header like so:
app.use(verifyToken);
function verifyToken(req, res, next) {
if (req.path === '/auth/google') {
next();
}
else {
var token = req.headers.authorization;
client.verifyIdToken({
idToken: token,
audience: 'myClientId'
}).then(pay => {
res.status(200); // What to send here?
}).catch(err => {
res.status(401).json({error: err});
});
}
}
So the login page calls /auth/google which verifies the token above and then redirects to /profile page where another API call is made...except the second API call is not being called. It works if the token verification function is removed above. So I need a next() somewhere to keep things going?
Never mind, I switched out the res.status(200) with next() which continued fired the API call on the profile page.
Suppose I have a script like this, which uses a Passport authentication strategy with an Express backend. How would I use this script to actually make API function calls? I don't see any explicit examples in the linked project's documentation nor can I find anything in Passport.js's documentation. Thanks.
After passport user serialization done, every request has user field, which contains information passed to done callback of passport.serializeUser method.
app.get('/userID', function (req, res) {
if (req.isAuthenticated()) {
res.json(req.user.id);
}
res.redirect('/login');
}
Also, you have access to session
app.get('/auth/fitbit/callback',
passport.authenticate('fitbit', { failureRedirect: '/login' }),
function(req, res) {
req.session.loggedInAt = Date.now();
res.redirect('/');
});
Information stored in session available in all requests, while user is authenticated
app.get('/someroute', function (req, res) {
// call to another service
var url = 'http://superservice.com/' + req.user.id + '/' + req.session.loggedInAt
http.get(url, function (_res) {
res.send(_res.data)
});
});
I'm supposing that you know how to use passport, and you will figure it out what's the right Fitbit API endpoint (honestly, I'm don't know it). Said that, let me give an idea that might help you solve your problem:
// An awesome npm module (https://github.com/mikeal/request)
var request = require('request');
//
//
//
// An express route.
app.get('/activities', function (req, res) {
if (req.user !== null) {
// User is authenticated.
getUserActivities(req.user.id, res);
} else {
// Redirect to login the user isn't authenticated.
res.redirect('/login');
}
});
// This function will make API calls to Fitbit
// using the User ID we got from the PassportJS
// authentication process.
function getUserActivities(id, res) {
// It will request from Fitbit User activities.
request('https://api.fitbit.com/1/user/'+ id +'/activities/',
function (error, response, body) {
if (!error && response.statusCode == 200) {
// If everything goes well.
return res.send(body);
} else {
// If something wrong happens.
return res.send(error);
}
);
}
The goal of this example is to show you that you need to use PassportJS to get fitbit users ID, then use that id to make API calls to fitbit.
I have an app running in node, express, and angular. It has 2 pages, a user profile page and a login page. I want to make it so that if the user is not signed on and when the profile page is reached, I will redirect you to the login page. The problem is, somehow every time I try to redirect the user in node my Angular app doesn't receive the 302 redirect call (the response is always 200 even when I'm not sending any 200 status message responses). Any help is appreciated. Here is my code:
Node js:
// serve index and view partials
app.get('/', routes.index);
app.get('/partials/:name', routes.partials);
//authentication
app.post('/login', auth.login);
app.get('/logout', auth.logout);
app.get('/user', auth.getCurrentUser);
...
function getCurrentUser (req, res) {
var user = req.user;
if (user && user.username) {
console.log("getCurrentUser succeeded on user "+ user);
res.json({
username: user.username
});
} else {
console.log("getCurrentUser failed")
res.redirect('/login');
}
}
...
//routes in node
exports.index = function(req, res){
console.log("rendering index page");
res.render('index');
};
exports.partials = function (req, res) {
var name = req.params.name;
res.render('partials/' + name);
};
angular:
when('/login', {
templateUrl: 'partials/login',
contorller: LoginCtrl
}).
...
when('/user', {
templateUrl: 'partials/user',
controller: UserCtrl
})
function UserCtrl($scope, $http, $location, $routeParams) {
$http.get('/user').
success(function(data, status, headers){
console.log("/user succeeded with status "+status);
//redirect the user
if (status == 302){
console.log("302 received, redirecting user to "+headers().location);
$location.path(headers.location);
}
console.log("CLIENT : "+data.username);
$scope.username = data.username;
});
};
Output on server when /user page is reached:
getCurrentUser failed
GET /user 302 8ms - 40b
rendering index page
GET /login 304 80ms
rendering index page
Output on client:
/user succeeded with status 200
The 302 sent back by Express is handled by your browser, before Angular even gets its hands on it (so you can't create an interceptor for it).
A solution would be to always return a JSON object, and set some property on it that you can check for in Angular.