27

Node and Angular. I have a MEAN stack authentication application where I am setting a JWT token on successful login as follows, and storing it in a session in the controller. Assigning the JWT token to config.headers through service interceptor:

var token = jwt.sign({id: user._id}, secret.secretToken, { expiresIn: tokenManager.TOKEN_EXPIRATION_SEC });
            return res.json({token:token});

authservice.js Interceptor(omitted requestError,response and responseError):

authServices.factory('TokenInterceptor', ['$q', '$window', '$location','AuthenticationService',function ($q, $window, $location, AuthenticationService) {
        return {
            request: function (config) {
                config.headers = config.headers || {};
                if ($window.sessionStorage.token) {
                    config.headers.Authorization = 'Bearer ' + $window.sessionStorage.token;
                }
                return config;
            }               
        };
    }]);

Now I wanted to get the logged in user details from the token, How can I do that? I tried as follows, not working. When I log the error from Users.js file it's saying "ReferenceError: headers is not defined"

authController.js:

$scope.me = function() {
    UserService.me(function(res) {
      $scope.myDetails = res;
    }, function() {
      console.log('Failed to fetch details');
      $rootScope.error = 'Failed to fetch details';
    })
  };

authService.js:

authServices.factory('UserService',['$http', function($http) {
  return {        
    me:function() {
    return $http.get(options.api.base_url + '/me');
    }
  }
}]);

Users.js (Node):

 exports.me = function(req,res){
    if (req.headers && req.headers.authorization) {
        var authorization =req.headers.authorization;
        var part = authorization.split(' ');
        //logic here to retrieve the user from database
    }
    return res.send(200);
}

Do i have to pass the token as a parameter too for retrieving the user details? Or save the user details in a separate session variable as well?

Yogesh Umesh Vaity
  • 41,009
  • 21
  • 145
  • 105
hakuna
  • 6,243
  • 10
  • 52
  • 77

5 Answers5

53

First of all, it is a good practice to use Passport middleware for user authorization handling. It takes all the dirty job of parsing your request and also provides many authorization options. Now for your Node.js code. You need to verify and parse the passed token with jwt methods and then find the user by id extracted from the token:

exports.me = function(req,res){
    if (req.headers && req.headers.authorization) {
        var authorization = req.headers.authorization.split(' ')[1],
            decoded;
        try {
            decoded = jwt.verify(authorization, secret.secretToken);
        } catch (e) {
            return res.status(401).send('unauthorized');
        }
        var userId = decoded.id;
        // Fetch the user by id 
        User.findOne({_id: userId}).then(function(user){
            // Do something with the user
            return res.send(200);
        });
    }
    return res.send(500);
}
Constantine Poltyrev
  • 1,015
  • 10
  • 12
  • 2
    Thank you very much ! It worked. Just have to do a simple change decoded = jwt.verify(authorization.split(' ')[1],secret.secretToken); as i have Bearer + token as my token. – hakuna Oct 31 '15 at 16:50
  • 1
    I found more advantages for using JWT rather than using passport. I'm following these two : http://www.kdelemme.com/2014/03/09/authentication-with-angularjs-and-a-node-js-rest-api/ and http://code.tutsplus.com/tutorials/token-based-authentication-with-angularjs-nodejs--cms-22543. Please advice if passport is a good choice. – hakuna Oct 31 '15 at 16:53
  • Passport can use JWT just the same. It just provides you a convenient abstraction from the authentication procedures. My router code for protected methods looks like this: `app.get('/api/content', passport.authenticate('local-authorization', { session: false }),api.listContent);` – Constantine Poltyrev Oct 31 '15 at 17:35
  • I get 'jwt.verify is not a function' with your solution – An-droid Sep 28 '17 at 14:43
  • You should import the jwt library to make this code work. https://github.com/auth0/node-jsonwebtoken – Constantine Poltyrev Sep 28 '17 at 18:34
15

Find a token from request data:

const usertoken = req.headers.authorization;
const token = usertoken.split(' ');
const decoded = jwt.verify(token[1], 'secret-key');
console.log(decoded);
סטנלי גרונן
  • 2,917
  • 23
  • 46
  • 68
Ajay yadav
  • 264
  • 2
  • 10
2

Your are calling the function UserService.me with two callbacks, although the function does not accept any arguments. What I think you want to do is:

$scope.me = function() {
    UserService.me().then(function(res) {
      $scope.myDetails = res;
    }, function() {
      console.log('Failed to fetch details');
      $rootScope.error = 'Failed to fetch details';
    });
  };

Also, note that the $http methods return a response object. Make sure that what you want is not a $scope.myDetails = res.data

And in your Users.js file, you are using the variable headers.authorization directly, whereas it should be req.header.authorization:

var authorization = req.headers.authorization;
Pedro M. Silva
  • 1,298
  • 2
  • 12
  • 23
  • Thanks for correcting my function, although this is a problem too my primary problem is not able to retrieve the token from config.headers. do you have any idea? – hakuna Oct 31 '15 at 13:20
  • Sorry, missed that error. See the answer now to check if it is fixed. :) – Pedro M. Silva Oct 31 '15 at 13:29
  • Corrected it, still the issue persists. In fact it's failing at the if statement itself in users.js. not even entering inside. – hakuna Oct 31 '15 at 13:38
0

According to the documentation https://github.com/themikenicholson/passport-jwt, you could use request.user. Note, I'm supposing that you are using passport with passport-jwt. It's possible because passport during the context of an authentication is setting the request object and populating the user property. So, just access that property. You don't need to do a middleware.

0

Anderson anzileiro is correct. If you return the full token in the middleware code, the request is indeed populated with the user property and you can access your profile.

passport.use(
  new JWTstrategy(
    {
      secretOrKey: process.env.ACCESS_TOKEN_SECRET,
      // jwtFromRequest: ExtractJWT.fromUrlQueryParameter('secret_token')
      jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken()
    },
    async (token, done) => {
      try {
        return done(null, token);
      } catch (error) {
        done(error);
      }
    }
  )
);

req.user will return :

{
    "user": {
        "username": "admin"
    },
    "iat": 1625920948,
    "exp": 1626007348
}
mirko
  • 1
  • 2