I am making mock authentication in node js using passport, jwt. I have created successfully api. I am using handlebars for templating.
Suppose when user post credential to login(post), the api should verify the user and return json content. I stuck here how I redirect to another page after redirection.I don't want this in my api.
My code is this:
app.post('/login', (req, res, next) => {
if (req.body.name == '' || req.body.password == '') {
res.status(401).send('Please fill all fields')
} else {
let name = req.body.name;
// let password = req.body.password;
// usually this would be a database call:
let user = users[_.findIndex(users, {
name: name
})];
if (user === undefined) {
res.status(401).send('User not defined')
} else {
if (user.password === req.body.password) {
let payload = {
id: user.id
};
let token = jwt.sign(payload, config.jwtSecret);
res.json({message: "ok", token: token});
} else {
res.status(401).send('Password did not match')
}
}
}
});
My doubt is this from this api How can I redirect to next page if user is authenticated. I can not change code in this api as I am using this api for testing also.
I'm still kinda new to Node.js but I believe to redirect you can use
res.redirect("/pageyouwant");
Maybe in the bracket where the user gets authenticated, you can use the code I wrote above.
Btw, maybe try my code in that place
if (user.password === req.body.password) {
let payload = {
id: user.id
};
let token = jwt.sign(payload, config.jwtSecret);
res.json({message: "ok", token: token});
res.redirect("/pageyouwant"); // <-- This one right here
}
Hope I could helped you.
Related
I would like to know how I can find my forum using my JWT token
exports.getByOwnerID = function (req, res, next) {
Forum.find({createdBy: req.body.createdBy})
.then(doc => {
if(!doc) { return res.status(400).end();}
return res.status(200).json(doc);
})
.catch(err => next(err));
}
So here I have my function to verify my JWT Token
I use it for example this way
this is my route :
router.post('/',verifyToken,getOwner);
this is my request :
POST http://localhost:8080/forum/getOwner
Authorization: Bearer {token}
const extractToken = (rawTokenHeader) => {
if(!rawTokenHeader) { return undefined; }
// Remove bearer and extract token value
const temp = rawTokenHeader.split(' ');
if(!temp || temp.length != 2) { return undefined; }
// Return encoded token
return temp[1];
};
module.exports = function(req,res,next){
// Get authorization header
const rawTokenHeader = req.header('Authorization');
// Get token value
const token = extractToken(rawTokenHeader);
// No token -> No access
if(!token) {
console.log('No token in request');
// Access denied
return res.status(401).send('Access Denied');
}
// Verify token
try {
const decoded = jwt.verify(token, process.env.JWT_KEY);
req.token= decoded;
req.user = decoded;
//console.log(token.userID);
// Proceed
next();
} catch(err) {
console.error('Error in JWT check: ', err);
// Tell client something went wrong
res.status(400).send('Invalid Token');
}
}
const forumSchema = ({
forumName: {
type: String,
required: true,
},
forumDescription: {
type: String,
required: true,
},
createdBy: {
type: Schema.Types.ObjectId, ref: 'User'
},
published_on: {
type: String,
default: moment().format("LLL")
},
});
I´ve tried a lot of things but I can´t solve it anymore.. I need help
How I don't have enough reputation to make a comment I leave this as an answer, is hard to say why this is not working if we don't know how the schema of Forum looks like and what is returning req.body.createdBy, but if the jwt is created by you could encode the Forum._id in it and when you receive it here you can decode it and find the forum in the database
edit---
Now that I can see the error you got and the Schema of Forum i can say that probably you can solve the error by importing mongoose and adding this at the query mongoose.Types.ObjectId(req.body.CreatedBy) that will parse the string to an objectId
As you can see, you have user (or token) data in the req object, and I hope your jwt token also includes the user's id. You can use the user id to find their Forums.
According to the router router.post('/', verifyToken, getOwner); (getByOwnerID ???), let's update getOwner handler:
exports.getOwner = function (req, res, next) {
Forum.find({ createdBy: req.user.userID }) // or something like that
.then(doc => {
if(!doc) { return res.status(400).end();}
return res.status(200).json(doc);
})
.catch(err => next(err));
}
I'm trying to beat this CTF: a website under construction that has a simple login page where you can login or register a new user.
It uses node express and a SQLite DB.
Analyzing the source code I found this query:
getUser(username){
return new Promise((res, rej) => {
db.get(`SELECT * FROM users WHERE username = '${username}'`, (err, data) => {
if (err) return rej(err);
res(data);
});
});
},
This function gets called after a Middleware checked session cookies with jsonwebtoken to retrieve the username when you GET '/'.
This is where it's called:
router.get('/', AuthMiddleware, async (req, res, next) => {
try{
let user = await DBHelper.getUser(req.data.username);
if (user === undefined) {
return res.send(`user ${req.data.username} doesn't exist in our database.`);
}
return res.render('index.html', { user });
}catch (err){
return next(err);
}
});
AuthMiddleware:
module.exports = async (req, res, next) => {
try{
if (req.cookies.session === undefined) return res.redirect('/auth');
let data = await JWTHelper.decode(req.cookies.session);
req.data = {
username: data.username
}
next();
} catch(e) {
console.log(e);
return res.status(500).send('Internal server error');
}
}
Since that appears to be the only query formatted that way (the others all use ?) and in general the only evident vulnerability, I suspect the flag is stored somewhere in the database.
As the only way to get that function called is to have an active session i registered a user with 'malicious' sql code as the username. At first I tried to close the quote and attach an OR WHERE to get all the users:
SELECT * FROM users WHERE username = '${username}' +
bob' OR WHERE username IS NOT NULL =
SELECT * FROM users WHERE username = 'bob' OR WHERE username IS NOT NULL
This should at least throw an error as WHERE username = 'bob' OR WHERE username IS NOT NULL should return a collection with all the users in the database while it's rendered on the webpage as
Welcome {{ user.username }}
This site is under development.
Please come back later.
I was expecting at least "no username property on array" or something like that. Instead it always return the full username I gave him
Welcome bob' OR WHERE username IS NOT NULL
This site is under development.
Please come back later.
Am I missing something? Is there a way to escape eventual quotes that might be added during the cookie reading?
EDIT:
Here is the function that gets called when you attempt a login
/auth route:
router.post('/auth', async (req, res) => {
const { username, password } = req.body;
if((username !== undefined && username.trim().length === 0)
|| (password !== undefined && password.trim().length === 0)){
return res.redirect('/auth');
}
if(req.body.register !== undefined){
let canRegister = await DBHelper.checkUser(username);
if(!canRegister){
return res.redirect('/auth?error=Username already exists');
}
DBHelper.createUser(username, password);
return res.redirect('/auth?error=Registered successfully&type=success');
}
// login user
let canLogin = await DBHelper.attemptLogin(username, password);
if(!canLogin){
return res.redirect('/auth?error=Invalid username or password');
}
let token = await JWTHelper.sign({ // Maybe something can be done with this function?
username: username.replace(/'/g, "\'\'").replace(/"/g, "\"\"")
})
res.cookie('session', token, { maxAge: 900000 });
return res.redirect('/');
});
attemptLogin():
attemptLogin(username, password){
return new Promise((res, rej) => {
db.get(`SELECT * FROM users WHERE username = ? AND password = ?`, username, password, (err, data) => {
if (err) return rej();
res(data !== undefined);
});
});
}
EDIT 2.0:
I just noticed the part where it stores the session cookie:
let token = await JWTHelper.sign({
username: username.replace(/'/g, "\'\'").replace(/"/g, "\"\"")
})
It apparently replaces all ' with \'\'. I can solve half of that by escaping the quote so that it becomes \\'\'. This allows me to close the username='' statement, but I still need to find a way to invalidate the second \'.
I'm currently creating some API with Zeit Now, and I was trying to implement 404 error when the user variable is [] (an empty array), but when I fetch this endpoint, API is sending [] aswell. How can I implement it? Is it because the user is a promise?
const db = require('../../lib/db')
const escape = require('sql-template-strings')
module.exports = async (req, res) => {
const user = await db.query(escape`
SELECT *
FROM users
WHERE username = ${req.body.username}
AND password = ${req.body.password}
`)
if (user === []) {
res.status(404).json({ message: 'User with these credentials doesn\'t exist.' })
return false
}
res.status(200).json(user)
}
Because
[] === [] // false
So you want to check the length property
if (user.length === 0) {
res.status(404).json({ message: 'User with these credentials doesn\'t exist.' })
return false
}
This is my login code in express router:
if (password === realpassword) {
res.cookie('thecookie', 'somethingliketoken');
res.redirect(302, '/somepages');
} else {
res.status(403).end();
}
I want to set the cookie so that the /somepages can recognize who is this user.
But I got error Error: Can't set headers after they are sent.
I had already tried return res.redirect(302, '/somepages');. The query to /somepages had be sent correctly but the page still stayed in my /login.
Please help.
I think I got the reason.
I posted the username and password using AJAX so the 302 redirect was sent to the AJAX query and yes it redirected successfully but it was for the AJAX query, not for the page, that was why the second query was received but the page didn't change.
this.$http({
url: '/userLogin',
method: 'post',
data: {
username: this.username,
password: SHA3(this.password, {outputLength: 256}).toString(),
},
}).then(res => {
console.log(res);
this.hintColor = '#00bdd8';
this.hint = 'success';
}).catch(err => {
this.hintColor = 'red';
if (err.response.status == 403) {
this.hint = 'mismatch';
} else {
this.hint = '500err';
}
});
I think it may should not be used like this.
Will using window.location instead.
If there are any other ways, I really appreciate your help.
I have done something like this in my login post request, and its working for me.
app.post('/index',function(req,res){
var uname = req.body.Username;
var pwd = req.body.Password;
if(uname && pwd === 'admin'){
var token = jwt.sign( { username: uname }, 'q1W2e3R4t5Y6u7I8o9P',{ expiresIn: '1m' }
);
res.cookie('auth',token);
res.redirect('home');
console.log('Authentication is done successfully.....');
console.log(token);
}
});
I have a login in my Angular 2 app, and I have been converting it from using a fake backend (which works) to connect to our mongoDB-based API instead.
This is the login function I am using in the authentication service:
login(username: string, password: string) {
const u = encodeURIComponent(username);
const p = encodeURIComponent(password);
this._url = `https://api.somesite.com/v0/staff/login/${u}/${p}?apikey=somekey`;
console.log(this._url);
return this.http.post(this._url, JSON.stringify({ username: username, password: password }))
.map((response: Response) => {
// login successful if there's a jwt token in the response
const user = response.json();
if (user && user.token) {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('currentUser', JSON.stringify(user));
}
});
}
In my login component I am subscribing like this:
login() {
this.loading = true;
this.authenticationService.login(this.model.username, this.model.password)
.subscribe(
data => {
this.router.navigate(['/']);
console.log('User logged in as: ' + this.model.username);
},
error => {
this.alertService.error(error);
this.loading = false;
});
this.authenticationService.username = this.model.username;
}
When I try this, and log to the console "this_url", I get what I would expect. For instance, if the user typed in "billsmith" for username, and "parisnow" for password, I see this in the console for "this_url":
https://api.somesite.com/v0/staff/login/billsmith/parisnow?apikey=somekey
Furthermore, I can type that url directly into the browser address window and see data (when the username and password correctly correspond to actual records in our database). So it's accessing the correct info in that sense.
But in the console I get a "404" error for that generated url. It also doesn't "do anything". In other words, it doesn't correctly redirect to the main component as it did with the fakeBackend-enabled login. And the only thing that's different now is the url that I am calling (because I'm connecting to our actual API now, as opposed to a fake backend provider).
FYI, the url when using the fake backend looked like this:
return this.http.post('/api/authenticate', JSON.stringify({ username: username, password: password}))
What am I missing here?
By the way, this is how things look on the server side re: our mongoDB:
exports.byLogin = function(req, res, next) {
let ioOnly = false, username, password;
if (_.isUndefined(req.params)){
ioOnly=true;
username = req.username;
password = req.password;
}
else {
username = req.params.username;
password = req.params.password;
}
staff.findOne({username: username, password: password}, function(err, doc) {
if (err) { if (!ioOnly) { return next(err) } else { return res(err)}}
else if(doc) ((!ioOnly) ? res.send(doc) : res(doc));
else ((!ioOnly) ? res.sendStatus(204) : res(doc));
});
};