Muhammad Ali
Muhammad Ali

Reputation: 369

restrict users from accessing other user profiles

I have to implement security on my app by preventing users to access other users profile.

route.js

router.get('/currentUser/:username', userAuth, (req, res) => {
  User.findOne({
    username: req.params.username
  }).then(user => {
    if (user) {
      return res.status(200).json(user);
    } else {
      return res.status(404).json({
        message: 'User not found'
      });
    }
  });
});

and my userAuth.js

const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
  try {
    const token = req.headers.authorization.split(' ')[1];
    jwt.verify(token, 'app_secret_token');
    next();
  } catch (error) {
    res.status(401).json({
      message: 'Authentication failed!'
    });
  }
};

now if I am logged in as user test so my URL will be http://localhost:4200/currentuser/test but if I change my URL to another user test2 it redirects and loads the test2 even though I am logged as test

how do I prevent this?

Upvotes: 0

Views: 1483

Answers (2)

Thatkookooguy
Thatkookooguy

Reputation: 7012

You need to also check that the logged in user accesses his data.

you can achieve this by checking the user in the token against the requested page. This means you need to encode the user Id inside the jwt token. That will also make sure this parameter wasn't meddled with since jwt.verify would fail if someone tried to change the jwt token without having the secret.

you can add that data to the jwt token when signing it:

jwt.sign({
  userId: 'username'
}, 'secret', { expiresIn: '1h' });

Basically if you save the same data as serializeUser\deserializeUser result, it should also work (the username is just a suggestion).

you can use the callback from jwt.verify to get the decoded token and retrieve that data

const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
  try {
    const token = req.headers.authorization.split(' ')[1];
    jwt.verify(token, 'app_secret_token', (err, decoded) => {
      if (err) { throw err; }

      const currentUsername = decoded.userId; // <-- this should be whatever data you encoded into the jwt token

      // if the user requested is different than the user in the token,
      // throw an authentication failure
      if (req.originalUrl.includes('/currentUser/') &&
          !req.originalUrl.includes(`/currentUser/${currentUsername}`)) {
        throw new Error('access to other user data denied');
       }

       next();
    });

  } catch (error) {
    res.status(401).json({
      message: 'Authentication failed!'
    });
  }
};

Even though I think this might be a good case separating this into two different middlewares :-)

PS - as @anand-undavia mentioned, it might be better to identify the user request based on the jwt token itself instead of the 'url' itself. that way, each user should only have access to their own data and this problem can't occur at all.

basically, the user should be accessible with the method above (getting it from the token) or from a req.user field if you use any middleware that adds it automatically.

Upvotes: 3

Kenana Reda
Kenana Reda

Reputation: 460

let us assume that user profile page id mydomian?passedId=userId so simply add profile-guard to check who can visit or activate this page, in CanActivate check if passed id id the same of current user id, then return true else redirect him to previous page

canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
let passedId: string = next.queryParams.passedId;
  let user = this.authService.GetUser();
  if (user.id == passedId)
    return true;
  else {
    this.router.navigate(['/home']); // or any page like un authorized to log to this page
    return false;
  }
}

Upvotes: 1

Related Questions