Skip to content
Snippets Groups Projects
auth.js 3.92 KiB
Newer Older
const moment = require('moment')

const { User } = require('database/models')
const HttpError = require('core/errors/HttpError')
const crypto = require('core/utils/crypto')
const reporter = require('services/Reporter')
const { config } = require('bootstrap')
exports.register = async ctx => {
	const { email, name, password, date_of_birth } = ctx.request.body
	const user = await User.findOne({ where: { email } })

	if (user != null) {
		throw new HttpError({ status: 409, title: 'Email Already Exists', description: 'That email address already exists. Please try another email address.' })
	}

	if (date_of_birth) {
		if (!(date_of_birth.hasOwnProperty('day') && date_of_birth.hasOwnProperty('month') && date_of_birth.hasOwnProperty('year'))) {
			throw new HttpError({ status: 400, title: 'Bad date of birth', description: 'The provided date of birth is not valid' })
		}
	}

	const newUser = await ctx.services.userService.register(name || null, email, password, { dob: date_of_birth })
	await newUser.handleIncludes(ctx.includes)
	const token = await newUser.asToken()

	ctx.body = { user: newUser, token }
}

exports.login = async ctx => {
	const { email, password } = ctx.request.body
	const user = await ctx.services.userService.attemptLogin(email, password)
	const token = await user.asToken()

	ctx.body = { token }
Louis's avatar
Louis committed
}

exports.triggerPasswordReset = async ctx => {
	const { email } = ctx.request.body
	const user = await User.findOne({ where: { email } })
	if (!user) {
		throw new HttpError({ status: 404, title: 'No Such Email', description: 'The provided email address is not associated with an account' })
	}

	const token = await user.generateResetToken()

	const name = user.name || 'Jetsam User (You haven\'t told us your name!)'
	const reset_link = new URL(`/reset-password?token=${ token }`, config('app.urls.web'))

	const { mail } = require('services')

	try {
		await mail.sendTemplate(email, 'Reset Your Jetsam password', config('mail.templates.reset-password'), {
			name,
			reset_link,
		})
	} catch (e) {
		reporter.report(e)
		console.log(e.response.body.errors)
		throw new HttpError({ status: 500, title: 'Failed to send reset email', description: 'Could not send the password reset email' })
	}

	ctx.body = {
}

exports.handlePasswordReset = async ctx => {
	const { reset_token, new_password, confirm_password } = ctx.request.body
	let token = null

	if (!reset_token) {
		ctx.body = {
			error: {
				message: 'The reset token was missing',
			},
			params: {
				new_password,
				confirm_password,
			},
		}
		ctx.status = 400
		return
	}

	if (!new_password || !confirm_password) {
		ctx.body = {
			error: {
				message: 'A new password and password confirmation must be supplied',
			},
			params: {
				new_password,
				confirm_password,
			},
		}
		ctx.status = 400
		return
	}

	if (new_password !== confirm_password) {
		ctx.body = {
			error: {
				message: 'The new password must match the password confirmation',
			},
			params: {
				new_password,
				confirm_password,
			},
		}
		return
	}

	try {
		token = JSON.parse(await crypto.decrypt(reset_token))
	} catch (e) {
		ctx.body = {
			error: {
				message: 'The reset token was invalid or expired',
			},
			params: {
				new_password,
				confirm_password,
			},
		}
		ctx.status = 400
		return
	}

	const expires = moment.utc(token.expires)
	if (expires.isSameOrBefore(moment.utc())) {
		ctx.body = {
			error: {
				message: 'The reset token was invalid or expired'
			},
			params: {
				new_password,
				confirm_password,
			},
		}
		ctx.status = 400
		return
	}

	const user = await User.findOne({ where: { reset_token, id: token.id } })
	if (!user) {
		ctx.body = {
			error: {
				message: 'The reset token was invalid or expired'
			},
			params: {
				new_password,
				confirm_password,
			},
		}
		ctx.status = 400
		return
	}

	user.reset_token = null
	await user.setPlaintextPassword(new_password)
	await user.save()

	ctx.status = 200
	ctx.body = { user }