I'm very new to angular, so my knowledge is based on tutorials and even then I don't succeed.
I need to authenticate using a google account. That works, I get a token where my api calls could be authorized with. But after login the pop up window should dismiss and I should be redirected to the homepage. This doesn't work.
this is my controller
angular.module('MyApp').controller('loginController', ['$scope', '$auth', '$location','loginService', loginController]);
function loginController($scope, $auth, $location, loginService) {
$scope.authenticate = function(provider) {
$auth.authenticate(provider).then(function(data) {
loginService.saveToken(data.data.token);
console.log('You have successfully signed in with ' + provider + '!');
$location.path('http://localhost/#/home');
});
};
};
in app.js I have my configuration. this is not my work but a friend who is an intern as wel as me, he is responsible for a mobile application, where he uses the same function to get his token, and it works.
authProvider.google({
clientId: CLIENT_ID,
redirectUri: 'http://localhost:3000/api/users/signIn'
});
$authProvider.storage = 'localStorage'; // or 'sessionStorage'
$authProvider.loginRedirect = 'http://localhost/#/home';
This is the controller in node where the url is redirected to (google developer console)
router.get('/signIn', function(req, res) {
//console.log(req);
var code = req.query.code;
oauth2Client.getToken(code, function(err, tokens) {
if (!err) {
https.get("https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=" + tokens.access_token, function(response) {
// Continuously update stream with data
var body = '';
response.setEncoding('utf8');
response.on('data', function(d) {
body += d;
});
// Data fetched
response.on('end', function() {
var parsed = JSON.parse(body);
// Check if client_id is from the right app
if (parsed.issued_to == '343234242055-vd082vo0o8r8lmfvp1a973736fd98dht.apps.googleusercontent.com') {
User.getGoogleId(parsed.user_id, function(err, user) {
if (err) {
res.status(500).send({
message: 'not authorized app'
});
}
// No user returned, create one
if (!user) {
// Request user info
oauth2Client.setCredentials(tokens);
plus.people.get({
userId: 'me',
auth: oauth2Client
}, function(err, plusUser) {
if (err) res.status(500).send({
message: 'not authorized app'
});
else {
// Create new user
User.create(plusUser.name.givenName, plusUser.name.familyName, (plusUser.name.givenName + "." + plusUser.name.familyName + "#cozmos.be").toLowerCase(), parsed.user_id, function(err, newUser) {
if (err) res.status(500).send({
message: 'not authorized app'
});
else {
res.statusCode = 200;
return res.send({
response: 'Success',
id: user._id,
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
token: tokens.access_token
});
}
});
}
});
} else {
// Return user
res.statusCode = 200;
return res.send({
response: 'Success',
id: user._id,
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
token: tokens.access_token
});
}
});
}
// if not right app, return unauthorized response
else {
res.status(500).send({
message: 'not authorized app'
});
}
});
});
}
});
});
So I login, I get asked to give permission to the application to use my account info, I get a json response where I can see my name, email and token, and that's it
Even within the company where I work, no one could find an answer. So I came with a solution myself. I don't use satellizer anymore.
.when('/access_token=:access_token', {
template: '',
controller: function($window, $http, $location, $rootScope) {
var hash = $location.path().substr(1);
var splitted = hash.split('&');
var params = {};
for (var i = 0; i < splitted.length; i++) {
var param = splitted[i].split('=');
var key = param[0];
var value = param[1];
params[key] = value;
$rootScope.accesstoken = params;
}
console.log(params.access_token);
var json = {
Token: params.access_token
};
$window.localStorage['token'] = params.access_token;
$http.post('http://localhost:3000/api/users/signIn', json).success(function(data, status) {
console.log(data);
}).error(function(err) {
console.log(err);
});
$location.path("/home");
}
/*controller: 'createNewsFeed',
templateUrl: 'homepage.html'*/
}).
So redirect the page by itself. Because the authentication works on the backend side, I can get a access token, which is the only thing I really need for future use of my rest api. I defined a route where, after receiving the json with the token, my browser is manually redirected to with $window.location. So when that page is loaded (not visible for the user, it goes too fast to notice) I analyse the token, save the token, analyse authentication, when that is successful I manually redirect to the homepage.
Related
In a web app using AnularJS and the Facebook SDK, right after initing I'm calling FB.getLoginStatus() to check if user is connected:
In my run method:
window.fbAsyncInit = function() {
FB.init({
appId : '186219064942355',
xfbml : false,
version : 'v2.10'
});
FB.AppEvents.logPageView();
//$rootScope.$broadcast('fbInitiated');
$rootScope.$broadcast('checkFBLoginStatus');
};
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
In my facebookService:
getLoginStatus: function () {
var deferred = $q.defer();
FB.getLoginStatus( function(response) {
if (response.status === 'connected') {
// the user is logged in and has authenticated your
// app, and response.authResponse supplies
// the user's ID, a valid access token, a signed
// request, and the time the access token
// and signed request each expire
//var uid = response.authResponse.userID;
//var accessToken = response.authResponse.accessToken;
deferred.resolve(response);
} else if (response.status === 'not_authorized') {
// the user is logged in to Facebook,
// but has not authenticated your app
deferred.reject('not authorized');
} else {
// the user isn't logged in to Facebook.
deferred.reject('user is not logged in to Facebook: ' + JSON.stringify(response));
}
}, true);
return deferred.promise;
},
I visually check the result and, if not connected, click in a button which is calling self.loginFB to popup the facebook login dialog. Then I'm successfully logging in and calling FB.getLoginStatus(), just to check the result:
In my controller:
self.loginFB = function () {
if (self.fbStatus !== 'connected') {
facebookService.login().then(
function(response) {
console.log('fb login success');
facebookService.getLoginStatus().then(
function (status) {
self.fbStatus = 'updated: connected';
},
function (statusError) {
self.fbStatus = 'updated: fb error: ' + statusError;
}
);
},
function(error) {
console.log('login error ' + JSON.stringify(error));
}
);
}
};
In my facebookService:
login: function () {
var deferred = $q.defer();
FB.login( function(response) {
if (response.authResponse) {
console.log('Welcome! Fetching your information.... ');
deferred.resolve(response);
} else {
console.log('User cancelled login or did not fully authorize.');
deferred.reject('Error occured');
}
}, {
scope: 'public_profile, email, user_birthday',
return_scopes: true
});
return deferred.promise;
},
But this getLoginStatus after login() is coming up as "unknown"! Why, if the login was successful?
Later on, I'm calling a FB.logout(), and I'm getting a response that the user was not logged in:
In my facebookService:
logout: function () {
var deferred = $q.defer();
FB.getLoginStatus(function(response) {
if (response.status === 'connected') {
FB.logout(function(response) {
//FB.Auth.setAuthResponse(null, 'unknown');
data = {
name: '',
gender: '',
birthday: '',
email: '',
id: ''
};
console.log('**** FB service logged out');
deferred.resolve(response);
});
} else {
console.log('*** FB was not connected');
}
});
return deferred.promise;
}
How come the previous FB.login() be successful but the user is not logged in?
The login went through without errors but both FB.loginStatus and FB.logout didn't work after it. Why?
Additionally, the FB.login() was so successful that now I'm currently logged in facebook.com too (which was previously logged out). Wasn't the FB.logout supposed to disconnect me also from facebook.com, since it was not connected before?
The biggest problem is that since the FB.logout didn't have any effect, now the user is unknowingly logged in to facebook.com... and if another user tries to perform a "signup with facebook" for a new account in my web app, this new user can hijack the facebook profile from the previous user. This is a huge problem!
I generated token with JWT using node and angular, and can't check if user is authorized.
Node:
module.exports.authenticate = function(req, res) {
var user = new User(req.body);
User.findOne({
username: req.body.username
}, function(err, user) {
if (err) throw err;
if (!user) {
res.json({ success: false, message: 'Authentication failed. User not found.' });
}
else if (user) {
if (user.password != req.body.password) {
res.json({ success: false, message: 'Authentication failed. Wrong password.' });
}
else {
var token = jwt.sign(user, config.secret, {
expiresIn: 60*60*24
});
res.json({
success: true,
token: token
});
}
}
});
};
Angular:
$http(req)
.then(function (response) {
console.log(response.data.success);
if(response.data.success) {
var user = localStorage.setItem('token', JSON.stringify(response.data));
token = localStorage.getItem('token');
// console.log('User info: ', JSON.parse(getuser));
// window.location = "/dashboard";
return response.data;
}
}, function (response) {
}
);
}
How can I check token when I change route?
And generically how can I use Token?
Angular ui-router provides $routeChangeStart event while you change a route. You can use it in the following way.
$rootScope.$on('$routeChangeStart', function (event, next, current){
//you can code here something to be run on changing routes
}
You might want to have a look here for detailed event documentation.
Regarding a more generic implementation , you can create a service to keep your token at the time of login or whenever you get it. Thereafter you can keep getting the token from the service for any future comparisons.
you should install "cookie-parser"
npm i cookie-parser
and go to index.js file and add
const cookieParser = require('cookie-parser');
app.use(cookieParser());
it works for me
Hi I followed a tutorial on the web. Everything work but I would encode bas64 with a secret or jwt but I don't know how. Can you help me please ?
(function () {
'use strict';
angular
.module('app')
.factory('AuthenticationService', Service);
function Service($http, $localStorage) {
var service = {};
service.Login = Login;
service.Logout = Logout;
return service;
function Login(username, password, callback) {
$http.post('/api/authenticate', { username: username, password: password })
.success(function (response) {
// login successful if there's a token in the response
if (response.token) {
// store username and token in local storage to keep user logged in between page refreshes
$localStorage.currentUser = { username: username, token: response.token };
// add jwt token to auth header for all requests made by the $http service
$http.defaults.headers.common.Authorization = 'Bearer ' + response.token;
// execute callback with true to indicate successful login
callback(true);
} else {
// execute callback with false to indicate failed login
callback(false);
}
});
}
function Logout() {
// remove user from local storage and clear http auth header
delete $localStorage.currentUser;
$http.defaults.headers.common.Authorization = '';
}
}
})();
and my service :
function run($rootScope, $http, $location, $localStorage) {
// keep user logged in after page refresh
if ($localStorage.currentUser) {
$http.defaults.headers.common.Authorization = 'Bearer ' + $localStorage.currentUser.token;
}
// redirect to login page if not logged in and trying to access a restricted page
$rootScope.$on('$locationChangeStart', function (event, next, current) {
var publicPages = ['/login'];
var restrictedPage = publicPages.indexOf($location.path()) === -1;
if (restrictedPage && !$localStorage.currentUser) {
$location.path('/login');
}
});
}
and the nodeJs :
function setupFakeBackend($httpBackend) {
var testUser = { username: 'test', password: 'test', firstName: 'Test', lastName: 'User' };
// fake authenticate api end point
$httpBackend.whenPOST('/api/authenticate').respond(function (method, url, data) {
// get parameters from post request
var params = angular.fromJson(data);
// check user credentials and return fake jwt token if valid
if (params.username === testUser.username && params.password === testUser.password) {
return [200, { token: 'fake-jwt-token' }, {}];
} else {
return [200, {}, {}];
}
});
$httpBackend.whenGET(/^\w+.*/).passThrough();
}
Thank you for your answer :)
JSON Web Tokens are composed of three JSON objects encoded to base 64 seperated by a . character.
header.payload.signiture
The example found at jwt.io eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ decodes to three JSON strings separated by .
If you wish to pull out the seperate componets you must first split the String
// es6
let myJwt = getToken();
let jwtParts = myJwt.split('.').map(part => btoa(part));
console.log(`header: ${jwtParts[0]}, payload: ${jwtParts[1]}, sig: ${jwtParts[2]}`)
On the server side you should be using the signing (for login) and verifying functions (for subsequent authentication) found in your JWT library i.e https://github.com/auth0/node-jsonwebtoken
Let me know if that was not quite what you are looking for
I create a jwt token and stored in database. when login successfully done i want to redirect to home page with token. how could i do that.
My Nodejs file.
app.post('/authenticate', function(req, res, next) {
User.findOne({name: req.body.name}, function(err, user) {
if (err) {throw err;}
if (!user) {
res.json({ success: false, message: 'Authentication failed. User not found.' });
} else if (user) {
// check if password matches
if (user.password !== req.body.password) {
res.json({ success: false, message: 'Authentication failed. Wrong password.' });
} else {
// if user is found and password is right
// create a token
var token = jwt.sign(user, app.get('superSecret'), {
expiresIn: 1440 // expires in 24 hours
});
// return the information including token as JSON
res.json({
success: true,
message: 'Enjoy your token!',
token: token
});
}
}
});
});
My angularjs file
var app = angular.module('loginApp', []);
// Controller function and passing $http service and $scope var.
app.controller('loginCtrl', function($scope, $http) {
// create a blank object to handle form data.
$scope.user = {};
// calling our submit function.
$scope.submitForm = function() {
// Posting data to file
$http.post('/tokken/login/', $scope.user).then(function (response) {
//$http.defaults.headers.common['Token'] = token
if (response.errors) {
// Showing errors.
$scope.errorName = response.errors.name;
$scope.erroPassword = response.errors.password;
} else {
$scope.message = response.data;
}
});
};
});
I print token value in same login page. I want to redirect to another page with the token
I have an angular-fullstack app generated from here -
https://github.com/angular-fullstack/generator-angular-fullstack
I am using the same directory structure as angular-fullstack.
Now I am trying to authenticate users with facebook sdk and did the following steps -
1) specify passport facebook login strategy
// created auth/facebook/index.js
'use strict';
var express = require('express');
var passport = require('passport');
var auth = require('../auth.service');
var router = express.Router();
router
.get('/', passport.authenticate('facebook', {
scope: ['email', 'public_profile', 'user_friends', 'user_events'],
failureRedirect: '/',
session: false
}))
.get('/callback', passport.authenticate('facebook', {
failureRedirect: '/',
session: false
}), auth.setTokenCookie);
module.exports = router;
// created auth/facebook/passport.js
var passport = require('passport');
var FacebookStrategy = require('passport-facebook').Strategy;
var config = require('../../config/environment');
var jwt = require('jsonwebtoken');
exports.setup = function (User, config) {
passport.use(new FacebookStrategy({
clientID: config.facebook.clientID,
clientSecret: config.facebook.clientSecret,
callbackURL: config.facebook.callbackURL
},
function(accessToken, refreshToken, profile, done) {
User.findOne({'facebookId':profile.id}, function(err, user){
if(err) return done(err);
if(user) {
return done(null, user);
} else {
var newUser = {};
newUser['facebookId'] = profile.id;
newUser['providerData'] = {
name: 'facebook',
username: profile.username,
displayName: profile.displayName,
gender: profile.gender,
profileUrl: profile.profileUrl
};
newUser['name'] = profile.name.givenName ? profile.name.givenName: '';
newUser['email'] = profile.emails.length>0? profile.emails[0].value : done('email not found');
function generatePassword() {
var length = 8,
charset = "abcdefghijklnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
retVal = "";
for (var i = 0, n = charset.length; i < length; ++i) {
retVal += charset.charAt(Math.floor(Math.random() * n));
}
return retVal;
}
newUser['password'] = generatePassword();
newUser['role'] = 'user';
var user = new User(newUser);
user.save(function (err, user) {
if (err) { console.log(err); done(err); }
var token = jwt.sign({_id: user._id }, config.secrets.session, { expiresInMinutes: 60 * 5 });
res.json({ token: token });
});
}
});
}
));
};
// added entry in auth/index.js for facebook module
'use strict';
var express = require('express');
var passport = require('passport');
var config = require('../config/environment');
var User = require('../api/user/user.model');
// Passport Configuration
require('./local/passport').setup(User, config);
require('./facebook/passport').setup(User, config);
var router = express.Router();
router.use('/local', require('./local'));
router.use('/facebook', require('./facebook'));
module.exports = router;
In client side I made the following changes -
// installed ng-facebook from https://github.com/GoDisco/ngFacebook using bower install ng-facebook
// added ngFacebook in Angular App module
// set App Id in app.config -
$facebookProvider.setAppId('XXXXXXXXXXXX');
Then added this - in app.run
app.run(function ($rootScope, $location, Auth) {
(function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {
return;
}
js = d.createElement(s);
js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
// Redirect to login if route requires auth and you're not logged in
$rootScope.$on('$stateChangeStart', function (event, next) {
Auth.isLoggedInAsync(function (loggedIn) {
if (next.authenticate && !loggedIn) {
$location.path('/access/signin');
}
});
});
})
Then finally calling /auth/facebook from my client now I am getting the data from facebook after a user logs in and I am able to save it in Database, but the homepage always gets redirected to login state and not the dashboard.
I have the following http interceptors in my client app -
app.factory('authInterceptor', function ($rootScope, $q, $cookieStore) {
return {
// Add authorization token to headers
request: function (config) {
config.headers = config.headers || {};
if ($cookieStore.get('token')) {
config.headers.Authorization = 'Bearer ' + $cookieStore.get('token');
}
return config;
},
// Intercept 401s and redirect you to login
responseError: function (response) {
if (response.status === 401) {
$cookieStore.remove('token');
return $q.reject(response);
}
else if (response.status === 403) {
$cookieStore.remove('token');
return $q.reject(response);
} else if (response.status === 405) {
$cookieStore.remove('token');
return $q.reject(response);
}
else {
return $q.reject(response);
}
}
};
})
Now after I login with my facebook account, when I receive the callback from facebook. It is getting redirected to the same login state again even when the API /api/users/me is giving me the logged in information in my browser console -
{"_id":"569bc8d6b0c2e8315539539e","facebookId":"XXXXX","name":"Harshit","email":"XXXX","__v":0,"providerData":{"name":"facebook"},"messages":[],"notifications":[],"subjects":[],"date":"2016-01-17T17:01:10.000Z","role":["user"]}
So, I am thinking passport is not setting the authorization headers properly or there is something other than my http interceptor that is redirecting me to the login page only
How can I debug this issue or find out where I am going wrong, or missing something ?
In my opinion I prefer just checking whether the server still has your sesssion information.
In your routing you can do check
when('/url', {
templateUrl: 'partial',
controller: 'controller',
resolve: { loggedin: checkLoggedin}}).
Just create your function checkLoggedin like this
var checkLoggedin = function($q, $http, $location){
var deferred = $q.defer();
$http.get('/loggedin').then(function(response){
deferred.resolve();
},function(response){
deferred.reject();
$location.path('/home');
});
return deferred.promise;
}
Basically your making a promise here. Your saying here go to the backend and check I will wait for the backend to send me back a response. If the response is 200 let them on the page or else redirect to home.
Your back-end should return a 401 (unauthorize) or 200 (success) response letting your front-end know whether to let the user on the page.
function(req,res){
if (req.isAuthenticated() == true) {
res.status(200).send("Authenticate.");
}else{
res.status(401).send("Not Authenticate");
}
}
You can check more info about resolve here
Angular Route Provider
The resolve will run all dependencies that is passed to it before loading your view.
This will allow you to make a http request to your back end and verify with passport to see if the session is still there.
Angular has to make a http request to the backend everytime to make sure that the user is indeed logged in.