I have a controllerfile where I use passport.authenticate. I declare my payload and sign my token now i need the info declared in the payload in another file so I could use them in my sql request.
Here's the code for the login auth :
login: (req, res, next) => {
console.log(" login");
passport.authenticate("local", { session: false }, (error, user) => {
console.log("executing callback auth * from authenticate for local strategy ");
//if there was an error in the verify callback related to the user data query
if (error || !user) {
next(new error_types.Error404("Email ou Mot de passe invalide !"))
}else {
console.log("*** Token generation begins ***** ");
console.log(user)
const payload = {
sub: user.id,
exp: Date.now() + parseInt(process.env.JWT_LIFETIME),
email: user.email,
name: user.prenom,
lastName: user.nom,
type:user.type,
};
const token = jwt.sign(JSON.stringify(payload), process.env.JWT_SECRET, {algorithm: process.env.JWT_ALGORITHM});
res.json({ token: token,type: user.type,userid:user.id });//added userid
}
})(req, res);
}
Now in my other file i need to get the user.id and user.type so that i could use them in my request :
const createProp=(req, res, next) => {
let con=req.con
let { xx,yy } = req.body;
con.query('INSERT INTO tab1
(xx,yy,user_id,user_type ) VALUES ($1, $2, $3, $4) ',[xx,yy,user_id,user_type],
(err, results) => {
if (err) {
console.log(err);
res.status(404).json({error: err});
}
else
{res.status(200).send(`success`)}
}
);
}
in my frontend VUEJS this is my file:
import router from '#/router'
import { notification } from 'ant-design-vue'
import JwtDecode from "jwt-decode";
import apiClient from '#/services/axios'
import * as jwt from '#/services/jwt'
const handleFinish = (values) => {
const formData = new FormData()
for (var key of Object.keys(formState)) {
formData.append(key, formState[key])//here im appending some fields in my
//form i have more than just xx,yy files i just put them as an
//example
}
const token = localStorage.getItem("accessToken");
var decoded = JwtDecode(token);
console.log(decoded)
formData.append('user_id',decoded.sub)
formData.append('user_type',decoded.type)
fileListFaisabilite.value.forEach((file) => {
formData.append('xx', file)
})
fileListEvaluation.value.forEach((file) => {
formData.append('yy', file)
})
// store.dispatch('user/PROPOSITION', formData)
}
methods:{
PROPOSITION({ commit, dispatch, rootState }, formData ) {
commit('SET_STATE', {
loading: true,
})
const proposition=
mapAuthProviders[rootState.settings.authProvider].proposition
proposition(formData)
.then(success => {
if (success) {
notification.success({
message: "Succesful ",
description: " form submited!",
})
router.push('/Accueil')
commit('SET_STATE', {
loading: false,
})
}
if (!success) {
commit('SET_STATE', {
loading: false,
})
}
})
return apiClient
.post('/proposition', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
.then(response => {
if (response) {
return response.data
}
return false
})
.catch(err => console.log(err))
},
},
What im looking for is how i can store in my database the userid and usertype using insertinto sql request.
You can set user data in your jwt sign function without stringify method:
const payload = {
sub: user.id,
exp: Date.now() + parseInt(process.env.JWT_LIFETIME),
email: user.email,
name: user.prenom,
lastName: user.nom,
type: user.type // <-- Add this
};
const token = jwt.sign(
payload, // Don't use JSON.stringify
process.env.JWT_SECRET,
{algorithm: process.env.JWT_ALGORITHM}
);
And access user info:
jwt.verify(token, process.env.JWT_SECRET, (err, payload) => {
if (err) {
// Handle error
}
// Get some data
let user_id = payload.sub;
let user_type = payload.type;
console.log(user_id, user_type);
next();
});
The vue file:
PROP({ commit, dispatch, rootState }, payload ) {
commit('SET_STATE', {
loading: true,
});
const prop = mapAuthProviders[rootState.settings.authProvider].prop
prop(payload)
.then(success => {
if (success) {
// success contains user information and token:
const { token, userid, type } = success;
// Save to localStorage (Optional)
localStorage.setItem("accessToken", token);
localStorage.setItem("userid", userid);
localStorage.setItem("type", type);
// This not works if don't have a JWT SEED
// var decoded = JwtDecode(token);
commit('SET_STATE', {
user_id: userid,
user_type: type,
})
//dispatch('LOAD_CURRENT_ACCOUNT')
notification.success({
message: "Succesful ",
description: " form submited!",
})
router.push('/Home')
commit('SET_STATE', {
loading: false,
})
}
if (!success) {
commit('SET_STATE', {
loading: false,
})
}
})
},
The api call file:
export async function prop(payload) {
try {
const response = await apiClient.post('/prop', payload, {
headers: { 'Content-Type': 'multipart/form-data'},
});
if (response) {
return response.data;
}
} catch (err) {
console.log(err);
}
return false;
}
Related
When I console.log the variable refToken before the get request the refToken shows what it contains but after that nothing, and my backend sends me the error 401 which means no token was provided!!
I am really confused here
utility file for expo-secure-store
import * as SecureStore from 'expo-secure-store';
const saveRefreshToken = async (value: string) => {
try {
await SecureStore.setItemAsync("refreshToken", value);
} catch (e) {
console.log("Cannot set refresh token")
}
}
const getRefreshToken = async () => {
try {
return await SecureStore.getItemAsync("refreshToken");
} catch (e) {
console.log("can't get requested refreshToken", e);
}
}
const deleteRefreshToken = async () => {
try {
await SecureStore.deleteItemAsync("refreshToken");
} catch (e) {
console.log("cannot delete refreshToken ", e);
}
}
export default {
saveRefreshToken,
getRefreshToken,
deleteRefreshToken
};
React native code
import axios from "../api/axios";
import useAuth from "./useAuth";
import storage from "../utility/storage";
import jwtDecode from "jwt-decode";
const useRefreshToken = () => {
const {setAuth, auth} = useAuth();
return async () => {
const refToken = await storage.getRefreshToken();
// withCredentials: true,
const response = await axios.get(`/auth/refresh/`, {
params: {refreshToken: refToken},
});
//#ts-ignore
setAuth({user: jwtDecode(response.data.accessToken), accessToken: response.data.accessToken});
return response.data.accessToken;
};
}
export default useRefreshToken;
Node.js backend part that deals with this request
// TODO: Generate new access token from refresh
export const refreshTokenSignIn = (
req: express.Request,
res: express.Response,
next: express.NextFunction
) => {
// const refreshToken = req.cookies.jwt;
const refreshToken = req.body.refreshToken;
try {
// Check if the refresh token is not null
if (!refreshToken)
return res.status(401).json({message: "No token provided!"});
// console.log(refreshToken);
const sql = `select * from token_records where refresh_token = '${refreshToken}'`;
// Check if there is such refresh token for the current user
db.query(
sql,
[refreshToken],
(err: QueryError, result: RowDataPacket[]) => {
if (err) return next(err);
if (result.length === 0)
return res.status(403).json({message: "You don't have access"});
//#ts-ignore
jwt.verify(
refreshToken,
//#ts-ignore
process.env.JWT_REFRESH_SECRET,
(err2: VerifyErrors, user: JwtPayload) => {
if (err2) return res.status(403).json({message: "You don't have access"});
const accessToken = jwt.sign(
{
user_id: user.user_id,
firstname: user.firstname,
lastname: user.lastname,
username: user.username,
email: user.email,
role: user.role,
},
//#ts-ignore
process.env.JWT_SECRET
);
res.status(200).json({accessToken: accessToken});
next();
}
);
}
);
} catch (e) {
console.log(e);
next();
}
};
const login = (req, res) => {
// console.log(req.body);
// let email = req.body.email.toLowerCase();
sequelize.models.User.findOne({
where: {
email: req.body.email,
},
})
.then(async (user) => {
if (!user) {
// console.log(" email not found is true");
return res.status(401).json({
success: false,
message: " Authentication failed, Wrong Credentials",
});
}
if (user.isActive == false) {
// console.log("user is not activated", user.isActive);
return res.status(400).json({
success: false,
message: "account is not activated",
});
}
console.log("test entry");
await user.comparePassword(req.body.password, async (err, isMatch) => {
console.log(req.body.password);
if (isMatch && !err) {
console.log("user crap");
// role_id: user.role_id,
const payload = {
user_id: user.user_id,
};
const options = {
expiresIn: "10day",
};
const token = await jwt.sign(payload, process.env.SECRET, options);
console.log("sssssss", payload);
if (user.twoFactorAuth == false) {
return res.json({
success: true,
token,
});
} else {
// let mobile = user.phone;
await twoFactorAuth(user); // we call the 2fa that will send a otp to the users cellphone
// console.log("after cb");
}
} else {
return res.json({
success: false,
msg: "Authentication failed.",
});
}
});
// console.log("user crap", user.user_id);
})
.catch((error) => {
return res.status(400).send(error);
});
};
const twoFactorAuth = async (user) => {
var data = qs.stringify({
sender: "hehe",
mobile: user.phone,
channel: "sms",
});
var config = {
method: "POST",
url: "https://blablabla",
headers: {
Authorization: "Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
},
data: data,
};
axios(config)
.then( async function (response) {
console.log(JSON.stringify(response.data));
// await verifyTwoFactorAuth (realToken)
})
.catch(function (error) {
console.log(error);
});
};
const verifyTwoFactorAuth = async(req, res) => {
//console.log("tet",req);
let otpcode = req.body.otpcode;
let mobile = req.body.mobile;
var data = qs.stringify({ mobile: mobile, code: otpcode });
var config = {
method: "POST",
url: "https://blablabla",
headers: {
Authorization: "Bearer xxxxxxxxxxxxxxxxxxxxxxxx",
},
data: data,
};
axios(config)
.then(async function (response) {
console.log(JSON.stringify(response.data));
if (response.data.code == 63 || response.data.status == 200) {
return res.json({
success: true,
token,
});
} else if (response.data.code == 21 || response.data.status == 422) {
return res.status(400).json({
success: false,
message: "wrong code, check your sms again",
});
}
})
.catch(function (error) {
console.log(error);
});
};
Hello, I am looking for a structure solution to how I should implement what I want.
Scenario: user try to login, system checks for username and passoword and generates the TOKEN, system finds that 2fa is active in users settings, system sends OTP to users cellphone.
Now my struggle begins, I am not sure what to do next, I thought about storing the token in users fields as tempToken then i look for the user via users mobile and extract the token that way, but I dont believe that this is best practice.
Any ideas of how to tackle this would be appreciated ! thank you
Currently I'm messing around with JWT tokens, authentication and authorization. I'm creating a JWT token and refresh tokens. I set the refresh token in a cookie. Creating and logging in a user works as intended. However trying to refresh the token and updating it, it goes wrong.
/login
router.post("/login", passport.authenticate("local"), (req, res, next) => {
const token = getToken({
_id: req.user._id
})
const refreshToken = getRefreshToken({
_id: req.user._id
})
User.findById(req.user._id).then(
user => {
user.refreshToken.push({
refreshToken
})
user.save((err, user) => {
if (err) {
res.statusCode = 500
res.send(err)
} else {
res.cookie("refreshToken", refreshToken, COOKIE_OPTIONS)
res.send({
success: true,
token
})
}
})
},
err => next(err)
)
})
/refreshToken
router.post("/refreshToken", (req, res, next) => {
console.log("in refreshtoken")
const { signedCookies = {} } = req;
const { refreshToken } = signedCookies
console.log(refreshToken)
if (refreshToken) {
try {
const payload = jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET)
const userId = payload._id
User.findOne({
_id: userId
}).then(
user => {
if (user) {
// Find the refresh token against the user record in database
const tokenIndex = user.refreshToken.findIndex(
item => item.refreshToken === refreshToken
)
if (tokenIndex === -1) {
res.statusCode = 401
res.send("Unauthorized1")
} else {
const token = getToken({
_id: userId
})
// If the refresh token exists, then create new one and replace it.
const newRefreshToken = getRefreshToken({
_id: userId
})
user.refreshToken[tokenIndex] = {
refreshToken: newRefreshToken
}
user.save((err, user) => {
if (err) {
res.statusCode = 500
res.send(err)
} else {
res.cookie("refreshToken", newRefreshToken, COOKIE_OPTIONS)
res.send({
success: true,
token
})
}
})
}
} else {
res.statusCode = 401
res.send("Unauthorized2")
}
},
err => next(err)
)
} catch (err) {
res.statusCode = 401
res.send("Unauthorized3")
}
} else {
res.statusCode = 403
res.send("Unauthorized4")
}
})
Cookie options
exports.COOKIE_OPTIONS = {
httpOnly: true,
secure: false,
signed: false,
maxAge: eval(process.env.REFRESH_TOKEN_EXPIRY) * 1000,
sameSite: "none",
}
App.js
function App() {
const [currentTab, setCurrentTab] = useState("login")
const [userContext, setUserContext] = useContext(UserContext)
const verifyUser = useCallback(() => {
fetch(process.env.REACT_APP_API_ENDPOINT + "users/refreshToken", {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
}).then(async response => {
console.log(response)
if (response.ok) {
const data = await response.json()
console.log(data)
setUserContext(oldValues => {
return { ...oldValues, token: data.token }
})
} else {
setUserContext(oldValues => {
return { ...oldValues, token: null }
})
}
// call refreshToken every 20 seconds to renew the authentication token. Make it longer for prod.
setTimeout(verifyUser, 20000)
}).catch((error) => {
console.log(error)
})
}, [setUserContext])
useEffect(() => {
verifyUser()
}, [verifyUser])
/**
* Sync logout across tabs
*/
const syncLogout = useCallback(event => {
if (event.key === "logout") {
// If using react-router-dom, you may call history.push("/")
window.location.reload()
}
}, [])
useEffect(() => {
window.addEventListener("storage", syncLogout)
return () => {
window.removeEventListener("storage", syncLogout)
}
}, [syncLogout])
return userContext.token === null ? (
<Card elevation="1">
<Tabs id="Tabs" onChange={setCurrentTab} selectedTabId={currentTab}>
<Tab id="login" title="Login" panel={<Login />} />
<Tab id="register" title="Register" panel={<Register />} />
<Tabs.Expander />
</Tabs>
</Card>
) : userContext.token ? (
<Welcome />
) : (
<Loader />
)
}
I'm unsure what causes it that when I console.log(refreshToken) in /refreshToken it comes out as undefined. I also tried looking it up in any responses in the developer tools but couldn't find it.
Would really appreciate if anyone has some clarity about what might cause this issue.
I try to make authorization and permissions availlable with react-admin and a Node server:https://github.com/hagopj13/node-express-mongoose-boilerplate
For react-admin there is an exemple of code: https://marmelab.com/react-admin/Authorization.html#configuring-the-auth-provider
// in src/authProvider.js
import decodeJwt from 'jwt-decode';
export default {
login: ({ username, password }) => {
const request = new Request('https://example.com/authenticate', {
method: 'POST',
body: JSON.stringify({ username, password }),
headers: new Headers({ 'Content-Type': 'application/json' }),
});
return fetch(request)
.then(response => {
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
return response.json();
})
.then(({ token }) => {
const decodedToken = decodeJwt(token);
localStorage.setItem('token', token);
localStorage.setItem('permissions', decodedToken.permissions);
});
},
logout: () => {
localStorage.removeItem('token');
localStorage.removeItem('permissions');
return Promise.resolve();
},
checkError: error => {
// ...
},
checkAuth: () => {
return localStorage.getItem('token') ? Promise.resolve() : Promise.reject();
},
getPermissions: () => {
const role = localStorage.getItem('permissions');
return role ? Promise.resolve(role) : Promise.reject();
}
};
But i don't understand how it work and on login the server return an user object like this:
{user: {id: "5e429d562910776587c567a2", email: "admin#test.com", firstname: "Ad", lastname: "Min",…},…}
tokens: {access: {,…}, refresh: {,…}}
access: {,…}
expires: "2020-03-03T06:45:10.851Z"
token: "eyJhbGciOi..."
refresh: {,…}
expires: "2020-04-02T06:15:10.851Z"
token: "eyJhbGciOi..."
user: {id: "5e429d562910776587c567a2", email: "admin#test.com", firstname: "Ad", lastname: "Min",…}
createdAt: "2020-02-11T12:25:58.760Z"
email: "admin#test.com"
firstname: "Ad"
id: "5e429d562910776587c567a2"
lastname: "Min"
role: "admin"
updatedAt: "2020-02-11T12:25:58.760Z"
There are already tokens and role and in the server, it seems to have a permission control:
role.js
const roles = ['user', 'admin'];
const roleRights = new Map();
roleRights.set(roles[0], []);
roleRights.set(roles[1], ['getUsers', 'manageUsers']);
module.exports = {
roles,
roleRights,
};
And the auth.js
const passport = require('passport');
const httpStatus = require('http-status');
const AppError = require('../utils/AppError');
const { roleRights } = require('../config/roles');
const verifyCallback = (req, resolve, reject, requiredRights) => async (err, user, info) => {
if (err || info || !user) {
return reject(new AppError(httpStatus.UNAUTHORIZED, 'Please authenticate'));
}
req.user = user;
if (requiredRights.length) {
const userRights = roleRights.get(user.role);
const hasRequiredRights = requiredRights.every(requiredRight => userRights.includes(requiredRight));
if (!hasRequiredRights && req.params.userId !== user.id) {
return reject(new AppError(httpStatus.FORBIDDEN, 'Forbidden'));
}
}
resolve();
};
const auth = (...requiredRights) => async (req, res, next) => {
return new Promise((resolve, reject) => {
passport.authenticate('jwt', { session: true }, verifyCallback(req, resolve, reject, requiredRights))(req, res, next);
})
.then(() => next())
.catch(err => next(err));
};
module.exports = auth;
But how to get authorization and permission works from the react-admin?
Thanks & Regards
Ludo
In react-admin there is a usePermissions() hook, which calls the authProvider.getPermissions() method on mount. This method return to you single string value like 'admin' or string array of permissions like ['admin','crm'...]. This strings are up to you how to set. In example below it's storing in localStorage, but in real life need to extract it from JWT token or retrieve it from backend.
getPermissions: () => {
const role = localStorage.getItem('permissions');
return role ? Promise.resolve(role) : Promise.reject();
}
import { usePermissions } from 'react-admin';
in function
const { permissions } = usePermissions();
return (
{ permissions == 'admin' &&
<DashboardMenuItem primaryText="Dashboard" onClick={onMenuClick} sidebarIsOpen={open} />
}
...
);
I have this User schema:
email: {
type: String,
required: true
},
name: {
type: String,
required: true
},
password: {
type: String,
required: true
}
When you do a POST (/api/user-add), I want all the fields to be required. But when I do a login (/api/login) then I only need the email and password fields. My problem is, in my login code I eventually get to this function:
staffSchema.methods.generateToken = function(callback) {
var token = jwt.sign(this._id.toHexString(), config.SECRET);
this.token = token;
this.save(function(err, staff) {
if (err) return callback(err);
callback(null, staff);
});
}
And here it thows an error because the name field is required. How do I bypass this. I am looking for something like this I assume:
this.save(function(err, staff) {
if (err) return callback(err);
callback(null, staff);
}).ignoreRequired('name');
When You Login using JWT token this is a basic example to generate token and authenticate user without store token
Note :
Example to authenticate the user without store token in DB
*Login Method
const jwt = require('./jwt');
userCtr.authenticate = (req, res) => {
const {
email, password,
} = req.body;
const query = {
email: email,
};
User.findOne(query)
.then((user) => {
if (!user) {
//return error user not found.
} else {
if (passwordHash.verify(password, user.password)) { // verify password
const token = jwt.getAuthToken({ id: user._id });
const userData = _.omit(user.toObject(), ['password']); // return user data
return res.status(200).json({ token, userData });
}
//return error password not match
}
})
.catch((err) => {
});
};
*jwt.js
const jwt = require('jwt-simple');
const logger = require('./logger');
const jwtUtil = {};
jwtUtil.getAuthToken = (data) => {
return jwt.encode(data, process.env.JwtSecret);
};
jwtUtil.decodeAuthToken = (token) => {
if (token) {
try {
return jwt.decode(token, process.env.JwtSecret);
} catch (err) {
logger.error(err);
return false;
}
}
return false;
};
module.exports = jwtUtil;
*use middleware to prevent another route to access.
userRouter.post('/update-profile', middleware.checkUser, userCtr.updateProfile);
*middleWare.js
middleware.checkUser = (req, res, next) => {
const { headers } = req;
if (_.isEmpty(headers.authorization)) {
//return error
} else {
const decoded = jwt.decodeAuthToken(headers.authorization.replace('Bearer ', ''));
if (decoded) {
User.findOne({ _id: decoded.id })
.then((user) => {
if (user) {
req.user = user;
next();
} else {
//error
}
})
.catch((err) => {
//errror
});
req.user = decoded;
} else {
//error
}
}
};