const { Controller } = require('libflitter') class AuthController extends Controller { static get services() { return [...super.services, 'models', 'auth', 'MFA', 'output', 'configs'] } async get_users(req, res, next) { const User = this.models.get('auth:User') const users = await User.find() const data = [] for ( const user of users ) { if ( !req.user.can(`auth:user:${user.id}:view`) && req.user.id !== user.id) continue data.push(await user.to_api()) } return res.api(data) } async get_roles(req, res, next) { const role_config = this.configs.get('auth.roles') const data = [] for ( const role_name in role_config ) { if ( !role_config.hasOwnProperty(role_name) ) continue data.push({ role: role_name, permissions: role_config[role_name], }) } return res.api(data) } async validate_username(req, res, next) { let is_valid = true if ( !req.body.username ) is_valid = false if ( is_valid ) { const User = this.models.get('auth:User') const user = await User.findOne({uid: req.body.username}) if ( !user || !user.can_login ) is_valid = false } return res.api({ is_valid }) } // TODO XSRF Token /* * Request Params: * - username * - password * - [create_session = false] */ async attempt(req, res, next) { const flitter = this.auth.get_provider('flitter') const errors = await flitter.validate_login(req.body) if ( errors && errors.length > 0 ) return res.status(400) .message(`Unable to complete authentication: one or more errors occurred`) .api({ errors }) const login_args = await flitter.get_login_args(req.body) const user = await flitter.login.apply(flitter, login_args) if ( !user ) return res.status(200) .message(`Invalid username or password.`) .api({ message: `Invalid username or password.`, success: false, }) if ( req.body.create_session ) await flitter.session(req, user) let destination = this.configs.get('auth.default_login_route') if ( req.session.auth.flow ) { destination = req.session.auth.flow } if ( user.mfa_enabled && !req.session.mfa_remember ) { req.session.auth.in_dmz = true destination = '/auth/mfa/challenge' } if ( req.session?.auth?.message ) delete req.session.auth.message // If we're doing a trust flow, check the grant if ( req.body.grant_code && req.trust.has_flow() ) { if ( req.trust.check_grant(req.body.grant_code) ) { req.trust.grant(req.trust.flow_scope()) // Trust re-verification is granted, // but the user might still need to verify MFA const next = req.trust.end() if ( req.session.auth.in_dmz ) { req.session.auth.flow = next } else { destination = next } } else { return res.status(401) .message(`Unable to grant trust. Grant token is invalid.`) .api() } } return res.api({ success: true, session_created: !!req.body.create_session, next: destination, }) } async generate_mfa_key(req, res, next) { if ( req.user.mfa_enabled ) return res.status(400) .message(`MFA already configured for user. Cannot fetch key.`) .api() const MFAToken = this.models.get('auth:MFAToken') const secret = await this.MFA.secret(req.user) req.user.mfa_token = new MFAToken({ secret: secret.base32, otpauth_url: secret.otpauth_url }, req.user) await req.user.save() return res.api({ success: true, secret: secret.base32, otpauth_url: secret.otpauth_url, qr_code: await this.MFA.qr_code(secret) }) } async attempt_mfa(req, res, next) { if ( !req.user.mfa_token ) return res.status(400) .message(`The user does not have MFA configured.`) .api() const code = req.body.verify_code const token = req.user.mfa_token const is_valid = token.verify(code) let next_destination = undefined if ( is_valid ) { req.session.auth.in_dmz = false next_destination = req.session.auth.flow || this.configs.get('auth.default_login_route') delete req.session.auth.flow } req.session.mfa_remember = true return res.api({ success: true, verify_code: code, is_valid, next_destination, }) } async enable_mfa(req, res, next) { if ( !req.user.mfa_token ) return res.status(400) .message(`The user does not have an MFA token configured.`) .api() req.user.mfa_enabled = true req.user.mfa_enable_date = new Date req.user.save() // invalidate existing tokens and other logins const flitter = await this.auth.get_provider('flitter') await flitter.logout(req) await req.user.kickout() return res.api({success: true, mfa_enabled: req.user.mfa_enabled}) } async disable_mfa(req, res, next) { if ( !req.user.mfa_enabled ) return res.status(400) .message('The user does not have MFA enabled.') .api() req.user.mfa_enabled = false delete req.user.mfa_enable_date delete req.user.mfa_token req.user.app_passwords = [] await req.user.save() // invalidate existing login tokens and logins const flitter = await this.auth.get_provider('flitter') await flitter.logout(req) await req.user.kickout() return res.api({success: true, mfa_enabled: req.user.mfa_enabled}) } async get_mfa_enable_date(req, res, next) { if ( !req.user.mfa_enabled ) return res.api({ mfa_enabled: false }) return res.api({ mfa_enabled: true, mfa_enable_date: req.user.mfa_enable_date }) } } module.exports = exports = AuthController