finding the user while assigning new access token - jwt

I have a website where when user logsIn, they are assigned an access and a refresh token. When the access token is expried, a request to the server is made and checks if the refresh token is present in the global array in the database. If it is, a new access token is assigned to the user.
But I wanted to ask if should also check for the user by the information given by the refresh token when it is decoded. Or it is not necessary.
Please suggest me good practice and also tell me if something is wrong with my process.
routes.post("/newAccessToken", async (req, res) => {
const token = req.headers.cookie?.split("=")[1];
try {
const existingToken = await refreshTokens.findOne({
tokens: { $in: [token] },
});
if (existingToken) {
const email = await jwt.verify(token, process.env.RefreshTokenSecret);
if (email) {
const user = await userSchema.findOne({ email });
if (user) {
const newAccessToken = await jwt.sign(
{ email },
process.env.AccessTokenSecret
);
res.json({ newAccessToken });
}
} else res.json({ message: "token is invalid" });
} else res.json({ message: "No token found" });
} catch (error) {
console.log(error);
}
});

Related

nextauth v4 credentials provider, adding the raw token to the session

I am new to nextauth credentials provider, and I have been following different tutorials on youtube and searching for answers here.
I have a web application using next.js and in it I have a bunch of rest apis to get data from mongodb. I have secured the api by accessing the token. I have used Postman to test the apis, and they work when I pass the raw token to in the Authorization header.
I need to get the raw token into the session object for the session call back in next-auth, so I then can call the apis from client side pages.
Any help would be appreciated.
In [...nextauth].js:
export default NextAuth({
providers: [
// Google Provider
GoogleProvider({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET
}),
GithubProvider({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET
}),
CredentialsProvider({
id: "credentials",
name: "Credentials",
async authorize(credentials, req) {
console.log("In Authorization");
connectMongo().catch((error) => {
error: "Connection Failed...!";
});
// check user existance
const user = await Users.findOne({ email: credentials.email });
if (!user) {
throw new Error("No user found with this email");
}
// compare()
const checkPassword = await compare(
credentials.password,
user.password
);
// incorrect password
if (!checkPassword || user.email !== credentials.email) {
throw new Error("Email or Password don't match");
}
// check if user is enabled
if (user.active === AccountStatus.DISABLED) {
throw new Error(
"Account has been disabled. Please contact support to re-enable your account"
);
}
// Value returned will go into token property
//console.log("Returnng User Object", user);
return user;
}
})
],
session: {
strategy: "jwt",
maxAge: 60 * 60 * 24
},
callbacks: {
async jwt({ token, user, account, profile, isNewUser }) {
if (user) token.user = user;
if (account) token.accessToken = account.access_token;
return token;
},
async session({ session, token, user, account }) {
// Send properties to the client, like an access_token from a provider.
const { password, ...tokenPwdRemoved } = token.user;
session.user = tokenPwdRemoved;
return session;
}
},
pages: {
signIn: "/login"
}
});
Take a look at the Session callback:
callbacks: {
async session({ session, token, user }) {
// Send properties to the client, like an access_token from a provider.
session.accessToken = token.accessToken
return session
}
}
Keep in mind security concerns relating to the token and session.
Session callback
The session callback is called whenever a session is checked. By
default, only a subset of the token is returned for increased
security. If you want to make something available you added to the
token through the jwt() callback, you have to explicitly forward it
here to make it available to the client.

when creating middleware for jsonwebtoken, its not working, it's showing 403 forbidden even user is valid

here is my code, this is the middleware I'm using to verify jwt token, but it's not working at all
const verifyJWT = (req, res, next) => {
const accessToken = req.headers.authorization;
if (!accessToken) {
res.status(401).send({ message: "Unauthorized Access" });
return;
}
const token = accessToken.split(" ")[1];
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, decoded) => {
if (err) {
res.status(403).send({ message: "Forbidden Access" });
return;
}
req.decoded = decoded;
next();
});
};
//this is my code

Permissions are not showing on facebook login screen

Some how facebook is not showing permissions required even if they are already aloud by facebook team, I'm asking for user_gender user_likes and user_birthday I have all the permissions approved but user_birthday now on the facebook login screen it doesn't show all the permissions required,
here is my code
login = async () => {
try {
this.setState({ showSpinner: true });
const APP_ID = 'XXXXXXXXXX';
const options = {
permissions: ['public_profile', 'user_birthday', 'user_likes', 'email', 'user_gender'],
};
await Facebook.initializeAsync(APP_ID);
const {
type,
token,
expires,
permissions,
declinePermissions,
} = await Facebook.logInWithReadPermissionsAsync(APP_ID, options);
if (type === 'success') {
// console.log('success');
const fields = ['id', 'first_name', 'birthday', 'likes', 'gender'];
// console.log(`fields ${fields}`);
const response = await fetch(`https://graph.facebook.com/me?fields=${fields.toString()}&access_token=${token}`);
const userData = await response.json();
// console.log(`a ver que onda: ${userData.first_name}`);
await this.authenticate(token, userData);
} else {
this.setState({ showSpinner: false });
}
} catch ({ message }) {
alert(`Facebook Login Error: ${message}`);
}
};
It usually show all the permissions required but suddenly it stops showing them.
If someone knows where should I look I'll appreciated.
I figure it out on the await facebook.logInWithReadPermissionsAsync(APP_ID, options) I got rid of APP_ID because it is already initialize in the line await facebook.initializeAsync(APP_ID) and end up with
await Facebook.initializeAsync(APP_ID);
const {
type,
token,
expires,
permissions,
declinePermissions,
} = await Facebook.logInWithReadPermissionsAsync(options);
And it worked again fine! it shows the permissions need it. :)
[![Permissions working][1]][1]

How to log out using jwt token in node backend

I have used jwt token to login
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET);
.
Below is my code for router
router.post("/login", async (req, res) => {
try {
const { email, password } = req.body;
// validate
if (!email || !password)
return res.status(400).json({ msg: "Not all fields have been entered." });
const user = await Customer.findOne({ email: email });
if (!user)
return res
.status(400)
.json({ msg: "No account with this email has been registered." });
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(400).json({ msg: "Invalid credentials." });
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET);
res.json({
token,
user: {
id: user._id,
displayName: user.displayName,
},
});
} catch (err) {
res.status(500).json({ error: err.message });
}
});
Can anybody provide code for loging out using jwt token
You just need to invalidate your jwt token in logout
more than one way you can achieve. Here I am going to explain a couple of ways
1.storing token in an array. In log out, you can remove the token
const refreshTokens = []; --> global declaration
in login, before res.json({...});
refreshTokens.push(refreshToken);
the constraint here is jwt tokens are time-bounded. You need to get a refresh token if old token expires. Whenever you issue a refresh token you need to remove the old and push the latest
router.post('/refreshtoken', function (req, res) {
const { token } = req.body;
if (!token) {
return res.sendStatus(401);
}
if (!refreshTokens.includes(token)) {
return res.sendStatus(403);
}
jwt.verify(token, refreshTokenSecret, (err, user) => {
if (err) {
return res.sendStatus(403);
}
const accessToken = jwt.sign({ username: user.username, role: user.role }, accessTokenSecret, { expiresIn: '20m' });
refreshTokens = refreshTokens.filter(token => t !== token);
refreshTokens.push(accessToken);
res.json({
accessToken
});
});
});
In Logout you need to invalidate token
app.post('/logout', (req, res) => {
const { token } = req.body;
refreshTokens = refreshTokens.filter(token => t !== token);
res.send("Logout successful");
});
2.Store token in cookie whenever you log in or reissues the token. verify jwt token from cookie instead of reading from headers.
res.cookie('jwt_token', token, {
expires: new Date(Date.now() + expiration),
secure: false, // set to true if your using https
httpOnly: true,
});
In Logout destroy the cookie
router.get('/logout', function (req, res) {
res.clearCookie('jwt_token');
req.session.destroy();
});

JWT - Why it generates extraordinary long tokens

Everything was working fine until I decided to save the tokens (as String) with the users. The tokens were used to be 5 lines long max, and now it is keep growing every time I refresh the token. The last token I generated was 100 lines long which is not acceptable.
Every time the user logs in, I am refreshing the token.
module.exports.login = function(req, res){
var user_name = req.body.username;
var password = req.body.password;
User.findOne({username: user_name}, function(err, user){
if(err || !user) {
return res.json({error: "cannot find the user"});
} else{
user.comparePassword(password, function(err, isMatch){
if (err){
return res.json({
error: "passowrd doesn't match"
});
}
});
var token = jwt.sign(user, process.env.SECRET, {
expiresIn: 4000
});
console.log(token); // printing the token
}
if(!token){
res.json({
success: false,
username: null,
token: null
});
}
else {
user.token = token;
User.updateUser(user._id, user, {new: true},function(err, updated_user){
res.json({
success: true,
username: user.username,
token: token
});
});
}
});
};
All the routes are secured, and it needs to verify the token for each request.
module.exports.secured = function(req, res, next){
var token;
var username = req.body.req_username || req.headers['req_username'];
if(username){
User.findOne({ 'username': username }, function (err, user) {
if (err || !user)
return res.json({
error: "cannot find the user"
});
else
token = user.token;
jwt.verify(token, process.env.SECRET, function(err, decode){
if(err){
res.status(500).send({
error: "wrong token or username"
});
} else{
next();
}
});
});
} else{
res.send({
error: "not found"
});
}
};
I think I am not refreshing the tokens correctly.
The token should not be stored in the server because it wastes unnecessary resources.
user.token = token;
User.updateUser(user._id, user, {new: true},function(err, updated_user){
res.json({
success: true,
username: user.username,
token: token
});
});
Each time the token is refreshed you encode the user variable that includes the previously issued token, therefore it grows
var token = jwt.sign(user, process.env.SECRET, {
expiresIn: 4000
});
Remove user.token = token
But the main issue is that the client MUST send the JWT in each request, not the username to recover the token. You are verifying the token stored in user entity. It does not make sense. Change the client code to.send the JWT in the headers instead of the username req.headers['req_username']