3m-portal/auth.js
Vadym Samoilenko 67fcb017cc Prepare for production: remove hardcoded credentials and fix bugs
- Move service account credentials to .env (loaded server-side only)
- server.js: inject credentials in proxy, strip any client-provided creds,
  replace deprecated url.parse with new URL
- auth.js / dashboard.js: remove all hardcoded passwords from client code
- dashboard.js: remove broken category filter, fix redundant user.info call
  (use stored userId), add HTML escaping against XSS
- login.html: remove unused password field
- dashboard.html: remove broken category filter UI
- Add .gitignore to exclude .env and node_modules
- Add .env.example as configuration template

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 20:17:49 +00:00

95 lines
3.7 KiB
JavaScript

const OLIVER_AUTH_CONFIG = {
apiBaseUrl: '/api',
clientId: '7',
};
const TMM_AUTH_CONFIG = {
apiBaseUrl: '/api',
clientId: '7',
};
async function handleLogin(authConfig, loginType) {
const username = document.getElementById('username').value.trim();
const errorMessage = document.getElementById('errorMessage');
const isOliver = loginType === 'oliver';
const loginButton = document.getElementById(isOliver ? 'oliverLoginButton' : 'tmmLoginButton');
const buttonText = document.getElementById(isOliver ? 'oliverButtonText' : 'tmmButtonText');
const spinner = document.getElementById(isOliver ? 'oliverSpinner' : 'tmmSpinner');
errorMessage.style.display = 'none';
if (!username) {
errorMessage.textContent = 'Please enter your username.';
errorMessage.style.display = 'block';
return;
}
loginButton.disabled = true;
buttonText.textContent = 'Signing in...';
spinner.style.display = 'inline-block';
try {
// Step 1: Resolve username → userId
const userInfoParams = new URLSearchParams({
command: 'user.info',
authDomain: 'local',
clientId: '6',
username,
domain: 'local',
});
const userInfoRes = await fetch(`${authConfig.apiBaseUrl}?${userInfoParams}`, {
headers: { Accept: 'text/xml' },
});
const userInfoDoc = new DOMParser().parseFromString(await userInfoRes.text(), 'text/xml');
const userInfoError = userInfoDoc.querySelector('error');
if (userInfoError) {
throw new Error(userInfoError.querySelector('message')?.textContent || 'User not found');
}
const userIdNode = userInfoDoc.querySelector('user > id') || userInfoDoc.querySelector('id');
if (!userIdNode) throw new Error('User not found. Please check your username.');
const userId = userIdNode.textContent;
// Step 2: Create external session for the user
const sessionParams = new URLSearchParams({
command: 'user.session.extern.add',
authDomain: 'local',
clientId: authConfig.clientId,
username,
userId,
});
const sessionRes = await fetch(`${authConfig.apiBaseUrl}?${sessionParams}`, {
headers: { Accept: 'text/xml' },
});
const sessionDoc = new DOMParser().parseFromString(await sessionRes.text(), 'text/xml');
const sessionError = sessionDoc.querySelector('error');
if (sessionError) {
throw new Error(sessionError.querySelector('message')?.textContent || 'Login failed');
}
const externSessionId = sessionDoc.querySelector('externSessionId')?.textContent;
if (!externSessionId) throw new Error('No session received from server');
sessionStorage.setItem('isAuthenticated', 'true');
sessionStorage.setItem('username', username);
sessionStorage.setItem('userId', userId);
sessionStorage.setItem('externSessionId', externSessionId);
sessionStorage.setItem('authConfig', JSON.stringify(authConfig));
window.location.href = 'dashboard.html';
} catch (error) {
console.error('Login error:', error);
errorMessage.textContent = error.message || 'An error occurred. Please try again.';
errorMessage.style.display = 'block';
loginButton.disabled = false;
buttonText.textContent = isOliver ? 'Oliver Login' : '3M Login';
spinner.style.display = 'none';
}
}
document.getElementById('oliverLoginButton').addEventListener('click', () => handleLogin(OLIVER_AUTH_CONFIG, 'oliver'));
document.getElementById('tmmLoginButton').addEventListener('click', () => handleLogin(TMM_AUTH_CONFIG, '3m'));