/**
* @module flitter-auth/Provider
*/
const ImplementationError = require('libflitter/errors/ImplementationError')
const { Injectable } = require('flitter-di')
const helpers = require('./Helpers')
/**
* Base class for all Flitter auth providers.
* @extends module:flitter-di/src/Injectable~Injectable
*/
class Provider extends Injectable {
/**
* Defines the services required by this provider.
* Includes the 'app', 'auth', and 'models' services by default.
* @returns {Array<string>}
*/
static get services() {
return [...super.services, 'app', 'auth', 'models']
}
/**
* Instantiates the provider class.
* @param {module:libflitter/app/FlitterApp~FlitterApp} app - the Flitter app
* @param {object} config - the config for this provider
*/
constructor(app, config){
super()
/**
* The Flitter app.
* @type {module:libflitter/app/FlitterApp~FlitterApp}
*/
this.app = app
/**
* The provider's config.
* @type {Object}
*/
this.config = config
/**
* The User model's class.
* @type {module:flitter-auth/model/User~User}
*/
this.User = this.models.get('auth:User')
}
/**
* Register a new user into whatever system the provider manages.
* Should also create a valid User document.
* @param {string} username - The UID of the user. Should be unique to Flitter.
* @param {object} [attrs] - optionally, other model attributes to be merged in
* @param {object} [data] - optionally, JSON data to be stored in the model
* @return {Promise<User>} - should return a saved user model instance
*/
async register(username, attrs, data){
throw new ImplementationError('Providers must implement the register() method.')
}
/**
* Ensure that registration form_data is valid. Checks for password and unique username.
* @param {object} form_data
* @returns {Promise<Array<string>>} - array of string errors. If empty array, no errors.
*/
async validate_registration(form_data){
const errors = []
if ( !Object.keys(form_data).includes('username') || !form_data.username ){
errors.push('Username field is required.')
}
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 {{username: string, password: string}} form_data
* @returns {Promise<Array<*>>}
*/
async get_registration_args(form_data){
return [
form_data.username,
{
password: form_data.password,
}
]
}
/**
* Get the Flitter canonical name of the registration view.
* @returns {string}
*/
registration_view(){
return 'auth:register'
}
/**
* Get the Flitter canonical name of the login view.
* @returns {string}
*/
login_view(){
return 'auth:login'
}
/**
* Handle a request to get the login view. By default, shows the view specified
* by this.registration_view(), passing it title, heading_text, provider_name
* @param {express/Request} req
* @param {express/Response} res
* @param {function} next
* @returns {Promise<*>}
*/
handle_login_get(req, res, next) {
return res.page(this.login_view(), {
title: 'Login',
heading_text: 'Hi, there. Login to continue:',
registration_enabled: req.auth_provider.config.registration,
provider_name: req.auth_provider.config.name,
})
}
/**
* Handle a request to get the registration view. By default, shows the view specified
* by this.registration_view(), passing it title, heading_text, provider_name
* @param {express/Request} req
* @param {express/Response} res
* @param {function} next
* @returns {Promise<*>}
*/
handle_register_get(req, res, next) {
return res.page(this.registration_view(), {
title: 'Register',
heading_text: 'Hi, there. Register to continue:',
provider_name: req.auth_provider.config.name,
})
}
/**
* Get the Flitter canonical name of the logout view.
* @returns {string}
*/
logout_view(){
return 'auth:form_page'
}
/**
* Ensure that login form_data is valid. Checks for username.
* @param {object} form_data
* @returns {Promise<Array<string>>} - array of string errors. If empty array, no errors.
*/
async validate_login(form_data){
const errors = []
if ( !Object.keys(form_data).includes('username') || !form_data.username ){
errors.push('Username field is required.')
}
return errors
}
/**
* From the form data, get the formatted arguments to be passed into the login function.
* Should create the username and password params.
* @param {{username: string, password: string}} form_data
* @returns {Promise<Array<string>>}
*/
async get_login_args(form_data){
return [
form_data.username,
form_data.password,
]
}
/**
* 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){
throw new ImplementationError()
}
/**
* 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){
throw new ImplementationError()
}
/**
* Bootstrap the session. Sets auth.user and auth.user_id.
* @param {express/Request} request
* @param {module:flitter-auth/model/User~User} user - the authenticated user
* @returns {Promise<void>}
*/
async session(request, user){
if ( !request.session.auth ) request.session.auth = {}
// request.session.auth.user = user
request.user = user
request.is_auth = true
request.session.auth.user_id = user.id
}
/**
* Log out the current user, if there is one, and clean the auth session.
* @param {express/Request} request
* @returns {Promise<object>} - the clean session
*/
async logout(request){
delete request.session.auth
return helpers.guarantee_auth_session(request)
}
/**
* Clean up resources used by this provider.
* @param {module:libflitter/app/FlitterApp~FlitterApp} app - the current app
* @returns {Promise<void>}
*/
async cleanup(app){}
}
module.exports = exports = Provider