wip(backend): Login flow
This commit is contained in:
parent
f6a796959b
commit
553ab91d26
4 changed files with 168 additions and 24 deletions
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue