Select only records where domain based on user "permission" - javascript

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

Why does a put after my post, remove the last added data in my json file

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.

Unable to verify emails with JWT, need to find user with email but can't send email in the email

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.

JWT authentication not work on Iphone devices

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?

nodejs mongoose - how to check items in the database before login

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

Make a login and registration system using Azure functions and Azure SQL server

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.

Categories

Resources