Hello everyone I'd like to get some tips on how to solve my problem.
I got table where I stores user details like:
ID, FIRST_NAME, LAST_NAME EMAIL, PASSWORD, ROLE, DOMAIN
also got table named Aliases where I got
ID, DOMAIN_ID, DOMAIN, DESTINATION
In user table role means admin, moderator, user etc. and Domain for example is aaa.pl, bbb.pl. Also Aliases table got domain like aaa.pl, bbb.pl
I want to select from table only aliases where the domain is the same as assigned to the user.
So user X can sees only aliases Where is the same domain.
backend
This is my controller
public async aliasesListByDomain(req: Request, res: Response): Promise<void> {
const { domain } = req.params;
const aliasesListByDomain = await pool.query('SELECT * FROM virtual_aliases WHERE domain= ?', [domain]);
if (aliasesListByDomain.length > 0) {
res.json(aliasesListByDomain);
} else {
res.status(404).json({ message: "Alias doesn't exists" });
}
}
There is how I authenticate user
router.post('/authenticate', (req, res) => {
User.findOne({
where: {
email: req.body.email,
password: req.body.password
}
})
.then(user => {
if (user) {
let token = jwt.sign(user.dataValues, process.env.SECRET_KEY, {
expiresIn: 1440
})
res.json({ token: token })
} else {
res.send('User does not exist')
}
})
.catch(err => {
res.send('error: ' + err)
})
})
And now I have problem with my frontend.
This is my Service where I get all aliases ( no matter of domain )
getAliases(): Observable<Alias> {
return this.http.get(`${this.API_URI}/aliases`);
}
and there is my component where I get all Aliases
getAliases() {
this.aliasesService.getAliases().subscribe(
res => {
this.alias = res;
console.log(this.alias);
},
err => console.error(err)
);
}
Now how can I select only aliases based on users permission.
I tried something like this:
Service
getAliasesByDomain(domain: string): Observable<Alias> {
return this.http.get(`${this.API_URI}/aliases/${domain}`);
}
This is my auth service
to login
public login(user: TokenPayload): Observable<any> {
const base = this.http.post(`http://localhost:3000/users/authenticate`, user);
const request = base.pipe(
map((data: TokenResponse) => {
console.log(data);
if (data.token) {
this.saveToken(data.token);
}
return data;
})
)
return request;
}
and UserDetails
public getUserDetails(): UserDetails {
const token = this.getToken();
let payload;
if (token) {
payload = token.split('.')[1];
payload = window.atob(payload);
return JSON.parse(payload);
} else {
return null;
}
}
Should I in my component get usertoken then JSON.parse() it and get domain
details from logged user? Then send it to api?
What is best solution for this?
-Edit
I did something like this:
private getToken(): string {
if (!this.token) {
this.token = localStorage.getItem('usertoken');
}
return this.token;
}
getAliasesByDomain(){
const token = this.getToken();
let user;
if (token) {
user = token.split('.')[1];
user = window.atob(user);
user = JSON.parse(user);
console.log('user z from getAliasesByDomain: '+user.domain);
this.aliasesService.getAliasesByDomain(user.domain).subscribe(
res => {
console.log(res);
this.alias = res;
},
err => console.error(err)
);
}
}
Related
In my backend is something happening, which I can't understand. If I'm registering a new User, it's working fine, and I can see the new User in my JSON File, but if I'm doing a put request after that to change my own user's data he deletes the new User which I made before?
My put request from my frontend:
//Changing user Data
export async function changeData(id, body) {
try {
await axios.put(`http://localhost:8000/users/${id}`, body, {
headers: {
'Content-Type': 'application/json',
'Authorization': localStorage.getItem('auth._token.local')
}
});
return true;
}
catch (e) {
return false;
}
}
My endpoint in my node backend for registering a user and changing data of a user
// Register New User
server.post('/register', (req, res) => {
console.log("register with request body", req.body)
const {username, password, firstname, lastname, roles} = req.body
if(!username || !password || !firstname || !lastname || !roles) {
const status = 400
const message = "Bad Request, make sure all properties are set in request body"
res.status(status).json({status, message})
return
}
if (req.headers.authorization === undefined || req.headers.authorization.split(' ')[0] !== 'Bearer') {
const status = 401
const message = 'Error in authorization format'
res.status(status).json({status, message})
return
}
// Send only token part to admin check
if(!isAdmin(req.headers.authorization.split(' ')[1])) {
const status = 401
const message = 'Only permitted by admin'
res.status(status).json({status, message})
return
}
if(isAuthenticated({username, password}) === true) {
const status = 401
const message = 'Email and Password already exist'
res.status(status).json({status, message})
return
}
fs.readFile("./users.json", (err, file) => {
if (err) {
const status = 401
const message = err
res.status(status).json({status, message})
return
}
// Get current users data
const data = JSON.parse(file.toString())
// Get the id of last user
const last_item_id = data.users[data.users.length - 1].id
//Add new user
data.users.push({id: last_item_id + 1, username: username, password: password, firstname: firstname, lastname: lastname, roles: roles}) //add some data
const writeData = fs.writeFile("./users.json", JSON.stringify(data), (err, result) => { // WRITE
if (err) {
const status = 401
const message = err
res.status(status).json({status, message})
}
})
})
res.status(201).json({status: 201, message: "Successfully created"})
})
// handle changing user data
server.use((req, res, next) => {
console.log('Entering Users')
if(req.method === 'PUT' && req.url.includes("/users")) {
if(req.body) {
const decodedToken = jwt.decode(req.headers.authorization.split(' ')[1])
const userList = JSON.parse(fs.readFileSync('./users.json', 'UTF-8'))
const userinfo = userList.users.find((user) => user.id === decodedToken.id)
if(!req.body.password) {
req.body.password = userinfo.password
}
// if admin made the request, he should be able to change roles
if(req.body.roles && decodedToken.roles && decodedToken.roles.includes("admin")) {
console.log("Able to change");
next()
return
}
req.body.roles = decodedToken.roles
} else {
res.status(400).json(
{
status: 400,
message: "Bad request, make sure all properties are set in request body"
}
)
return
}
}
next()
})
The only thing i noticed is that after the register comes, the JSON file gets to a one-liner, but I don't think that this is the problem. It seems like the put works with an old user List? I'm not sure. Thanks in forward.
I am trying to verify user emails with JWT. My current set up is that a JWT is sent to a user when they try to log in if they do not have a confirmed email.
When the email is sent it composes a URL with the token and then sends the request to the server to verify the email. It worked great in postman as I could easily add the email that I want to verify in the body. But I can't think of a way how to do it in the browser.
This is the code that should verify the email.
confirmEmail = async (req, res, next) => {
const { email } = req.body
const param = req.params.token
const user = await userModel.findOne({email})
if(!user)
{
throw new HttpException(401, 'User not found')
}
if(user.confirmed)
{
throw new HttpException(401, 'User already confirmed')
}
if(!user.confirmed)
{
const confirmJWT = jwt.verify(param, process.env.SECRET_JWT)
if(!confirmJWT)
{
throw new HttpException(200, 'Token invalid')
}
const result = await userModel.emailConfirmed(email)
}
res.send('Database updated.')
}
This is the code that generates the JWT and sends it in an email.
if(!user.confirmed)
{
const emailToken = jwt.sign(
{
email: user.email
},
process.env.SECRET_JWT,
{
expiresIn: '15m'
}
)
console.log(emailToken)
emailModel.verifyEmail(email, emailToken)
throw new HttpException(401, 'Email not confirmed')
}
I was wondering if there is any way I can use the just the token to find the email of the user or is that not possible with JWT?
export const verifyEmail = () => {
try
{
return API()
.post(`/api/confirm/:token`, {}, {
params: {
token: store.user.authToken
},
email: store.user.email
})
.then(({data: userData}) => {
console.log('worked')
})
}
catch(error)
{
console.log(error)
}
}
import { verifyEmail } from '../../services/authAPI'
import { useUserStore } from '../../stores/user'
const store = useUserStore()
export default {
data()
{
return {
email: store.user.email
}
},
methods: {
async handleSubmit()
{
try
{
const response = await verifyEmail(this.email)
}
catch(err)
{
console.log(err)
}
}
}
}
</script>
Basically you do not need to send the email in the body as already encoded the email into the JWT. Once you do const verifiedToken = jwt.sign(token, secret key) You can do verifiedToken.email to grab the email.
i am using jwt authentication in my demo app.https://musflix.netlify.app
my demo application github url:https://github.com/danimadmolil/musify
i use my forked version of json-serve-auth https://github.com/danimadmolil/json-server-auth
i make a change to original json-serve-auth to support jwt auth throught cookies.
login function is like so:
(req, res, next) => {
const { email, password } = req.body as User
const { db } = req.app
if (db == null) {
throw Error('You must bind the router db to the app')
}
const user = db.get('users').find({ email }).value() as User
if (!user) {
res.status(400).jsonp('Cannot find user')
return
}
bcrypt
.compare(password, user.password)
.then((same) => {
if (!same) throw 400
return new Promise<string>((resolve, reject) => {
jwt.sign(
{ email },
JWT_SECRET_KEY,
{ expiresIn: JWT_EXPIRES_IN, subject: String(user.id) },
(error, accessToken) => {
if (error) reject(error)
else resolve(accessToken!)
}
)
})
})
.then((accessToken: string) => {
const { password: _, ...userWithoutPassword } = user
res.cookie('Authorization', `Bearer ${accessToken}`, {
sameSite: 'none',
secure: true,
httpOnly: true,
domain: req.hostname,
path:"/",
expires:new Date(Date.now() + 24 * 60 * 60 * 1000)
})
res.status(200).jsonp({ accessToken, user: userWithoutPassword })
})
.catch((err) => {
if (err === 400) res.status(400).jsonp('Incorrect password')
else next(err)
})
}
the code for using cookie in sever.js file in main app is like so
server.get("/getAllPlaylists", (req, res) => {
let { db } = req.app;
try {
var { id: userId } = getUserByCookie(req);
console.log("useriD", userId);
if (userId) {
return res.send(getUserPlaylists(db, userId));
} else {
return res.send("you are not fucking authorized");
}
} catch (e) {
res.statusCode = 200;
return res.send("error");
}
});
function getUserByCookie(req) {
let { db } = req.app;
const [schema, token] = req.cookies.Authorization
? req.cookies.Authorization.split(" ")
: [undefined, undefined];
if (token && schema) {
const { email } = jwt.verify(token, constants.JWT_SECRET_KEY);
const user = db.get("users").find({ email }).value();
return user;
}
return undefined;
}
there is no problem with android devices and windows systems.
the problem is when i use iphone devices (like iphone 7 or newest) when use try to login it should set a Authorization cookie to browser and then on each request (i use credential:"include" with fetch api) browser should send Authorization cookie to serve. but only on iphone devices it not sending that
i know it is a long question,i am sorry
any one can help me please?
In my project, I've different roles (seller/user/admin)and i want to check the role and redirect to specific page if they are seller for example.
I struggle on how i can check the role in Mongo DB before the login. My login page is basic email-password and submit button.
for my signup all is good, it's use the correct model and post it in the DB.
here are some pieces of my code:
(client model)
userSchema.statics.login = async function (email, password, role) {
const user = await this.findOne({ email });
if (user) {
const auth = await bcrypt.compare(password, user.password);
if (auth) {
return user;
}
throw Error("incorrect password");
}
throw Error("incorrect email");
};
const ClientModel = mongoose.model("client", userSchema, "users");
login controller:
module.exports.clientSignIn = async (req, res) => {
const { email, password } = req.body;
try {
const user = await LoginModel.login(email, password);
const token = createToken(user._id);
res.cookie("jwt", token, { httpOnly: true, maxAge });
res.redirect('/success');
} catch (err) {
console.log(err.message);
}
};
thanks in advance for your help, if you need more info please feel free to ask
Following #EAzevedo 's advice.
i just change my Controller
module.exports.clientSignIn = async (req, res) => {
const { email, password } = req.body;
try {
const user = await LoginModel.login(email, password);
const token = createToken(user._id);
res.cookie("jwt", token, { httpOnly: true, maxAge });
if (user.role == "client") {
res.redirect("/success");
} else if (user.role == "technicien") {
res.redirect("/success-technicien");
} else if (user.role == "superuser") {
res.redirect("/success-admin");
};
} catch (err) {
const errors = signInErrors(err);
res.status(200).json({ errors });
}
};
when you get the user , you should have field for the role ,
then check which role logged in and redirect him to where he needs to be
I want to make a Dating application using node.js and javascript with Azure functions and an Azure sql server. I can create a user so it appears in my database, but how do I make a login system that "checks" if the users email and password is in the database and is correct.
This is what I have so far:
**Login.js:**
var form = document.getElementById("form")
form.addEventListener('submit', function(e) {
e.preventDefault()
var email = document.getElementById("email").value
var password = document.getElementById("password").value
fetch("http://localhost:7071/api/login", {
method: 'POST',
body: JSON.stringify({
email: email,
password: password,
}),
headers: {
"Content-Type": "application/json; charset-UTF-8"
}
})
.then((response) => {
return response.text()
})
.then((data) => {
console.log(data)
}).catch((err) =>{ // catcher fejl, hvis noget går galt
console.log("wuups: " + err)
})
})
**DB.js connect:**
function login (payload) {
return new Promise((resolve, reject) => {
const sql = 'SELECT * FROM [user] where email = #email AND password = #password'
const request = new Request(sql,(err,rowcount) =>{
if (err){
reject(err)
console.log(err)
} else if( rowcount == 0){
reject({messsage:"user does not exit"})
}
});
request.addParameter('email', TYPES.VarChar, payload.email)
request.addParameter('password', TYPES.VarChar, payload.password)
request.on('row',(colums) => {
resolve(colums)
})
connection.execSql(request)
return "you are now logged in"
});
}
module.exports.login = login;
You're on the right track. Consider an updated version of db.sql:
function login(payload, connection) {
return new Promise((resolve, reject) => {
const sql = 'SELECT * FROM [user] where email = #email AND password = #password'
const request = new Request(sql, (err, rowCount) => {
if (err) {
reject(err)
console.error(err)
}
else {
if (rowCount == 1) {
resolve(payload.email)
}
else {
reject('Invalid credentials')
}
}
});
request.addParameter('email', TYPES.VarChar, payload.email)
request.addParameter('password', TYPES.VarChar, payload.password)
connection.execSql(request)
});
}
Since we can infer a successful login from the amount of returned rows, we don't need access to the actual rows in the row callback.
However: as pointed out by Robert in the comments, storing passwords in plain text is a security concern (since access to the database immediately unveils user passwords).
Better approach
The better approach is to store hashed passwords instead. Imagine this simple user table schema in MSSQL:
CREATE TABLE [User] (
[Email] [varchar](max) NOT NULL UNIQUE,
[PasswordHash] [varchar(max)] NOT NULL
)
The login procedure will remain almost the same. Instead of comparing passwords we now compare hashed passwords. Without going into too much detail, you would usually use a library for this purpose (to handle salts, mitigate timing attacks, etc.). I chose bcryptjs for the example below:
var bcrypt = require('bcryptjs');
function login(email, password, connection) {
return new Promise((resolve, error) => {
const sql = 'SELECT * FROM [user] where email = #email' // Note that the password comparison no longer lives here
const request = new Request(sql, (err, rowCount) => {
if (err) {
reject(err)
}
})
request.addParameter('email', TYPES.VarChar, email)
let userRow = null
// This time we need the 'row' callback to retrieve the password hash
request.on('row', row => {
userRow = {
email = row[0].value,
passwordHash = row[1].value
}
})
// .. and the 'done' callback to know, when the query has finished
request.on('done', rowCount => {
if (rowCount == 0) {
reject('User not found')
}
else {
bcrypt.compare(password, userRow.passwordHash) // Password comparison
.then(passwordsMatch => {
if (passwordsMatch) {
resolve(email)
}
else {
reject('Invalid credentials')
}
})
}
})
connection.execSql(request)
})
}
And here's an example of how to create new users with this approach using the same library:
var bcrypt = require('bcryptjs');
const PASSWORD_SALT_ROUNDS = 10 // Learn more at ex. https://stackoverflow.com/questions/46693430/what-are-salt-rounds-and-how-are-salts-stored-in-bcrypt
function createNewUser(email, password, connection) {
return bcrypt.hash(password, PASSWORD_SALT_ROUNDS).then(passwordHash => {
const sql = 'INSERT INTO [user] (Email, PasswordHash) VALUES (#email, #passwordHash)'
const request = new Request(sql, err => {
if (err) {
error(err)
}
else {
resolve()
}
})
request.addParameter('Email', TYPES.VarChar, email)
request.addParameter('PasswordHash', TYPES.VarChar, passwordHash)
connection.execSql(request)
})
}
Consider this a pragmatic proposal to get started. Please note, that the code is illustrative, since I haven't actually executed it, and it is made under certain assumptions.