You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
139 lines
4.0 KiB
139 lines
4.0 KiB
const { Controller } = require('libflitter')
|
|
|
|
class AuthController extends Controller {
|
|
static get services() {
|
|
return [...super.services, 'models', 'auth', 'MFA', 'output']
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// TODO remember-device feature
|
|
if ( user.mfa_enabled && !req.session.mfa_remember ) {
|
|
req.session.auth.in_dmz = true
|
|
destination = '/auth/mfa/challenge'
|
|
}
|
|
|
|
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.save()
|
|
|
|
// TODO invalidate existing tokens and other logins
|
|
const flitter = await this.auth.get_provider('flitter')
|
|
await flitter.logout(req)
|
|
|
|
return res.api({success: true, mfa_enabled: req.user.mfa_enabled})
|
|
}
|
|
|
|
}
|
|
|
|
module.exports = exports = AuthController
|