diff --git a/app/controllers/api/v1/Auth.controller.js b/app/controllers/api/v1/Auth.controller.js new file mode 100644 index 0000000..96de344 --- /dev/null +++ b/app/controllers/api/v1/Auth.controller.js @@ -0,0 +1,96 @@ +const { Controller } = require('libflitter') + +class AuthController extends Controller { + static get services() { + return [...super.services, 'models', 'auth']; + } + + async get_user_info(req, res, next) { + const User = this.models.get('auth:User') + + if ( !req.query.uid ) { + return res.status(400).api() + } + + const user = await User.findOne({ + uid: req.query.uid, + provider: 'flitter', + }) + + if ( user ) { + return res.api({ + exists: true, + uid: user.uid, + id: user.id, + }) + } + + return res.api({ exists: false }) + } + + async attempt_registration(req, res, next) { + const flitter = this.auth.get_provider('flitter') + + const form_data = { + username: req.body.uid, + password: req.body.password, + } + + const errors = await flitter.validate_registration(form_data) + + if ( !req.body.fullName ) { + errors.push('Full name is required.') + } + + if ( req.body.password !== req.body.passwordConfirmation ) { + errors.push('Password does not match confirmation.') + } + + if ( errors.length ) { + return res.api({ success: false, message: errors[0] }) + } + + const [username, attrs] = await flitter.get_registration_args(form_data) + attrs.full_name = req.body.fullName + + const user = await flitter.register(username, attrs, undefined) + if ( !user ) { + return res.api({ success: false, message: 'An unknown error has occurred' }) + } + + await flitter.session(req, user) + return res.api({ success: true }) + } + + async attempt_authentication(req, res, next) { + const flitter = this.auth.get_provider('flitter') + + if ( !req.body.uid ) { + return res.status(400) + .message('Missing required field: uid') + .api() + } + + if ( !req.body.password ) { + return res.status(400) + .message('Missing required field: password') + .api() + } + + const user = await flitter.login(req.body.uid, req.body.password) + if ( !user ) { + return res.api({ success: false, message: 'Invalid username or password' }) + } + + await flitter.session(req, user) + return res.api({ success: true }) + } + + async end_session(req, res, next) { + const flitter = this.auth.get_provider('flitter') + await flitter.logout(req); + return res.api(); + } +} + +module.exports = exports = AuthController diff --git a/app/models/auth/User.model.js b/app/models/auth/User.model.js index 85302d6..7e12203 100644 --- a/app/models/auth/User.model.js +++ b/app/models/auth/User.model.js @@ -13,6 +13,7 @@ class User extends AuthUser { static get schema() { return {...super.schema, ...{ // other schema fields here + full_name: String, preferences: { dark_mode: { type: Boolean, default: false }, auto_prefetch: { type: Boolean, default: false }, diff --git a/app/routing/routers/api/v1/auth.routes.js b/app/routing/routers/api/v1/auth.routes.js new file mode 100644 index 0000000..2686a60 --- /dev/null +++ b/app/routing/routers/api/v1/auth.routes.js @@ -0,0 +1,28 @@ +module.exports = exports = { + + prefix: '/api/v1/auth', + + middleware: [], + + get: { + '/user-info': [ + 'middleware::auth:GuestOnly', + 'controller::api:v1:Auth.get_user_info', + ], + }, + + post: { + '/attempt': [ + 'middleware::auth:GuestOnly', + 'controller::api:v1:Auth.attempt_authentication', + ], + '/end-session': [ + 'middleware::auth:UserOnly', + 'controller::api:v1:Auth.end_session', + ], + '/register': [ + 'middleware::auth:GuestOnly', + 'controller::api:v1:Auth.attempt_registration', + ], + }, +}