Sign in Twitter OAuth without Sessions aka token_secret (AngularJS Fail) - angularjs

This is an express route from angularjs satellizer example, implementing 3 legged OAuth with Twitter:
/*
|--------------------------------------------------------------------------
| Login with Twitter
|--------------------------------------------------------------------------
*/
app.get('/auth/twitter', function(req, res) {
var requestTokenUrl = 'https://api.twitter.com/oauth/request_token';
var accessTokenUrl = 'https://api.twitter.com/oauth/access_token';
var authenticateUrl = 'https://api.twitter.com/oauth/authenticate';
if (!req.query.oauth_token || !req.query.oauth_verifier) {
var requestTokenOauth = {
consumer_key: config.TWITTER_KEY,
consumer_secret: config.TWITTER_SECRET,
callback: config.TWITTER_CALLBACK
};
// Step 1. Obtain request token for the authorization popup.
request.post({ url: requestTokenUrl, oauth: requestTokenOauth }, function(err, response, body) {
var oauthToken = qs.parse(body);
var params = qs.stringify({ oauth_token: oauthToken.oauth_token });
// Step 2. Redirect to the authorization screen.
res.redirect(authenticateUrl + '?' + params);
});
} else {
var accessTokenOauth = {
consumer_key: config.TWITTER_KEY,
consumer_secret: config.TWITTER_SECRET,
token: req.query.oauth_token,
verifier: req.query.oauth_verifier
};
// Step 3. Exchange oauth token and oauth verifier for access token.
request.post({ url: accessTokenUrl, oauth: accessTokenOauth }, function(err, response, profile) {
profile = qs.parse(profile);
// Step 4a. Link user accounts.
if (req.headers.authorization) {
User.findOne({ twitter: profile.user_id }, function(err, existingUser) {
if (existingUser) {
return res.status(409).send({ message: 'There is already a Twitter account that belongs to you' });
}
var token = req.headers.authorization.split(' ')[1];
var payload = jwt.decode(token, config.TOKEN_SECRET);
User.findById(payload.sub, function(err, user) {
if (!user) {
return res.status(400).send({ message: 'User not found' });
}
user.twitter = profile.user_id;
user.displayName = user.displayName || profile.screen_name;
user.save(function(err) {
res.send({ token: createToken(user) });
});
});
});
} else {
// Step 4b. Create a new user account or return an existing one.
User.findOne({ twitter: profile.user_id }, function(err, existingUser) {
if (existingUser) {
var token = createToken(existingUser);
return res.send({ token: token });
}
var user = new User();
user.twitter = profile.user_id;
user.displayName = profile.screen_name;
user.save(function() {
var token = createToken(user);
res.send({ token: token });
});
});
}
});
}
});
The problem is Step 3:
var accessTokenOauth = {
consumer_key: config.TWITTER_KEY,
consumer_secret: config.TWITTER_SECRET,
token: req.query.oauth_token,
verifier: req.query.oauth_verifier
};
// Step 3. Exchange oauth token and oauth verifier for access token.
request.post({ url: accessTokenUrl, oauth: accessTokenOauth });
Because the node-request documentation describes Step 3 as:
// step 3
// after the user is redirected back to your server
var auth_data = qs.parse(body)
, oauth =
{ consumer_key: CONSUMER_KEY
, consumer_secret: CONSUMER_SECRET
, token: auth_data.oauth_token
, token_secret: req_data.oauth_token_secret
, verifier: auth_data.oauth_verifier
}
, url = 'https://api.twitter.com/oauth/access_token'
;
request.post({url:url, oauth:oauth}
The difference is, in the satellizer example, it doesn't pass token_secret to sign-in, but it should. So is this a mistake or what am I missing?
The real problem for me was, 3 legged twitter sign-in flow actually requires session on server side, but the satellizer example doesn't use any sessions, so I was wondering how this possible without sessions, but either it is not possible and satellizer example is wrong, or I don't understand something.

Related

Make facebook posts with ionic/angular app

I'm creating an app with the ionic framework and integrate the facebookConnectPlugin for login and making posts to a fan page.
The login part is working properly, I can see the user data and display on the app. The problem I'm having is when I try to make a post to facebook.
This is the complete controller that manage the login and the post
.controller('signupCtrl', function($scope, $state, $q, UserService, $ionicLoading){
// This is the success callback from the login method
var accesstoken;
var fbLoginSuccess = function(response){
if (!response.authResponse){
fbLoginError("Cannot find the authResponse");
return;
}
var authResponse = response.authResponse;
accesstoken = authResponse.accessToken;
getFacebookProfileInfo(authResponse).then(function(profileInfo){
// For the purpose of this example I will store user data on local storage
UserService.setUser({
authResponse: authResponse,
userID: profileInfo.id,
name: profileInfo.name,
email: profileInfo.email,
picture: "http://graph.facebook.com/" + authResponse.userID + "/picture?type=large"
});
$ionicLoading.hide();
}, function(fail){
// Fail get profile info
alert('profile info fail ' + fail);
});
};
// This is the fail callback from the login method
var fbLoginError = function(error){
alert('fbLoginError ' + error);
$ionicLoading.hide();
};
// This method is to get the user profile info from the facebook api
var getFacebookProfileInfo = function (authResponse){
var info = $q.defer();
facebookConnectPlugin.api('/me?fields=email,name&access_token=' + authResponse.accessToken, null,
function (response) {
console.log(response);
info.resolve(response);
},
function (response) {
console.log(response);
info.reject(response);
}
);
return info.promise;
};
//This method is executed when the user press the "Login with facebook" button
$scope.facebookSignIn = function(){
console.log('---> facebookSignIn');
facebookConnectPlugin.getLoginStatus(function(success){
if (success.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
//alert('getLoginStatus ' + success.status);
// Check if we have our user saved
var user = UserService.getUser('facebook');
if (!user.userID){
alert('UNO');
getFacebookProfileInfo(success.authResponse).then(function(profileInfo) {
// For the purpose of this example I will store user data on local storage
UserService.setUser({
authResponse: success.authResponse,
userID: profileInfo.id,
name: profileInfo.name,
email: profileInfo.email,
picture: "http://graph.facebook.com/" + success.authResponse.userID + "/picture?type=large"
});
//$state.go('menu.home');
}, function(fail){
// Fail get profile info
alert('profile info fail ' + fail);
});
}else{
//alert('DOS');
$state.go('menu.home');
var fan_token = 'EAAH1eElPgZBl1jwZCI0BADZBlrZCbsZBWF5ig29V1Sn5ABsxH1o4kboMhpjZBDfKtD1lfDK1dJLcZBI4gRBOF2XGjOmWMXD0I8jtPZA4xLJNZADarOGx8fiXBRZCTOaxwBLQEwRjsvaqTtb2DTCI0Qdo3haX6vqHlJoWMZD';
console.log('access token', accesstoken);
facebookConnectPlugin.api(
'/186259017448757/feed',
'POST',
{
access_token: fan_token,
'message':'HOLA!'
},
function(response){console.log(response);alert(response.id)}
)
}
}else{
// If (success.status === 'not_authorized') the user is logged in to Facebook,
// but has not authenticated your app
// Else the person is not logged into Facebook,
// so we're not sure if they are logged into this app or not.
alert('getLoginStatus ' + success.status);
$ionicLoading.show({
template: 'Logging in...'
});
// Ask the permissions you need. You can learn more about
// FB permissions here: https://developers.facebook.com/docs/facebook-login/permissions/v2.4
facebookConnectPlugin.login(['email', 'public_profile'], fbLoginSuccess, fbLoginError);
}
});
};
})
This is the part where I try to make a post to a facebook wall
facebookConnectPlugin.api('/181297057448657/feed', 'POST', {access_token: fan_token, 'message':'HOLA!'},
function(response){
console.log(response);
alert(response.id)
}
)
I went to https://developers.facebook.com/tools/explorer to generate the access tokens, tried with several tokens (user, fanpage, etc) but any seem to work. I've also used this code snippet provided in the facebook devs explorer tool
FB.api(
'/174801556748297/feed',
'POST',
{"message":"testing3"},
function(response) {
// Insert your code here
}
);
The result is always the same, I get a JSON error in the response object and no facebook post to the wall
Any help would be really appreciated!
PD: app ID's have been modified, that why they don't match. Same with the tokens and fan page ID
I used following in my app to post ,Reference https://github.com/ccoenraets/sociogram-angular-ionic
$scope.share = function () {
OpenFB.post('/me/feed', $scope.item)
.success(function () {
console.log("done");
$ionicLoading.hide();
$scope.item.message="";
$ionicLoading.show({ template: 'Post successfully on Facebook!', noBackdrop: true, duration: 2000 });
$scope.status = "This item has been shared from OpenFB";
})
.error(function(data) {
$ionicLoading.hide();
alert(data.error.message);
});
};
HTML
<div class="media-body">
<textarea placeholder="What's on your mind?" ng-model="item.message"></textarea>
</div>
<hr style="margin-top: -1px;" />
<div style="text-align:right;padding-right:20px;padding-bottom:10px;">
<button class="fb-login" ng-click="share()">Post to Facebook</button>
</div>

How to pass JsonWebToken x-access-token through angular js

I created a node express RESTful API with jsonwebtoken as authentication method. But unable to pass the x-access-token as headers using angular js.
my JWT token authentication script is,
apps.post('/authenticate', function(req, res) {
// find the item
Item.findOne({
name: req.body.name
}, function(err, item) {
if (err) throw err;
if (!item)
{
res.json({ success: false, message: 'Authentication failed. item not found.' });
}
else if (item)
{
// check if password matches
if (item.password != req.body.password)
{
res.json({ success: false, message: 'Authentication failed. Wrong password.' });
}
else
{
// if item is found and password is right
// create a token
var token = jwt.sign(item, app.get('superSecret'), {
expiresIn: 86400 // expires in 24 hours
});
res.json({
success: true,
message: 'Enjoy your token!',
token: token
});
}
}
});
});
Middleware which checks the token is correct is,
apps.use(function(req, res, next) {
// check header or url parameters or post parameters for token
var token = req.body.token || req.params.token || req.headers['x-access-token'];
// decode token
if (token)
{
// verifies secret and checks exp
jwt.verify(token, app.get('superSecret'), function(err, decoded) {
if (err)
{
return res.json({ success: false, message: 'Failed to authenticate token.' });
}
else
{
// if everything is good, save to request for use in other routes
req.decoded = decoded;
next();
}
});
}
else
{
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
Finally the GET method script is,
app.get('/display', function(req, res) {
Item.find({}, function(err, items) {
$http.defaults.headers.common['X-Access-Token']=token;
res.json(items);
});
});
But it always failed to authenticate. Please any one help me to solve this issue. I am really stucked here.
It always shows only the following authentication failed message.
{"success":false,"message":"No token provided."}
If you use $http as the dependency in your angular controller then this would help you I guess -
var token = this.AuthToken.getToken();
$http.get('/api/me', { headers: {'x-access-token': token} });
I will change this according to your code once you upload your angular code.
The client should be sending the token in the Authorization header, using the Bearer scheme, as 'X-' headers have been deprecated since 2012:
Your node would now be along the lines of:
apps.post('/authenticate', function(req, res) {
.....
var token = 'Bearer' + ' ' + jwt.sign(item, app.get('superSecret'), {
expiresIn: 86400 // expires in 24 hours
});
.....
}
apps.use(function(req, res, next) {
// Trim out the bearer text using substring
var token = req.get('Authorization').substring(7);
....
}
Then your angular code would become:
var token = this.AuthToken.getToken();
$http.get('/api/me', { headers: {'Authorization': token} });
You could create a interceptor that catches all ajax calls and injects the token into the header. That way you would not have inject it every time you make an ajax call.
This is a good place to start if you wanted to go that route:
http://www.webdeveasy.com/interceptors-in-angularjs-and-useful-examples/

MEAN: Loggin in by JWT

[Q1] What advantage does an HTTP Interceptor provide on modifying the config.headers["Authorization"] (frontend AngularJS) to contain the value of token when I can verify the requests by checking the req.cookies object? (at the backend NodeJS)
I am trying to understand how JSON web tokens function. The demo application I have setup has a login functionality.
On GET '/login' I am able to produce a token, set a cookie with it.
On the frontend, I can access a JSON object containing the token.
I can view the cookie in the developer console.
Nodejs:
index.js - login route
router.post('/login', function(req, res, next) {
Authenticator.find(req.cookies.token, req.body, Heartbeat.common, function(err, warning, data){
if(err) {
res.status(404).send({token:false, warning: null, error:err});
} else if(warning){
res.status(200).send({token:true, warning: warning, error:null});
} else {
res.cookie('token', data, {maxAge: 3600000, httpOnly:true});
res.status(200).json({token:true, error: null});
}
});
});
Authenticator.ctrl.js - Authenticator.find()
find: function(token, user, heartbeat, callback) {
if(!token) {
Auth.findOne({email:user.email}, function(err, data){
if(err) {
console.log(err);
} else {
if(data) {
if(data.checkHash(user.password)) {
callback(null, null,TokenMaker.createToken(user.email, heartbeat));
} else {
callback(Errors.login.strict.MISMATCH, null, null);
}
} else {
callback(Errors.login.strict.NOT_REGISTERED, null, null);
}
}
});
} else {
callback(null, Errors.login.warning.ACTIVE_REFRESH, null);
}
},
Angular Controller
app.controller('userAccessCtrl', ['$scope', '$http', function ($scope, $http){
$scope.user = {
email: "someone#some.com",
password: "12345679"
};
$scope.error = {};
$scope.loginAccess = function(user) {
var submitReady = true;
var emailStatus = EmailValidator.email(user.email);
var passwordStatus = EmailValidator.password(user.password);
if(typeof emailStatus === "string") {
$scope.error.email = emailStatus;
submitReady = false;
}
if(typeof passwordStatus === "string") {
$scope.error.password = passwordStatus;
submitReady = false;
}
if(submitReady) {
$scope.error = {}
var data = $scope.user;
$scope.user = {};
$http.post('/login', data)
.then(function(success){
console.log(success);
},function(error){
console.log(error);
});
}
}
}]);
Console response:
{
"data": {
"token":true,
"error":null
},
"status":200,
"config":{
"method":"POST",
"transformRequest":[null],
"transformResponse":[null],
"url":"/login",
"data":{
"email":"someone#some.com",
"password":"12345679"
},
"headers":{
"Accept":"application/json, text/plain, */*",
"Content-Type":"application/json;charset=utf-8"
}
},
"statusText":"OK"
}
Actually it's a wrong to use cookies and JWT tokens.
JWT token is much better for authentication than cookies.
When you use token, your server doesn't need to store session in database of memory and this is a big advantage for your application - you can scale you application, add new servers without thinking about how to sync sessions between servers.
In short words, when you use JWT token your flow is next:
frontend (in you case it's an angular) sends login and password to /login route
backend checks credentials and sends back token (in request body, not in cookies)
frontend app saves token in local storage or session storage of browser
and you can write HTTP Interceptor which will intercepts all requests to backend and it will attach "Authorization" header to all requests, it looks like next:
Authorization: Bearer here-is-your-jwt-token
backend can check this authorization header and if it is correct (look at http://jwt.io to read how verification works) backend can serve you request.

Ionic Facebook Connect

I'm pretty new to developing in Ionic and I'm trying to integrate my application with Facebook login. I found this tutorial https://ionicthemes.com/tutorials/about/native-facebook-login-with-ionic-framework and I did everything the way it was shown but I'm getting the following error
TypeError: Cannot read property 'getLoginStatus' of undefined
at Scope.$scope.facebookSignIn (controllers.js:547)
at $parseFunctionCall (ionic.bundle.js:21172)
at ionic.bundle.js:53674
at Scope.$eval (ionic.bundle.js:23228)
at Scope.$apply (ionic.bundle.js:23327)
at HTMLAnchorElement.<anonymous> (ionic.bundle.js:53673)
at HTMLAnchorElement.eventHandler (ionic.bundle.js:11841)
at triggerMouseEvent (ionic.bundle.js:2865)
at tapClick (ionic.bundle.js:2854)
at HTMLDocument.tapTouchEnd (ionic.bundle.js:2977)
My Code for the controller is as follows:
.controller('WalkthroughCtrl', function($scope, $state, $q, UserService, $ionicLoading) {
var fbLoginSuccess = function(response) {
if (!response.authResponse){
fbLoginError("Cannot find the authResponse");
return;
}
var authResponse = response.authResponse;
getFacebookProfileInfo(authResponse)
.then(function(profileInfo) {
// For the purpose of this example I will store user data on local storage
UserService.setUser({
authResponse: authResponse,
userID: profileInfo.id,
name: profileInfo.name,
email: profileInfo.email,
picture : "http://graph.facebook.com/" + authResponse.userID + "/picture?type=large"
});
$ionicLoading.hide();
$state.go('app.feeds-categories');
}, function(fail){
// Fail get profile info
console.log('profile info fail', fail);
});
};
// This is the fail callback from the login method
var fbLoginError = function(error){
console.log('fbLoginError', error);
$ionicLoading.hide();
};
// This method is to get the user profile info from the facebook api
var getFacebookProfileInfo = function (authResponse) {
var info = $q.defer();
facebookConnectPlugin.api('/me?fields=email,name&access_token=' + authResponse.accessToken, null,
function (response) {
console.log(response);
info.resolve(response);
},
function (response) {
console.log(response);
info.reject(response);
}
);
return info.promise;
};
//This method is executed when the user press the "Login with facebook" button
$scope.facebookSignIn = function() {
facebookConnectPlugin.getLoginStatus(function(success){
if(success.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
console.log('getLoginStatus', success.status);
// Check if we have our user saved
var user = UserService.getUser('facebook');
if(!user.userID){
getFacebookProfileInfo(success.authResponse)
.then(function(profileInfo) {
// For the purpose of this example I will store user data on local storage
UserService.setUser({
authResponse: success.authResponse,
userID: profileInfo.id,
name: profileInfo.name,
email: profileInfo.email,
picture : "http://graph.facebook.com/" + success.authResponse.userID + "/picture?type=large"
});
$state.go('app.feeds-categories');
}, function(fail){
// Fail get profile info
console.log('profile info fail', fail);
});
}else{
$state.go('app.home');
}
} else {
// If (success.status === 'not_authorized') the user is logged in to Facebook,
// but has not authenticated your app
// Else the person is not logged into Facebook,
// so we're not sure if they are logged into this app or not.
console.log('getLoginStatus', success.status);
$ionicLoading.show({
template: 'Logging in...'
});
// Ask the permissions you need. You can learn more about
// FB permissions here: https://developers.facebook.com/docs/facebook-login/permissions/v2.4
facebookConnectPlugin.login(['email', 'public_profile'], fbLoginSuccess, fbLoginError);
}
});
};
})
Thank You in Advance.

Create account with social login (facebook) and authenticate it angular-fullstack generator

I'm facing a problema with social signup because its not logging after its created as in local signup. When redirects, api/users/me is not accessible..is unauthorized (401), different from what i get in local signup, that redirects with user information.
in facebook/index.js I have default gets
.get('/', passport.authenticate('facebook', {
scope: ['email', 'user_about_me'],
failureRedirect: '/signup',
session: false
}))
.get('/callback', passport.authenticate('facebook', {
failureRedirect: '/signup',
session: false
}), auth.setTokenCookie);
and in auth.service.js I have the default functions
function isAuthenticated() {
return compose()
// Validate jwt
.use(function(req, res, next) {
// allow access_token to be passed through query parameter as well
if (req.query && req.query.hasOwnProperty('access_token')) {
req.headers.authorization = 'Bearer ' + req.query.access_token;
}
validateJwt(req, res, next);
})
// Attach user to request
.use(function(req, res, next) {
User.findByIdAsync(req.user._id)
.then(function(user) {
//user here is undefined, it shouldn't be. i want to find out why.
if (!user) {
return res.status(401).end();
}
req.user = user;
next();
})
.catch(function(err) {
return next(err);
});
});
}
function signToken(id, role) {
return jwt.sign({ _id: id, role: role }, config.secrets.session, {
expiresInMinutes: 60 * 5
});
}
/**
* Set token cookie directly for oAuth strategies
*/
function setTokenCookie(req, res) {
if (!req.user) {
return res.status(404).send('Something went wrong, please try again.');
}
var token = signToken(req.user._id, req.user.role);
res.cookie('token', token);
res.redirect('/');
}
Could anyone help me with this...Am I missing something?
Thanks in advance.
I had the similar issue. I had modified Angular full-stack code with a customizable backend URL.
https://github.com/kannach/AFSPhonegap/blob/master/client/components/services/config.service.js
I was testing the facebook login in localhost, whereas my backend url was pointing to my production server. Once I chanded the backed url to localhost, then everything worked as expected

Resources