Login by facebook in angular app with loopback backend - angularjs

I'm making an angular application with strongloop loopback backend.
Also I integrating a third party login by facebook using loopback-passport module.
everything was fine in loopback-example-passport and everything is fine in my app right before the moment of redirecting to my app. User and Access-token created.
the code:
app.get('/auth/login', ensureLoggedIn('/#login'), function(req, res, next) {
console.log('LOOGED IN!!');
console.log(req.user);
res.redirect('/#auth/login');
});
works fine. But i can't understand. how to give authenticated state to my angular application.
i tried to make a controller to route '/#auth/login':
.controller('AuthCalbackCtrl', function($scope, $cookies, $location, AppAuth, $http, User, LoopBackAuth) {
//analogue of User.login responce interceptor
LoopBackAuth.currentUserId = $cookies['userId'] || null;
LoopBackAuth.accessTokenId = $cookies['access-token'] || '';
LoopBackAuth.rememberMe = false;
LoopBackAuth.save();
//asking for currentUser
User.getCurrent(function(user) {
console.log('ser.getCurrent ', user);
});
$location.path('/');
})
This code makes a request GET /api/users/2 but receives 401 error.
If I tweak the file /loopback/lob/models/user.js setting permission:
principalType: ACL.ROLE,
// principalId: Role.OWNER,
principalId: Role.EVERYONE,
permission: ACL.ALLOW,
property: "findById"
Then the request GET /api/users/2 receives 200 and everything ok.
I'm a little confused. I can`t understand how to make my angular app authenticate to loopback, although i know access-token and userId
Have anybody any ideas how to do it?

Here is a valid code.
app.get('/auth/login', function(req, res, next) {
//workaround for loopback-password
//without this angular-loopback would make incorrect authorization header
res.cookie('access-token', req.signedCookies['access-token']);
res.cookie('userId', req.user.id);
res.redirect('/#auth/login');
});
The problem is that loopback-passport signs cookie:
res.cookie('access-token', info.accessToken.id, { signed: true,
maxAge: info.accessToken.ttl });
In string it looks something like the following "s:.eBvo8bpo9Q9wnNrPjjlG%2FAcYqWkxEgNFqn%2FO54rdGwY"
But loopback-angular just copies the access-token to header.authorization, so we need to put there plain cookie.

Related

Ionic + Passport isAuthenticated() returns false

I have my app in angularJs 1.6 built with MEAN stack working well, i'm using passport for authentication.
When I decide to test it with ionic, the app itself works well (files are basically identical) but the authentication with passport is broken
I can register and login a user, but when I want to check if the user is logged by using req.isAuthenticated() on my server, it always answers false.
I think it's because when I make a request from my normal angular app, the request contains a user object with password and email, but when I do from my ionic app, the user is missing
I've spend the day working on it, any help would be great !
EDIT 1:
Sorry for not including code, it's my first experience here
My login route + my function for login
app.post('/api/login', login);
function login(req, res, next) {
//console.log(req);
passport.authenticate('local-login', function(err, user, info) {
if (err) {
return next(err); // will generate a 500 error
}
// Generate a JSON response reflecting signup
if (! user) {
return res.send({success : 'false', message : req.flash('loginMessage') });
}
req.login(user, function(err) {
if (err) { return next(err); }
//console.log(req);
return res.send({success : 'true', message : req.flash('loginMessage') });
});
})(req, res, next);
}
The problem is, req.login is executed and I get a success: true, but with the ionic/cordova app, nothing seems to be memorized
After that, when I try to check if the user is logged with this
app.get('/api/login/loggedin', function(req, res) {
res.send(req.isAuthenticated() ? req.user : '0');
});
I always get '0', I think it is because cordova/ionic app cannot use cookies (the difference between requests is also the lack of cookie from the ionic one), but I can't understand how to manage a solution that works both with my web angular app and it's ionic version (still with passport)
Solution I just found:
In fact, it was a CORS problem because I don't know exactly why but Ionic/cordova didn't put {user:...} informations in the post request
Simply add
var cors = require('cors');
app.use(cors({origin: 'http://localhost:8100', credentials: true}));
to your server, it allows req to contains informations needed
and add
{withCredentials: true}
to all of your requests that are going to be checked with isAuthenticated(). For example:
$http.get('http://localhost:8081/api/todos', {withCredentials: true});
So the request sent contains the {user:...} part
I don't exactly know why you need to authorize it both in client and server side but it works fine

Node API - How to link Facebook login to Angular front end?

Rewriting this question to be clearer.
I've used passport-facebook to handle login with facebook on my site.
My front end is in Angular so I know now need to understand whats the correct way of calling that api route. I already have several calls using Angular's $http service - however as this login with facebook actually re-routes the facebook page can i still use the usual:
self.loginFacebook = function )() {
var deferred = $q.defer();
var theReq = {
method: 'GET',
url: API + '/login/facebook'
};
$http(theReq)
.then(function(data){
deferred.resolve(data);
})
return deferred.promise;
}
or is it perfectly ok/secure/correct procedure to directly hit that URL in a window location:
self.loginFacebook = function (){
$window.location.href = API + '/login/facebook';
}
Furthermore, from this how do I then send a token back from the API? I can't seem to modify the callback function to do that?
router.get('/login/facebook/callback',
passport.authenticate('facebook', {
successRedirect : 'http://localhost:3000/#/',
failureRedirect : 'http://localhost:3000/#/login'
})
);
Thanks.
I was stacked on the same problem.
First part:
I allow in backend using cors and in frontend i use $httpProvider, like this:
angular.module('core', [
'ui.router',
'user'
]).config(config);
function config($httpProvider) {
$httpProvider.defaults.useXDomain = true;
$httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
};
The second part:
<span class="fa fa-facebook"></span> Login with facebook
This call my auth/facebook route that use passport to redirect to facebook page allowing a user to be authenticated.
If the user grant access, the callback /api/auth/facebook/callback is called and the facebook.strategy save the user with the profile data.
After saving the user, i create a special token with facebook token, id and email. This info is used to validate every time the user access to private states in the front.
My routes are something like this:
router.get('/facebook', passport.authenticate('facebook',
{ session: false, scope : 'email' }));
// handle the callback after facebook has authenticated the user
router.get('/facebook/callback',
passport.authenticate('facebook',
{session: false, failureRedirect: '/error' }),
function(req, res, next) {
var token = jwt.encode(req.user.facebook, config.secret);
res.redirect("/fb/"+token);
});
In frontend i catch the /fb/:token using a state and assign the token to my local storage, then every time the user go to a private section, the token is sent to backend and validate, if the validation pass, then the validate function return the token with the decoded data.
The only bad thing is that i don't know how to redirect to the previous state that was when the user click on login with facebook.
Also, i don't know how you are using the callback, but you need to have domain name to allow the redirect from facebook. I have created a server droplet in digitalocean to test this facebook strategy.
In the strategy you have to put the real domain in the callback function, like this:
callbackURL: "http://yourdomain.com/api/auth/facebook/callback"
In the same object where you put the secretId and clientSecret. Then, in your application in facebook developers you have to allow this domain.
Sorry for my english, i hope this info help you.
Depending on your front-end, you will need some logic that actually makes that call to your node/express API. Your HTML element could look like
<a class='btn' href='login/facebook'>Login</a>
Clicking on this element will make a call to your Express router using the endpoint of /login/facebook. Simple at that.

Session and Login User data with Node and AngularJS

I need to know that if my authentication and session management method is right.
I am using session management as when I receive successful auth. from node server. I store user data(without any trace of pass.) in $window.sessionStorage and if user marked rememberMe(checkbox), store data in $window.localStorage too.
Through this I am able to get data in different controllers. Though I read somewhere about session implementation at server(nodeJs) side is also possible. But I am not sure about how to use session along with JSONToken Authentication.
I was using
https://jasonwatmore.com/post/2015/12/09/MEAN-Stack-User-Registration-and-Login-Example.aspx
as a learning example but I could not understand it.
/app/app.js
Why is it in the run() method ?
// add JWT token as default auth header
$http.defaults.headers.common['Authorization'] = 'Bearer ' + $window.jwtToken;
and what is this:
// manually bootstrap angular after the JWT token is retrieved from the server
$(function () {
// get JWT token from server
$.get('/app/token', function (token) {
window.jwtToken = token;
angular.bootstrap(document, ['app']);
});
});
/controllers/app.controller.js
// use session auth to secure the angular app files
router.use('/', function (req, res, next) {
if (req.path !== '/login' && !req.session.token) {
return res.redirect('/login?returnUrl=' + encodeURIComponent('/app' + req.path));
}
next();
});
// make JWT token available to angular app
router.get('/token', function (req, res) {
res.send(req.session.token);
});
// serve angular app files from the '/app' route
router.use('/', express.static('app'));
So using a session server-side with JWT kind of defeats the purpose of using JWT. JWT's are awesome in a number of ways, but one of the ways they are great, is regardless which server intercepts a request, they can verify the user.
If you put it in a session, you have to make sure the client keeps going to the same server as the session is saved in memory on that machine. There are plenty of ways around that, but again it kind of defeats the purpose of a JSON web token.
What I did for my authentication with angular/node/JWT was just passed the JWT back in the header every time, and with my middleware intercepted it with:
req.header.whatever_my_tokens_name_is
The code below set the $http to send on every request the JWT Token to the server.
// add JWT token as default auth header
$http.defaults.headers.common['Authorization'] = 'Bearer ' + $window.jwtToken;
The code below get the token from '/app/token' and store it in LocalStorage. After that, it starts the angular.
// manually bootstrap angular after the JWT token is retrieved from the server
$(function () {
// get JWT token from server
$.get('/app/token', function (token) {
window.jwtToken = token;
angular.bootstrap(document, ['app']);
});
});
Here this is a middleware that check if there are no token stored in req.session.token and requested url is not '/login'. If so, send a redirect to '/login'.
// use session auth to secure the angular app files
router.use('/', function (req, res, next) {
if (req.path !== '/login' && !req.session.token) {
return res.redirect('/login?returnUrl=' + encodeURIComponent('/app' + req.path));
}
next();
});
Finally here, this is a endpoint to the client request the '/token' again from the server.
// make JWT token available to angular app
router.get('/token', function (req, res) {
res.send(req.session.token);
});
Anyway, check the #morgan-g response regarless session-side and JWT.
I hope this helps.

Sending authenticated requests to GitHub API from authorized Auth0 app user

I am having a very difficult time getting authenticated API requests to GitHub to work. I have created an authorized application in GitHub and connected it to my Auth0 account. I have no problems getting a user signed in using their GitHub account but once they are signed in I cannot make authenticated requests to the GitHub API (I am trying to set a GitHub webhook in one of the user's GitHub repos). All my requests are rejected for having incorrect credentials.
I have the JWT issued by Auth0 being sent along in each request to the GitHub API endpoint but it appears as though this is not sufficient. The Auth0 profile that comes back from my user seems to have an access_token in it, but sending this along does not work either.
Here is what my Auth0 login code looks like (using the Angular API):
angular.module('myApp').controller('LoginCtrl', ['$scope', '$http', 'auth', 'store', '$location',
function ($scope, $http, auth, store, $location) {
$scope.login = function () {
auth.signin({
authParams: {
responseType: 'token' // I think this is the default but just in case
}
}, function (profile, token) {
// Success callback
store.set('profile', profile);
store.set('token', token);
$location.path('/');
}, function () {
// Error callback
console.debug("error logging in");
});
};
}]);
This works fine. They authorize the GitHub application tied to my organization's Auth0 account with its requested permissions without issue and land back in my application and I then have access to an Auth0 profile tied to their GitHub account, but then if I try and make an authenticated request to the GitHub API on their behalf:
var username = auth.nickname;
var repo = "some_user.github.io"; // todo: get repo from setup process
var url = "https://api.github.com/repos/" + username + "/" + repo + "/hooks/";
var conf = {
name: "web",
active: true,
config: {
"url": "https://webtask.it.auth0.com/api/run/wt-my-container_com-0/echo?webtask_no_cache=1",
"content_type": "json"
}
};
$http.post(url, conf).success(function(data, status) {
console.log("post successful:");
console.log(status);
console.log(data);
});
... GitHub rejects the request, either saying the request resource doesn't exist (to prevent private data leakage) or that I supplied bad credentials, depending on different variables (if I try supplying the "access_token" field provided in their Auth0 profile as a query param or supply my Auth0 application's client secret, etc).
I have scoured the documentation of both Auth0 and GitHub trying to figure out what the correct procedure is (for example, do I need to implement the whole OAuth2 token flow myself? it seems like Auth0 should be doing that for me) but nothing I have tried so far works, and nothing on Google has pointed me in the right direction. I have tried a number of other methods of doing this without success but I don't want to make this post too much longer. Any help would be greatly appreciated.
I figured it out. There were two problems: one, a trailing slash had crept in on the end of my API call to the GitHub endpoint, which evidently breaks something and causes GitHub to reject the request, and second, I had set things up to send along the Authorization header with every request as per the Auth0 guide here: https://auth0.com/docs/client-platforms/angularjs, specifically this part:
myApp.config(function (authProvider, $routeProvider, $httpProvider, jwtInterceptorProvider) {
// ...
// We're annotating this function so that the `store` is injected correctly when this file is minified
jwtInterceptorProvider.tokenGetter = ['store', function(store) {
// Return the saved token
return store.get('token');
}];
$httpProvider.interceptors.push('jwtInterceptor');
// ...
});
But GitHub does not like that since it does not contain the token it is expecting and will reject the request if it sees it. Once I removed the trailing slash and removed the above code, everything started working as expected.
Look at this gitHub page. It is something like this with angular:
//'common' will add the headder to every request.
$httpProvider.defaults.headers.common["Authorization"] = token YOUR_TOKEN;

Authentication when using angularjs and passportjs

I am currently using passportjs for authenticaton.
I have come across a stage where i need to ensure the user is authenticated if the url is typed in the browser/ I have been using the passportja example which has the following:
app.get('/admin', ensureAuthenticated, function(req, res){
console.log('get admin');
res.render('admin', { user: req.user });
});
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login')
}
I am using angularjs for routing so my get does not work and run the ensure authenticated.
How should this be implemented?
With AngularJS, you need to restrict the display of templates to the user.
So, let us say you have the following code in AngularJS:
$routeProvider.when('/admin', {
templateUrl: '/partials/admin-page.html'
});
When the user tries the /admin route, AngularJS will then request the template /partials/admin-page.html.
Thus, in your nodeJs server, you then implement the following code:
app.get('/partials/admin-page.html', ensureAuthenticated, function (req, res) {
res.render('admin', { user: req.user});
});

Resources