From 6d44590dfff950c4f8da778b456f5b5aa1d33083 Mon Sep 17 00:00:00 2001 From: FragginWagon Date: Wed, 28 Jan 2026 22:46:22 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=92=20Add=20authentication=20route=20l?= =?UTF-8?q?ogic=20for=20user=20login=20and=20registration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pokedex.online/server/routes/auth.js | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 code/websites/pokedex.online/server/routes/auth.js diff --git a/code/websites/pokedex.online/server/routes/auth.js b/code/websites/pokedex.online/server/routes/auth.js new file mode 100644 index 0000000..143d0c1 --- /dev/null +++ b/code/websites/pokedex.online/server/routes/auth.js @@ -0,0 +1,175 @@ +/** + * Authentication Routes + * + * Handles login, logout, token refresh, and user info endpoints + */ + +import { Router } from 'express'; +import { createToken, verifyToken, decodeToken, getTokenExpiresIn } from '../utils/jwt-utils.js'; + +export function createAuthRouter({ secret, adminPassword } = {}) { + const router = Router(); + + /** + * POST /auth/login + * Login with admin password to receive JWT token + */ + router.post('/login', (req, res) => { + const { password } = req.body; + + // Validate input + if (!password) { + return res.status(400).json({ + error: 'Password is required', + code: 'MISSING_PASSWORD' + }); + } + + // Validate password + if (password !== adminPassword) { + return res.status(401).json({ + error: 'Invalid password', + code: 'INVALID_PASSWORD' + }); + } + + try { + // Create token with admin permissions + const token = createToken( + { + isAdmin: true, + permissions: ['admin', 'gamemaster-edit'], + loginTime: new Date().toISOString() + }, + secret, + 7 * 24 * 60 * 60 // 7 days + ); + + res.json({ + success: true, + token, + expiresIn: 7 * 24 * 60 * 60, + user: { + isAdmin: true, + permissions: ['admin', 'gamemaster-edit'] + } + }); + } catch (err) { + res.status(500).json({ + error: 'Failed to create token', + code: 'TOKEN_CREATION_ERROR' + }); + } + }); + + /** + * POST /auth/verify + * Verify that a token is valid + */ + router.post('/verify', (req, res) => { + const { token } = req.body; + + if (!token) { + return res.status(400).json({ + error: 'Token is required', + code: 'MISSING_TOKEN' + }); + } + + try { + const decoded = verifyToken(token, secret); + const expiresIn = getTokenExpiresIn(token); + + res.json({ + valid: true, + user: { + isAdmin: decoded.isAdmin, + permissions: decoded.permissions + }, + expiresIn: Math.floor(expiresIn / 1000), + expiresAt: new Date(Date.now() + expiresIn) + }); + } catch (err) { + return res.status(401).json({ + valid: false, + error: err.message, + code: 'INVALID_TOKEN' + }); + } + }); + + /** + * POST /auth/refresh + * Refresh an existing token + */ + router.post('/refresh', (req, res) => { + const { token } = req.body; + + if (!token) { + return res.status(400).json({ + error: 'Token is required', + code: 'MISSING_TOKEN' + }); + } + + try { + const decoded = verifyToken(token, secret); + + // Create new token with same payload but extended expiration + const newToken = createToken( + { + isAdmin: decoded.isAdmin, + permissions: decoded.permissions, + loginTime: decoded.loginTime + }, + secret, + 7 * 24 * 60 * 60 // 7 days + ); + + res.json({ + success: true, + token: newToken, + expiresIn: 7 * 24 * 60 * 60 + }); + } catch (err) { + return res.status(401).json({ + error: err.message, + code: 'INVALID_TOKEN' + }); + } + }); + + /** + * GET /auth/user + * Get current user info (requires valid token via middleware) + */ + router.get('/user', (req, res) => { + if (!req.user) { + return res.status(401).json({ + error: 'Not authenticated', + code: 'NOT_AUTHENTICATED' + }); + } + + res.json({ + user: { + isAdmin: req.user.isAdmin, + permissions: req.user.permissions, + loginTime: req.user.loginTime + } + }); + }); + + /** + * POST /auth/logout + * Logout (token is invalidated on client side) + */ + router.post('/logout', (req, res) => { + res.json({ + success: true, + message: 'Logged out successfully' + }); + }); + + return router; +}