/**
* @module flitter-auth/flitter/FlitterProvider
*/
const Provider = require('../Provider')
const bcrypt = require('bcrypt')
/**
* Local database authentication provider for Flitter.
* @extends module:flitter-auth/Provider~Provider
*/
class FlitterProvider extends Provider {
/**
* Register a new user.
* @param {string} username - name of the new user
* @param {object} attrs - collection of key-value pairs to set in the model
* @param {object} data - additional data to save in the model's JSON
* @returns {Promise<module:flitter-auth/model/User~User>}
*/
async register(username, attrs, data) {
const User = this.User
let user_data = {
uid: username,
}
if ( data ) user_data.data = JSON.stringify(data)
if ( attrs ) user_data = {...user_data, ...attrs}
const user = new User(user_data)
await user.save()
return user
}
/**
* Validate the form input to register a new user.
* @param {object} form_data - form data to validate
* @returns {Promise<Array<string>>} - collection of error messages. If array is empty, input is valid.
*/
async validate_registration(form_data) {
const errors = await super.validate_registration(form_data)
const min_pw_length = this.config.min_password_length
if ( form_data.username ){
const match = await this.User.findOne({ provider: this.config.name, uid: form_data.username })
if ( match ) errors.push('Username is already taken.')
}
if ( !Object.keys(form_data).includes('password') || !form_data.password ){
errors.push('Password field is required.')
}
else if ( form_data.password.length < min_pw_length ){
errors.push(`Password must be at least ${min_pw_length} characters long.`)
}
return errors
}
/**
* From the form data, get the formatted arguments to be passed into the registration function.
* Should create the username and {password} objects.
* @param {object} form_data - the form data from the request
* @returns {Promise<Array<*>>}
*/
async get_registration_args(form_data){
const username = form_data.username
delete form_data.username
return [
username,
{
password: await bcrypt.hash(form_data.password, 10)
}
]
}
/**
* Attempt to authenticate a user with the provided credentials. If it succeeds, return their User object.
* @param {string} username
* @param {string} password
* @param [args] - not required
* @returns {Promise<boolean|module:flitter-auth/model/User~User>} - false if the auth is unsuccessful, a User instance if it is
*/
async login(username, password, args = {}){
const User = this.User
const user = await User.lookup({ uid: username, provider: this.config.name })
if ( !user ) return false
const success = await this.check_user_auth(user, password)
return success ? user : false
}
/**
* Check the validity of the provided credentials.
* @param {string} user
* @param {string} password
* @returns {Promise<boolean>} - true if the credentials succeed, false otherwise
*/
async check_user_auth(user, password){
return await bcrypt.compare(password, user.password)
}
}
module.exports = exports = FlitterProvider