// backend/routes/auth.js // Uses HANA ZCUST_USERS table for authentication. // Roles: // manager → can view list, open detail, verify/reject, fill manager fields // sap_adder → all manager perms + approve & push to SAP B1 const express = require('express'); const router = express.Router(); const jwt = require('jsonwebtoken'); const userDb = require('../services/hanaUsers'); const cryptoUtil = require('../services/cryptoUtil'); const sap = require('../services/sapServiceLayer'); const { verifyToken } = require('../middleware/auth'); const SECRET = process.env.JWT_SECRET || 'sap-portal-secret'; const EXPIRES = '12h'; // Build the signed JWT payload for a user, embedding their per-user SAP login // (SAP password AES-encrypted) so downstream SAP calls can act as them without // a per-request DB lookup. `creds` is the decrypted { sapUser, sapPassword } // or null when the user hasn't set one. function buildPayload(user, creds) { return { id: user.id, username: user.username, role: user.role, name: user.fullName, email: user.email || '', sapUser: creds ? creds.sapUser : '', sapPwdEnc: creds ? cryptoUtil.encrypt(creds.sapPassword) : '', }; } // ── POST /auth/login ────────────────────────────────────────────────────────── router.post('/login', async (req, res) => { const { username, password } = req.body; if (!username || !password) return res.status(400).json({ success: false, message: 'Username and password required' }); try { const user = await userDb.findByUsername(username); if (!user) return res.status(401).json({ success: false, message: 'Invalid username or password' }); const ok = await userDb.verifyPassword(password, user.passwordHash); if (!ok) return res.status(401).json({ success: false, message: 'Invalid username or password' }); await userDb.touchLastLogin(user.id); const creds = await userDb.getSapCredentials(user.id); // decrypted or null const payload = buildPayload(user, creds); const token = jwt.sign(payload, SECRET, { expiresIn: EXPIRES }); res.json({ success: true, token, user: { id: user.id, username: user.username, name: user.fullName, role: user.role, email: user.email, hasSapLogin: !!creds, sapUser: creds ? creds.sapUser : '' }, }); } catch (err) { console.error('[AUTH] login error:', err.message); res.status(500).json({ success: false, message: err.message }); } }); // ── GET /auth/me ────────────────────────────────────────────────────────────── router.get('/me', (req, res) => { const token = (req.headers.authorization || '').replace('Bearer ', ''); if (!token) return res.status(401).json({ success: false }); try { const user = jwt.verify(token, SECRET); res.json({ success: true, user }); } catch { res.status(401).json({ success: false }); } }); // ── GET /auth/profile — full profile from DB ────────────────────────────────── router.get('/profile', async (req, res) => { const token = (req.headers.authorization || '').replace('Bearer ', ''); if (!token) return res.status(401).json({ success: false }); try { const decoded = jwt.verify(token, SECRET); const user = await userDb.findById(decoded.id); if (!user) return res.status(404).json({ success: false, message: 'User not found' }); res.json({ success: true, user }); } catch (err) { res.status(401).json({ success: false, message: err.message }); } }); // ── GET /auth/sap-credentials — own SAP login status (never the password) ───── router.get('/sap-credentials', verifyToken, async (req, res) => { try { const user = await userDb.findById(req.user.id); res.json({ success: true, sapUser: user?.sapLoginUser || '', hasSapLogin: !!user?.hasSapLogin }); } catch (err) { res.status(500).json({ success: false, message: err.message }); } }); // ── PUT /auth/sap-credentials — set own SAP login (validated against SAP) ────── // Returns a fresh token carrying the new credentials so they take effect // immediately without re-login. router.put('/sap-credentials', verifyToken, async (req, res) => { const sapUser = (req.body?.sapUser || '').trim(); const sapPassword = req.body?.sapPassword || ''; if (!sapUser || !sapPassword) return res.status(400).json({ success: false, message: 'SAP User ID and Password are required' }); try { await sap.testSapLogin(null, sapUser, sapPassword); // validate before saving } catch (e) { return res.status(400).json({ success: false, message: 'SAP login failed — check your SAP User ID/Password. (' + e.message + ')' }); } try { await userDb.setSapCredentials(req.user.id, sapUser, sapPassword); const user = await userDb.findById(req.user.id); const token = jwt.sign(buildPayload(user, { sapUser, sapPassword }), SECRET, { expiresIn: EXPIRES }); res.json({ success: true, token, sapUser, hasSapLogin: true }); } catch (err) { res.status(500).json({ success: false, message: err.message }); } }); // ── PUT /auth/profile — update own name / email / password ──────────────────── router.put('/profile', async (req, res) => { const token = (req.headers.authorization || '').replace('Bearer ', ''); if (!token) return res.status(401).json({ success: false }); try { const decoded = jwt.verify(token, SECRET); const { fullName, email, currentPassword, newPassword } = req.body; if (newPassword) { const user = await userDb.findByUsername(decoded.username); const ok = await userDb.verifyPassword(currentPassword || '', user.passwordHash); if (!ok) return res.status(400).json({ success: false, message: 'Current password is incorrect' }); } const patch = {}; if (fullName !== undefined) patch.fullName = fullName.trim(); if (email !== undefined) patch.email = email.trim(); if (newPassword) patch.password = newPassword; await userDb.updateUser(decoded.id, patch); res.json({ success: true, message: 'Profile updated successfully' }); } catch (err) { res.status(500).json({ success: false, message: err.message }); } }); module.exports = router;