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.
CoreID/app/controllers/api/v1/Auth.controller.js

216 lines
6.5 KiB

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