diff --git a/src/auth/auth.js b/src/auth/auth.js index 96e07b2..0501ef3 100644 --- a/src/auth/auth.js +++ b/src/auth/auth.js @@ -4,6 +4,14 @@ import jwt from 'jsonwebtoken'; import log4js from 'log4js'; // Load configuration import { loadConfig } from '../config.js'; +import { + editObject, + getObject, + getObjectByFilter +} from '../database/database.js'; +import { hostModel } from '../database/schemas/management/host.schema.js'; +import { userModel } from '../database/schemas/management/user.schema.js'; +import { generateAuthCode } from '../utils.js'; const config = loadConfig(); @@ -11,7 +19,7 @@ const logger = log4js.getLogger('Auth'); logger.level = config.server.logLevel; export class KeycloakAuth { - constructor(config) { + constructor() { this.config = config.auth; this.tokenCache = new Map(); // Cache for verified tokens } @@ -66,7 +74,7 @@ export class KeycloakAuth { // Parse token to extract user info const decodedToken = jwt.decode(token); - const user = { + const decodedUser = { id: decodedToken.sub, username: decodedToken.preferred_username, email: decodedToken.email, @@ -74,6 +82,11 @@ export class KeycloakAuth { roles: this.extractRoles(decodedToken) }; + const user = await getObjectByFilter({ + model: userModel, + filter: { username: decodedUser.username } + }); + // Cache the verified token const expiresAt = introspection.exp * 1000; // Convert to milliseconds this.tokenCache.set(token, { expiresAt, user }); @@ -120,28 +133,101 @@ export class KeycloakAuth { } } -// Socket.IO middleware for authentication -export function createAuthMiddleware(auth) { - return async (socket, next) => { - const { token } = socket.handshake.auth; - - if (!token) { - return next(new Error('Authentication token is required')); - } - +export class CodeAuth { + // Verify a code with the database + async verifyCode(id, authCode) { try { - const authResult = await auth.verifyToken(token); - - if (!authResult.valid) { - return next(new Error('Invalid authentication token')); + const host = await getObject({ model: hostModel, id, cached: true }); + if (host == undefined) { + const error = 'Host not found.'; + logger.warn(error, 'Host:', id); + return { valid: false, error: error }; } - - // Attach user information to socket - socket.user = authResult.user; - next(); - } catch (err) { - logger.error('Authentication error:', err); - next(new Error('Authentication failed')); + if (host.active == false) { + const error = 'Host not active.'; + logger.warn(error, 'Host:', id); + return { valid: false, error: error }; + } + if (host.authCode == undefined || host.authCode == '') { + const error = 'No authCode on database.'; + logger.warn(error, 'Host:', id); + return { valid: false, error: error }; + } + if (host.authCode != authCode) { + const error = 'authCode does not match.'; + logger.warn(error, 'Host:', id); + return { valid: false, error: error }; + } + return { valid: true, host: host }; + } catch (error) { + logger.error('Code verification error:', error.message); + return { valid: false }; } + } + + async verifyOtp(otp) { + try { + const host = await getObjectByFilter({ + model: hostModel, + filter: { otp: otp }, + cached: false + }); + if (host == undefined) { + const error = 'No host found with OTP.'; + logger.warn(error); + return { valid: false, error: error }; + } + const id = host._id.toString(); + if (host.active == false) { + const error = 'Host is not active.'; + logger.warn(error, 'Host:', id); + return { valid: false, error: error }; + } + if (host.otp == undefined) { + const error = 'No OTP on database.'; + logger.warn(error, 'Host:', id); + return { valid: false, error: error }; + } + if (host.otpExpiresAt == undefined) { + const error = 'No OTP expiry.'; + logger.warn(error, 'Host:', id); + return { valid: false, error: error }; + } + if (host.otpExpiresAt < Date.now()) { + const error = 'OTP expired.'; + logger.warn(error, 'Host:', id); + return { valid: false, error: error }; + } + const authCodeHost = await editObject({ + model: hostModel, + id: id, + updateData: { authCode: generateAuthCode() } + }); + return { valid: true, host: authCodeHost }; + } catch (error) { + logger.error('Code verification error:', error.message); + return { valid: false, error: error.message }; + } + } +} + +// Socket.IO middleware for authentication +export function createAuthMiddleware(socketUser) { + return async (packet, next) => { + const [event] = packet; // event name is always first element + + // Allow the 'authenticate' event through without checks + + logger.trace('Event:', event); + if (event === 'authenticate') { + next(); + return; + } + + if (socketUser.authenticated) { + next(); + return; + } + return next(new Error('Authentication is required.')); }; }