I'm implementing fb authentication in my SPA built using MEAN stack. While I've successfully implemented the fb authentication using facebook token passport strategy, I'm facing issues in securing API endpoints. Because for that I need to pass both the authenticated user object and access token in the $http service and I've tried passing access_token as a property of the user object and also as a header property, but I still 401 (Unauthorized error). Below is my code snippet.
Passport documentation says "Authorization: Bearer base64_access_token_string". Should the token be encoded in a base64 format? Pls help.
server code
app.get('/api/getbikes*',
passport.authenticate('facebook-token',{session: false}),
function(req,res){
if(req.user){
console.log('In getbikes api');
// console.log('req.query :',req.query);
var msg="";
ubBike
.find({cust:req.query._id})
.populate('cust','email')
.exec(function(err,bikes){
res.send(bikes);
if(err) throw err;
});
}
else
{
res.send(401);
}
});
angular code
service
this.getbikes = function(user){
var deferred = $q.defer();
$http({
method:"GET",
url:"http://localhost:3000/api/getbikes",
params: user,
headers:{
Authorization:auth.getAccesstoken()
}
}).then(function successCallback(srresponse){
deferred.resolve(srresponse.data);
},
function failureCallback(srresponse){
$log.error("get bikes http call failed ",srresponse.data);
deferred.reject(srresponse.data);
});//$http
return deferred.promise;
};//getbikes
controller
$scope.fblogin= function(){
auth.fblogin().then(
function(response){
$scope.isAuth = auth.isAuth;
$scope.usr =auth.getResponseobj();
$scope.usr.access_token=auth.getAccesstoken();
$scope.profpic=auth.profpic;
bike.getbikes($scope.usr).then(function(response){
if (response.length ==0)
{
$location.path('/addbike');//redirect to addbike screen
}
else{
$location.path('/appoint');//else redirect to view appointment screen
}
},function(reason){
$scope.msg1 = reason;
});//getbikes
},function(reason){
$log.log("fblogin() - failure :Need to login to the application :"+reason);
})
};//fblogin
Surprisingly, when I send the header as "Authorization: Bearer access_token_string" ie the fb token as is without base64 encoding the API authentication works perfectly fine. This is contrary to the passport facebook token documentation https://github.com/drudge/passport-facebook-token
Related
I get follow scenario which is working now:
MVC controller using System.Web.Mvc.AuthorizeAttribute to authenticate user is authenticated or not, it will be using cookie.
API controller using System.Web.Http.AuthorizeAttribute to authorise with bearer token.
I do also have angular http interceptor that verify and get bearer token for API purpose that can use among all angular $http request. But I am confusing how to achieve both after user has login?
This is current workflow
User click login, angular verify and store bears token in local storage.
After complete, manually trigger MVC controller so that it will get cookie for MVC authenticate.
This seem to me really double job, or I should focusing on using one AuthorizeAttribute?
You need you use Authorize key to give permission to those functions where authorization is needed. And those functions can only be accessed when authorization token is generated and passed with http request.
module.service('tokenservice', function ($http) {
this.get = function () {
var accesstoken = sessionStorage.getItem('accessToken');
var logged_in = localStorage.getItem('logged_in').toString().trim() === 'false' ? false : true;
var authHeaders = {};
if (accesstoken && logged_in) {
authHeaders.Authorization = 'Bearer ' + accesstoken;
}
return authHeaders;
};
});
module.controller('yourControllerName', function ( $http, tokenservice) {
$http({
method: "POST",
url: '/Controller/MyFucntion',
headers: tokenservice.get(),
});
});
This will help you to get generated token in user login. After that You need to work with your controller
[Authorize]
public JsonResult MyFucntion()
{
//Your logic and calculation
//return
}
Hope that will help
To authenticate a user in an app using the Instagram API, you redirect the user to the URL https://api.instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=REDIRECT-URI&response_type=code.
Once the user signs in on this page, Instagram then makes a GET request to the redirect URI (REDIRECT-URI in the above URL). This call to the redirect URI contains a code which you must then include in a POST request to https://api.instagram.com/oauth/access_token to exchange for an access token which is sent in a JSON form in the response.
So I'm receiving the redirect call and making the request for the access token with my Express API and this works fine and I get the form with the access token.
app.get('/redirect', function(req, res){
if(req.query.error){
console.log('Error authenticating user');
console.log(req.query.error);
}
else{
request.post(
'https://api.instagram.com/oauth/access_token',
{form:{
client_id: 'my_client_id',
client_secret: 'my_client_secret',
grant_type: 'authorization_code',
redirect_uri: 'http://localhost/redirect',
code: req.query.code
}
},
function(error, response, body){
if(!error && response.statusCode == 200){
console.log(body);
//HOW DO I GET THIS RESPONSE TO ANGULAR?
res.json({
access_token: body.access_token,
full_name: body.full_name,
username: body.username,
profile_picture: body.profile_picture,
id: body.id
});
return;
}
else{
console.log(response);
console.log(body);
}
}
);
};
My question is: how to I send the access token (or error response) back to Angular?
In other words, how does Angular receive the response from a request it did not make (since the response I want to receive is from the request made with NodeJS's request)?
Initially I tried to make the first GET to https://api.instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=REDIRECT-URI&response_type=code with Angular but I don't think this can be done because the user should be directed to the Instagram URL to sign in.
Update:
When Instagram makes the GET /redirect?code=xxxxxx, it sends the request to my API but also changes the page URL to http://localhost/redirect?code=xxxxxx. I can receive the access token in my API and send it to my client-side but because of the above URL, the page is just displayed as the text of the response and not my index.html with ng-view. How do I get the page to be my index.html in this situation?
I've tried with angular-router the following ways but neither worked:
.when('/redirect', {
templateUrl: 'views/success.html',
controller: 'Controller'
})
and
.when('/success', {
templateUrl: 'views/success.html',
controller: 'Controller'
})
.when('/redirect', {
redirectTo: '/success'
})
What I ended up doing (and I'm not sure this is optimal):
Once I receive the response from the call made from Node, I store the access token from the response, body.access_token, in the req.session.user object provided by the express-session npm package and then I redirect the user to the home page with res.redirect('/#/home');.
app.get('/redirect', function(req, res){
if(req.query.error){
console.log('Error authenticating user');
console.log(req.query.error);
}
else{
request.post(
'https://api.instagram.com/oauth/access_token',
{form:{
client_id: 'my_client_id',
client_secret: 'my_client_secret',
grant_type: 'authorization_code',
redirect_uri: 'http://localhost/redirect',
code: req.query.code
}
},
function(error, response, body){
if(!error && response.statusCode == 200){
console.log('Success making POST to Insta API for token, making new session object');
console.log('token:', (JSON.parse(body).access_token), '\n');
req.session.user = {};
req.session.user.token = JSON.parse(body).access_token;
req.session.save();
res.redirect('/#/home');
}
else{
console.log(error);
res.redirect('/#/login');
}
}
);
};
This way, Angular doesn't need to receive the access token after redirecting the user to Instagram's authorization page because the access token is saved in the req.session.user object which is then accessible on the backend for the duration of the session.
You can then use the access token in any requests to the Instagram API from your backend (initiated by a call to your API from Angular), e.g. by concatenating it to whatever the endpoint is for your Instagram API call like so:
var endpoint = 'https://api.instagram.com/v1/users/self/follows?access_token=' + req.session.user.token;
This method also works well with the session management functionality of express-session since a session is only created when the user can be authenticated by Instagram. See here for a quick tutorial on how to integrate session management into your app.
recently i am working hard on my website with angularjs on the Front End and Symfony 3 on the backend. I put a security layer on my backend so every request from my FE must need a valid token (using grant_type=client_credentials). I have read a looooot about the best practices about call my API Backend with angular... I normally send the token on every request that i make to the Backend, but i read that i can use the $http interceptor to send always on the header my bearer token.
So, i am a little confused that how start... because for one part:
i want to do calls to my backend to load certain data to be used on my pages to show info (using the grant_type=client_credentials) and,
i will have an user management system too. So this users must to login with user and password (again another call to my backend) but with grant_type=password...
The really big question is:
can i do the same things with one interceptor? (one for show page elements data with grant_type=client_credentials and other for the normal users?)
Tha another question is... can i make a token with this interceptor if the token has not been created yet (only for the pages info, for the users i want to refresh the token if is going to expire)?
Sorry if is a little confused... i am confused, i really read many posts, documentation and help... but i don't know where to start... I hope that you can help me...
Thanks for all.
The beauty of JWT is that they are essentially just javascript objects. You could for instance provide the user a token containing their role in the system (user, admin, support etc...) and show/hide elements accordingly.
So basically not only you grant the user access to the API, you also provide them with their type of access. Of course you should NEVER rely on client side authentication to allow restricted API's directly (verify the token on each request, check for the provided role on the server).
Here's an example in NodeJS and Angular:
//In NodeJS...
app.get('/path/to/secured/api', verifyTokenOr401, function(req, res) {
//Do stuff...
res.json({msg: 'Success');
});
function verifyTokenOr401(req, res, next) {
var authHeader = req.headers.authorization;
try {
var token = authHeader.split(' ')[1];
if(jwt.verify(token, 'myAppSecret'))
next();
} catch(e) {
res.status(401).send('Not authorized');
}
}
//Assuming you use node-jsonwebtoken package
app.post('/path/to/authentication', function (req, res) {
//Verify authentication...
User.findOne({username: req.body.username}).then(function(user) {
//VerifyPassword
if(!user)
return res.status(401).send('No such user ' + req.body.username);
if(!user.verifyPassword(req.body.password))
return res.status(401).send('Wrong password for user ' + user.username);
//Provide the user with the access token
var token = jwt.sign({ subject: user.id, role: user.role }, 'myAppSecret');
res.setHeader('Authorization', 'Bearer ' + token.toString());
res.json(user);
})
.catch(function (e) { res.status(500).json(e); });
});
//In angular...
.factory('jwtInterceptor', function() {
return {
request: function(config){
var authHeader = config.headers('authorization');
//Attach header if not present
if(!authHeader)
config.headers.authorization = 'Bearer ' + localStorage.get('myAppToken');
return config;
},
response: function(response){
//Look for token in the header if you get a response and save it
var authHeader = response.headers('authorization');
if(authHeader){
try { localStorage.myAppToken = authHeader.split(' ')[1]; } catch(e) {}
}
return response;
}
}
});
Notable mention: check out auth0's repos for NodeJS and Angular. Both are awesome.
You can create a service which when loaded by angular make a get call for authorization token and set in header. Through this you do not need to set token at every Ajax call. You can do it like this:
app.service("MyService", ["$http", function($http) {
initialize();
function initialize() {
getAuthorizationToken().then(function(response) {
$http.defaults.headers.common.Authorization = 'Bearer some_auth_code_here';
});
}
function getAuthorizationToken() {
// Get call for token
}
}]);
I am working on a token implementation into Angular/.Net application. My part is the front-end. What's happening is that when UI sends a request with the expired token and the server replies with 401 I cannot intercept that before the Browser raises the Login form. As the result I cannot send a request to refresh the token. Can someone please give me an idea how that is supposed be managed? I will provide code just don't know what's to show.
Thanks
Adding code:
var response = $http({
method: "GET",
dataType: "json",
params: params,
headers: {
'Content-Type': "application/xml; charset=utf-8",
},
url: someurl
});
response = response.then(function (data) {
return data.data;
});
response.catch(function (data) {
$q.reject(data);
});
// Return the promise to the controller
return response;
The problem is that I cannot redirect on UI because Browser throws Login form before my code is hit when the server returns 401.
Make ajax request, and if you get 401 then redirect to login page.
P.s. for better understanding provide your code how you implement ajax request. Which module do you use for front-end auth? I recommend satellizer
Added:
I guess you need the following configuration on angular
var app = angular.module('App', ['satellizer'])
.config(function() {
/* your config */
}
.run(function($rootScope, $location, $auth) {
// Check auth status on each routing,
// Redirect to login page, if user is not authenticated or if token expired
$rootScope.$on('$routeChangeStart', function(event, next, current) {
if (!$auth.isAuthenticated()) {
$location.path('/auth/login');
}
});
});
I just finished auth0 angularjs tutorial described at https://auth0.com/docs/quickstart/spa/angular/no-api with a successfull twitter login (so i got token). I want to extend the seed project doing some requests to twitter API to get the user timeline, etc.
I've tried it doing $http request with jsonp and adding a request interceptor at app.js with the Authorization header. It looks like this:
HomeCtrl
$http.jsonp('https://api.twitter.com/1.1/statuses/home_timeline.json?callback=JSON_CALLBACK')
.then(function(data) {
console.log(data);
});
App.js
.config( function myAppConfig ($routeProvider, authProvider, $httpProvider, $locationProvider, jwtInterceptorProvider, RestangularProvider) {
...
$httpProvider.interceptors.push('jwtInterceptor');
})
.factory('jwtInterceptor', function(store) {
return {
request: function(config) {
config.headers = config.headers || {};
config.headers.Authorization = 'Bearer ' + store.get('token');
console.log(config);
return config;
}
};
})
Response returns 401 Authorization Required:
Failed to load resource: the server responded with a status of 401 (Authorization Required)https://api.twitter.com/1.1/statuses/home_timeline.json?callback=angular.callbacks._0
What am i doing wrong? Should I make another thing before do request to get timeline? Maybe am i confused about oauth works and i need access_token to do the request?
Thanks in advance.
When calling the Twitter API, you must call it with the Twitter access token and not with the Auth0 token which is for your API.
Check this docs which explains how to do it: https://auth0.com/docs/what-to-do-once-the-user-is-logged-in/calling-an-external-idp-api
Let me know if this helps :).
Cheers!