restrict users from accessing other user profiles - javascript

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?

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.

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;
}
}

Related

How do I set a header in the login route so the browser remembers who is logged in

I've created a user login back end and everything works fine, but when I log in to a user detail, despite being correct, I'm unable to explore other routes because the user isn't authorized. how do I save the access token to the browser so it remembers? This is the login route below.
router.post("/login", async (req, res) => {
try {
const oneUser = await users.findOne({
username: req.body.username,
});
if (!oneUser) {
return res.status(500).json("User is not in the database");
}
const oPassword = cj.AES.decrypt(
oneUser.password,
process.env.pass
).toString(cj.enc.Utf8);
if (oPassword !== req.body.password) {
return res.status(500).json("Password is incorrect");
}
const accessToken = jwt.sign(
{
id: oneUser._id,
isAdmin: oneUser.isAdmin,
},
process.env.jwtToken,
{ expiresIn: "300" }
);
const { password, ...others } = oneUser._doc;
res.status(200).json({ ...others, accessToken });
} catch (err) {
res.status(500).json(err);
}
});
I saw something like this on the net.
res.header("token", accessToken)
for this you can save a session for that user or response the token and save that to the client browser cookies or session store.
for setting session at node js app checkout link below
https://www.npmjs.com/search?q=session

Handle Email Verification Check with auth0/nextjs

I am using the library auth0/nextjs. I am trying to handle the email verification. I have access to the email_verification variable. If not verified, it will redirect to the page /please-verifiy-your-email.
At the moment I am using handleCallback method which is provided by auth0/nextjs.
Code:
const afterCallback = (req, res, session, state) => {
if (!session.user.email_verified) {
res.status(200).redirect('/please-verifiy-your-email')
}
return session;
};
export default auth0.handleAuth({
async login(req, res) {
try {
await auth0.handleLogin(req, res, {
authorizationParams: {
audience: 'https://dev-okz2bacx.us.auth0.com/api/v2/',
scope: 'openid profile email read:branding'
},
returnTo: "/dashboard"
});
} catch (error) {
res.status(error.status || 400).end(error.message);
}
},
async callback(req, res) {
try {
await auth0.handleCallback(req, res, { afterCallback });
} catch (error) {
res.status(error.status || 500).end(error.message);
}
}
});
How can I make sure, the user is still logged in, to get his email for a resend or the possibility to change his email.
He also needs the chance to do a new sign up , because when I will call api/auth/login after login with the unverified email, it will redirect automatically to /please-verifiy-your-email. I guess the session is not killed and auth0 goes back redirecting even tho I didn’t had the chance to sign up again.
Would be really cool if I get a few inputs.
It would solve the problem if I could log in the user and then it would redirect to /please-verifiy-your-email. I would be able to get his email address, name for further functions like resend verification. Right now I call api/auth/me I get:
{"error":"not_authenticated","description":"The user does not have an
active session or is not authenticated"}

How do I make node.js using express find a JSON Web Token (JWT) inside of a response header cookie in user's browser?

I have a question on how to search for my JWT token inside of a user's browser cookies.
Below I have some code that searches the user's browser for cookies in the response header, but I am not sure how to make the code more specific and search for the JWT token within the cookie and verify that it is an actual JWT token that was a assigned to that user.
const jwt = require('jsonwebtoken');
const router = require('express')();
const cookieParser = require('cookie-parser');
router.use(cookieParser());
module.exports = function(req,res,next){
const token = req.header('Cookie');
if (!token) {
return res.status(403).send('Access Denied');
}
try{
const verified = req.header('Cookie');
req.user = verified;
// const verified = jwt.verify(token, process.env.TOKEN_SECRET);
// req.user = verified;
next();
} catch (err) {
res.clearHeader;
res.status(403).send('Invalid Token');
}
};
I hope I didn't misunderstand your question and waste a bunch time.
Short Answer: How to retrieve information
Use req.body or req.headers. If something will contain the token or authentication details, then it's one of these two.
Full Auth Walkthrough:
To get the JSON Web Tokens you first have to generate them. Wouldn't recommend implementing your own token authentication though. I'll show how to create a whole authentication system here step by step.
For simplicity, let's say we have an exported route in a file auth.js, this route will be a sub-route domain.com/auth, an array of all active refreshTokens and the jwt:
const express = require("express")
const jwt = require("jsonwebtoken")
let route = (exports.route = express())
let refreshTokens = []
What we will do is generate a long-lasting refresh token, which users will be able to use to generate a smaller 15-minute access token. Afterwards, you generate a new access token with the refresh token and so on. But to get the refresh token you need to login or register. Users can also logout killing the refresh token.
route.post("/token", async (req, res) => {
// Input: Refresh Token
// Output: Access Token Generation
})
route.post("/login", async (req, res) => {
// Input: User, Password
// Output: Refresh Token
})
route.delete("/logout", async (req, res) => {
// Input: Token to Remove
})
Let's start with the end. You have a refresh token, you won't to destroy it. Simply filter the array against this token and submit a status. The token becomes unusable after it's cleared from the array, that's the goal here.
route.delete("/logout", async (req, res) => {
refreshTokens = refreshTokens.filter((token) => token != req.body.token)
res.sendStatus(204)
})
With me so far? Now let's jump back to the start. If you log in with an email and password, if they're wrong respond with an error message, if they're correct receive the tokens.
route.post("/login", async (req, res) => {
const username = req.body.username
const password = req.body.password
// This is just a quick demonstration,
// you would have to use the bcrypt hash
// or other hash/salt methods. DO NOT
// STORE passwords plaintext
// Not existent user = Unauthorized
if (username != 'admin') return res.sendStatus(401)
// Wrong Password = Forbidden
if (password != 'abc123') return res.sendStatus(403)
const user = {
id: 0,
username: username,
password: password
}
const accessToken = generateAccessToken(user)
const refreshToken = generateRefreshToken(user)
let result = {
success: true,
accessToken: accessToken,
refreshToken: refreshToken,
}
res.send(result)
})
Now how do we sign the JSON web tokens? Let's take a look at the two methods used here:
function generateAccessToken(content) {
return jwt.sign(content, process.env.ACCESS_TOKEN_SECRET, {
expiresIn: "15m",
})
}
function generateRefreshToken(content) {
const token = jwt.sign(content, process.env.REFRESH_TOKEN_SECRET)
refreshTokens.push(token)
return token
}
Both use some sort of environment tokens, but why? That's the token you will have to generate once for the back end. It will be used as a public key. We simply generate the access tokens for 15 minutes and push the refresh tokens to the array.
route.post("/token", async (req, res) => {
const refreshToken = req.body.token
if (refreshToken == null) return res.sendStatus(401)
if (!refreshTokens.includes(refreshToken)) return res.sendStatus(403)
jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403)
res.json({ accessToken:
generateAccessToken({
id: 0,
username: user.name,
password: user.password
})
})
})
})
We verify the refresh token, if it exists and it is valid, return a new access token for 15 minutes. That's it for the token part, you can login (create refresh token), retrieve an access token and logout (kill refresh token)
How to Use: Authenticate and Authorize
Admin pages should return 403 while the forum board should be different whether you're logging as a guest or an actual user. The first one is authentication, the second authorization.
Let's create two functions for each. Express is quite handy with the next() function
exports.authenticate = function (req, res, next) {
const authHeader = req.headers["authorization"]
const token = authHeader?.split(" ")[1]
jwt.verify(token || "", process.env.ACCESS_TOKEN_SECRET, (err, user) => {
req.user = err ? {} : user
next()
});
};
exports.authorize = function (req, res, next) {
const authHeader = req.headers["authorization"]
const token = authHeader?.split(" ")[1]
if (token == null)
return res.sendStatus(401)
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403)
req.user = user
next()
})
}
Now you're done with the whole authentication system (aside some cleanup's) and probably the registration system. Let's make use of it.
Client side you can create a REST api like so:
POST http://localhost:8081/auth/login
Content-Type: application/json
{
"username": "admin",
"password": "abc123"
}
# Returns refresh and access token.
###
DELETE http://localhost:8081/auth/logout
Content-Type: application/json
{
"token": "REFRESH_TOKEN"
}
# Logs out a user.
###
POST http://localhost:8081/auth/token
Content-Type: application/json
{
"token": "REFRESH_TOKEN"
}
#
# This is how you can provide the access token
# when making a request to say a forum api
#
GET http://localhost:8081/forum/api/board/0
Authorization: Bearer ACCESS_TOKEN
Usage:
route.get("forum/board/:id", authenticate, async (req, res) => {
res.send(req.user)
})
Expected Output when going to localhost:8081/forum/board/7 authenticated:
{id:0,username:"admin",password:"abc123"}
Otherwise:
{}
Nevertheless, do not try implementing your own authentication. Really, you shouldn't.
Source
https://www.youtube.com/watch?v=mbsmsi7l3r4

How to use efficiently Json Web Token in Node JS

I have a node js app. I'm trying to do authorization usin JWT. When my code is as i wrote below, im trying to login with a user in my database but it firstly reach the middleware and it says 'Auth Failed'. But if I delete middleware and login with a user who is in my database it returns a token to me. Then i add the middleware again and i use that token for another user, this time it works and it accepts that token for another user too! But I dont want this. I want that when I login with a user in my database, it should return a unique token for the user and then i should reach the middleware. Here is my codes:
Here is my middleware:
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization.split(" ")[1];
const decodedToken = jwt.verify(token, 'secret_key');
req.userData = decodedToken;
next();
}catch(error) {
return res.status(401).send({
message: 'Auth failed'
});
}
And here is my login code (im creating the token here) if password is true:
const token = jwt.sign({
email:user.email,
password: user.password
},
'secret_key',
{
expiresIn :"2h"
}
)
return res.status(200).send({ message: 'success', token: token});
And in app.js:
const checkAuth = require('../middleware/checkauth');
router.get('/api/company',checkAuth,companyController.list);
How can I do this authorization security using JWT as I want? Any Suggestion ?

JWT Login - No authorization token was found in middleware

I followed a tutorial to add login and registration to my Node.js app using JWT token and I'm having a hard time logging in and redirecting to my 'logged in' admin page. User registration works great, but the login portion I can't figure out.
This is the tutorial I was following:
https://medium.freecodecamp.org/learn-how-to-handle-authentication-with-node-using-passport-js-4a56ed18e81e
My code for login looks like this:
router.post('/login', auth.optional, (req, res, next) => {
console.log(req.body);
var user = {
email: req.body.email,
password: req.body.password
}
if (!user.email) {
return res.status(422).json({
errors: {
email: 'is required',
},
});
}
if (!user.password) {
return res.status(422).json({
errors: {
password: 'is required',
},
});
}
return passport.authenticate('local', { session: false }, (err, passportUser, info) => {
if (err) {
return next(err);
}
if (passportUser) {
const user = passportUser;
user.token = passportUser.generateJWT();
console.log("TOKEN: " + user.token);
res.setHeader('Authorization', 'Token ' + user.token);
return res.json({ user: user.toAuthJSON() });
}
return res.status(400).json({
errors: {
message: info,
},
});
})(req, res, next);
});
My '/admin' "logged in" route looks like this:
router.get("/admin", auth.required, function(req, res) {
res.render('admin', {
user : req.user // get the user out of session and pass to template
});
});
I'm not sure how I can redirect to my '/admin' route while also passing the token because currently I am seeing the following error after logging in. Makes sense since I am not passing the token to the '/admin' route...but how do I do that? :)
UnauthorizedError: No authorization token was found at middleware
Thanks in advance for the help!
EDIT:
Still can't figure this out and don't really understand how this flow is supposed to work...where do the headers need to be set to the token and how do I redirect to my admin page once the login is successful.
Here is my middleware code if this helps:
const getTokenFromHeaders = (req) => {
console.log("REQ: " + JSON.stringify(req.headers));
const { headers: { authorization } } = req;
if(authorization && authorization.split(' ')[0] === 'Token') {
return authorization.split(' ')[1];
}
return null;
};
const auth = {
required: jwt({
secret: 'secret',
userProperty: 'payload',
getToken: getTokenFromHeaders,
}),
optional: jwt({
secret: 'secret',
userProperty: 'payload',
getToken: getTokenFromHeaders,
credentialsRequired: false,
}),
};
Your code does not have a problem. You seem to be confused with the login flow from server to client (Frontend/Web).
Let's first have a look the RESTFUL way of doing it. The article also refers to the same flow.
The RESTFUL API flow looks like this:
User requests for login:
POST: /api/v1/auth/login with username and password in request body.
If successful, user is returned with basic inforamtion and token.
If not, user is returned a 401 (Unauthorized) status code.
The login flow ends here.
The token provided earlier to the user is used to make subsequent calls to the backend, which a user can use to perform different operations on the sustem. In essence, it is the client which requests server for subsequent actions with the token provided in the login request.
So for your case, user after receiving the token should make a request for retrieving admin information from the backend.
But, I am assuming you are rendering views from your server-side and you want to render the admin view once the user is successfully logged in, and that's pretty straight forward.
Instead of your res.json() after successful login. You need to use res.render().
res.render('admin', {
user: user.toAuthJSON() // assuming your user contains the token already
})
Edit:
Since res.render() does not change the url in the browser. For that, you need to use res.redirect(). But the problem is, you can not send context in res.redirect().
To achieve that, you will need to pass in the user token as query paramter. See here.
TL;DR
// assuming you are using Node v7+
const querystring = require('querystring');
const query = querystring.stringify({
token: user.token,
});
const adminRoute = '/admin?' + query;
res.redirect(adminRoute)
And in your admin route, you need to slightly modify the code.
Verify the token belongs to a real user and get user information out of the token.
Render the admin template with user information retrieved from step 1.
router.get("/admin", function(req, res) {
// verify the token
const token = req.query.token;
const user = null;
jwt.verify(token, 'secret', function (err, decoded) {
if (err) {
res.status(401).send('Unauthorized user')
}
// decoded contains user
user = decoded.user
});
res.render('admin', {
user : user
});
});
I'm somewhat new to this as well, but I've got it working as follows.
In your server.js file:
const passport = require("passport");
const JwtStrategy = require("passport-jwt").Strategy;
const ExtractJwt = require("passport-jwt").ExtractJwt;
app.use(passport.initialize());
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = Keys.secretOrKey;
passport.use(
new JwtStrategy(opts, (jwt_payload, done) => {
// somefunction looks up the id in jwt payload and
// supplies passport the authenticated user via the "Done" function
somefunction.user(jwt_payload.id)
.then(user => {
if (user) {
return done(null, user);
}
return done(null, false);
});
})
);
In your API definitions
const jwt = require("jsonwebtoken");
router.post("/login", (req, res) => {
const { userInfo } = req.body;
// userInfo has username and password in it
// anotherFuction validates the user id and password combo
anotherFunction(userInfo.id, userInfo.password)
.then(isAuthenticated => {
if (isAuthenticated) {
const payload = {
id: user.sAMAccountName,
firstname: user.givenName,
lastname: user.sn
};
// Sign Token with the payload
jwt.sign(
payload,
Keys.secretOrKey,
{ expiresIn: 3600 },
(err, token) => {
res.json({
success: true,
token: "Bearer " + token
});
}
);
} else {
// don't mind the statuses ^_^'
return res.status(401).json({ error: "Login failed." });
}
})
.catch(err => {
return res.status(400).json(err);
});
});
After calling the API you want to set the auth token. The following lets you delete the token if nothing is passed in, effectively "Logging out".
const setAuthToken = token => {
if (token) {
// Apply to every request
axios.defaults.headers.common["Authorization"] = token;
} else {
// Delete Auth Header
delete axios.defaults.headers.common["Authorization"];
}
};
If you're trying to use it in the front end, you need to use jwt_decode to pull the values from the token and set it however you deem necessary. If using redux to store login data it should look something like this. As I feel that the discussion of using localstorage for jwtToken is outside of the scope of this, just know would need to check for the token.
if (localStorage.jwtToken) {
setAuthToken(localStorage.jwtToken);
const decoded = jwt_decode(localStorage.jwtToken);
store.dispatch({
type: USER_LOGIN,
payload: decoded
});
}
Hope this helped.
From one beginner in JWT to another. Good luck.

Categories

Resources