1
0
Fork 0

wip(backend): Login flow

This commit is contained in:
joostdecock 2022-11-02 12:47:13 +01:00
parent f6a796959b
commit 553ab91d26
4 changed files with 168 additions and 24 deletions

View file

@ -4,7 +4,7 @@ import jwt from 'jsonwebtoken'
//import fs from 'fs'
import Zip from 'jszip'
//import rimraf from 'rimraf'
import { hash, hashPassword, randomString } from '../utils/crypto.mjs'
import { hash, hashPassword, randomString, verifyPassword } from '../utils/crypto.mjs'
import { clean, asJson } from '../utils/index.mjs'
import { log } from '../utils/log.mjs'
import { emailTemplate } from '../utils/email.mjs'
@ -260,15 +260,41 @@ UserController.prototype.login = async function (req, res, tools) {
})
} catch (err) {
log.warn(err, `Error while trying to find username: ${req.body.username}`)
return res.status(401).send({ error: 'logingFailed', result })
return res.status(401).send({ error: 'loginFailed', result })
}
if (!account) {
log.warn(`Login attempt for non-existing user: ${req.body.username} from ${req.ip}`)
return res.status(401).send({ error: 'logingFailed', result })
return res.status(401).send({ error: 'loginFailed', result })
}
// Account found, check password
const [valid, updatedPasswordField] = verifyPassword(req.body.password, account.password)
if (!valid) {
log.warn(`Wrong password for existing user: ${req.body.username} from ${req.ip}`)
return res.status(401).send({ error: 'loginFailed', result })
}
// Login success
log.info(`Login by user ${account.id} (${account.username})`)
if (updatedPasswordField) {
// Update the password field with a v3 hash
let updateUser
try {
updateUser = await prisma.user.update({
where: {
id: account.id,
},
data: {
password: updatedPasswordField,
},
})
} catch (err) {
log.warn(
err,
`Could not update password field with v3 hash for user id ${account.id} (${account.username})`
)
}
}
return res.status(200).send({
result: 'success',

View file

@ -1,7 +1,7 @@
import bcrypt from 'bcryptjs' // Required for legacy password hashes
import { createHash, createCipheriv, createDecipheriv, scryptSync, randomBytes } from 'crypto'
import { log } from './log.mjs'
import { asJson, clean } from './index.mjs'
import { asJson } from './index.mjs'
/*
* Hashes an email address (or other string)
@ -14,7 +14,7 @@ export const hash = (string) => createHash('sha256').update(string).digest('hex'
* This is not used in anything cryptographic. It is only used as a temporary
* username to avoid username collisions
*/
export const randomString = (bytes=8) => randomBytes(bytes).toString('hex')
export const randomString = (bytes = 8) => randomBytes(bytes).toString('hex')
/*
* Returns an object holding encrypt() and decrypt() methods

View file

@ -4,7 +4,8 @@ import { api } from '../config.mjs'
* Cleans a string (typically email) for hashing
*/
export const clean = (string) => {
if (typeof string !== 'string') throw 'clean() only takes a string as input'
if (typeof string === 'number') string = string.toString()
if (typeof string !== 'string') throw 'clean() only takes a string or number as input'
return string.toLowerCase().trim()
}

View file

@ -87,6 +87,7 @@ describe(`${user} Signup flow and authentication`, () => {
expect(typeof res.body.account.id).to.equal(`number`)
store.token = res.body.token
store.username = res.body.account.username
store.userid = res.body.account.id
done()
})
})
@ -106,7 +107,141 @@ describe(`${user} Signup flow and authentication`, () => {
})
})
step(`${user} Should not login with the wrong password`, (done) => {
chai
.request(config.api)
.post('/login')
.send({
username: store.username,
password: store.username,
})
.end((err, res) => {
expect(res.status).to.equal(401)
expect(res.type).to.equal('application/json')
expect(res.charset).to.equal('utf-8')
expect(res.body.result).to.equal(`error`)
expect(res.body.error).to.equal(`loginFailed`)
done()
})
})
step(`${user} Should login with username and password`, (done) => {
chai
.request(config.api)
.post('/login')
.send({
username: store.username,
password: data.password,
})
.end((err, res) => {
expect(res.status).to.equal(200)
expect(res.type).to.equal('application/json')
expect(res.charset).to.equal('utf-8')
expect(res.body.result).to.equal(`success`)
expect(res.body.account.email).to.equal(data.email)
expect(res.body.account.username).to.equal(store.username)
expect(res.body.account.lusername).to.equal(store.username.toLowerCase())
expect(typeof res.body.token).to.equal(`string`)
expect(typeof res.body.account.id).to.equal(`number`)
store.token = res.body.token
done()
})
})
step(`${user} Should login with USERNAME and password`, (done) => {
chai
.request(config.api)
.post('/login')
.send({
username: store.username.toUpperCase(),
password: data.password,
})
.end((err, res) => {
expect(res.status).to.equal(200)
expect(res.type).to.equal('application/json')
expect(res.charset).to.equal('utf-8')
expect(res.body.result).to.equal(`success`)
expect(res.body.account.email).to.equal(data.email)
expect(res.body.account.username).to.equal(store.username)
expect(res.body.account.lusername).to.equal(store.username.toLowerCase())
expect(typeof res.body.token).to.equal(`string`)
expect(typeof res.body.account.id).to.equal(`number`)
store.token = res.body.token
done()
})
})
step(`${user} Should login with email and password`, (done) => {
chai
.request(config.api)
.post('/login')
.send({
username: data.email,
password: data.password,
})
.end((err, res) => {
expect(res.status).to.equal(200)
expect(res.type).to.equal('application/json')
expect(res.charset).to.equal('utf-8')
expect(res.body.result).to.equal(`success`)
expect(res.body.account.email).to.equal(data.email)
expect(res.body.account.username).to.equal(store.username)
expect(res.body.account.lusername).to.equal(store.username.toLowerCase())
expect(typeof res.body.token).to.equal(`string`)
expect(typeof res.body.account.id).to.equal(`number`)
store.token = res.body.token
done()
})
})
step(`${user} Should login with EMAIL and password`, (done) => {
chai
.request(config.api)
.post('/login')
.send({
username: data.email.toUpperCase(),
password: data.password,
})
.end((err, res) => {
expect(res.status).to.equal(200)
expect(res.type).to.equal('application/json')
expect(res.charset).to.equal('utf-8')
expect(res.body.result).to.equal(`success`)
expect(res.body.account.email).to.equal(data.email)
expect(res.body.account.username).to.equal(store.username)
expect(res.body.account.lusername).to.equal(store.username.toLowerCase())
expect(typeof res.body.token).to.equal(`string`)
expect(typeof res.body.account.id).to.equal(`number`)
store.token = res.body.token
done()
})
})
step(`${user} Should login with userid and password`, (done) => {
chai
.request(config.api)
.post('/login')
.send({
username: store.userid,
password: data.password,
})
.end((err, res) => {
expect(res.status).to.equal(200)
expect(res.type).to.equal('application/json')
expect(res.charset).to.equal('utf-8')
expect(res.body.result).to.equal(`success`)
expect(res.body.account.email).to.equal(data.email)
expect(res.body.account.username).to.equal(store.username)
expect(res.body.account.lusername).to.equal(store.username.toLowerCase())
expect(typeof res.body.token).to.equal(`string`)
expect(typeof res.body.account.id).to.equal(`number`)
store.token = res.body.token
done()
})
})
/*
step(`${user} Should login with email address and password`, (done) => {
chai
.request(config.api)
.post('/login')
@ -129,27 +264,9 @@ describe(`${user} Signup flow and authentication`, () => {
})
})
/*
describe('Login/Logout and session handling', () => {
it('should login with the username', (done) => {
chai
.request(config.backend)
.post('/login')
.send({
username: config.user.username,
password: config.user.password,
})
.end((err, res) => {
res.should.have.status(200)
let data = JSON.parse(res.text)
data.account.username.should.equal(config.user.username)
data.token.should.be.a('string')
config.user.token = data.token
done()
})
})
it('should login with the email address', (done) => {
chai