42ff3e8f33
Build & Deploy / Test & Lint (push) Has been cancelled
Build & Deploy / Security Scan (push) Has been cancelled
Code Quality / Code Quality Checks (push) Has been cancelled
Code Quality / Dependency Vulnerabilities (push) Has been cancelled
Code Quality / Performance Testing (push) Has been cancelled
Build & Deploy / Build Docker Images (push) Has been cancelled
Build & Deploy / Deploy to Kubernetes (push) Has been cancelled
221 lines
9.9 KiB
JavaScript
221 lines
9.9 KiB
JavaScript
// Universal Auth Module: Immutable, Pattern-Based, Project-Agnostic
|
|
// Usage: import and call Auth.use('pb-graph') or Auth.use('pb') or Auth.use('graph')
|
|
// Provides: getToken(type), ensureToken(type), refreshToken(type), getUserInfo(), promptReauth()
|
|
// Token types: 'pb' (PocketBase user/agent), 'graph' (Graph delegated), 'graphAgent' (Graph app-only)
|
|
// All storage, retrieval, refresh, and UI are standardized and immutable
|
|
|
|
const TOKEN_KEYS = {
|
|
'pb-user': 'pbUserToken',
|
|
'pb-agent': 'pbAgentToken',
|
|
'graph-user': 'graphUserToken',
|
|
'graph-agent': 'graphAgentToken'
|
|
};
|
|
|
|
function parsePattern(pattern) {
|
|
return pattern.split('+').map(s => s.trim()).filter(Boolean);
|
|
}
|
|
|
|
window.Auth = (() => {
|
|
let pattern = 'pb-user'; // default
|
|
let enabledTypes = ['pb-user'];
|
|
let pb = null;
|
|
let popup = null;
|
|
let agentCreds = { email: '', password: '' };
|
|
let superuserCreds = { email: '', password: '' };
|
|
|
|
// --- Setup ---
|
|
function use(type) {
|
|
pattern = type;
|
|
enabledTypes = parsePattern(type);
|
|
if (enabledTypes.some(t => t.startsWith('pb'))) {
|
|
pb = window.PocketBase ? new PocketBase('https://pocketbase.ccllc.pro') : null;
|
|
}
|
|
// Only one-time setup
|
|
if (!document.getElementById('authPopup')) {
|
|
createPopup();
|
|
}
|
|
}
|
|
|
|
// --- Token Storage ---
|
|
function getToken(type) {
|
|
return localStorage.getItem(TOKEN_KEYS[type]) || '';
|
|
}
|
|
function setToken(type, value) {
|
|
if (value) localStorage.setItem(TOKEN_KEYS[type], value);
|
|
}
|
|
function clearToken(type) {
|
|
localStorage.removeItem(TOKEN_KEYS[type]);
|
|
}
|
|
|
|
// --- Info Extraction ---
|
|
function getUserInfo() {
|
|
if (pb && pb.authStore.model) {
|
|
return {
|
|
displayName: pb.authStore.model.display_name || pb.authStore.model.name || pb.authStore.model.email || pb.authStore.model.username || 'Unknown',
|
|
email: pb.authStore.model.email || pb.authStore.model.username || null
|
|
};
|
|
}
|
|
return { displayName: '', email: '' };
|
|
}
|
|
|
|
// --- Token Ensure/Refresh ---
|
|
async function ensureToken(type) {
|
|
let token = getToken(type);
|
|
if (token) return token;
|
|
if (type === 'pb-user' && pb) {
|
|
if (pb.authStore.isValid) {
|
|
setToken('pb-user', pb.authStore.token);
|
|
return pb.authStore.token;
|
|
}
|
|
// Choose login method: email/password or OAuth
|
|
if (pattern.includes('pb-user-oauth')) {
|
|
return await promptReauth('pb-user-oauth');
|
|
} else {
|
|
return await promptReauth('pb-user');
|
|
}
|
|
}
|
|
if (type === 'pb-superuser' && pb) {
|
|
// Prompt for superuser credentials
|
|
await promptReauth('pb-superuser');
|
|
// Use superuser credentials to login
|
|
const authData = await pb.collection('_superuser').authWithPassword(superuserCreds.email, superuserCreds.password);
|
|
setToken('pb-superuser', pb.authStore.token);
|
|
return pb.authStore.token;
|
|
}
|
|
if (type === 'pb-agent' && pb) {
|
|
// Prompt for agent credentials if not set
|
|
if (!agentCreds.email || !agentCreds.password) {
|
|
await promptReauth('pb-agent');
|
|
}
|
|
// Use agent credentials to login
|
|
const authData = await pb.collection('users').authWithPassword(agentCreds.email, agentCreds.password);
|
|
setToken('pb-agent', pb.authStore.token);
|
|
return pb.authStore.token;
|
|
}
|
|
if (type === 'graph-user' && pb) {
|
|
// OAuth only
|
|
return await promptReauth('graph-user');
|
|
}
|
|
if (type === 'graph-agent') {
|
|
// Prompt for agent credentials if not set
|
|
if (!agentCreds.email || !agentCreds.password) {
|
|
await promptReauth('graph-agent');
|
|
}
|
|
// Use agent credentials to get token (simulate, real flow is backend)
|
|
setToken('graph-agent', 'AGENT_TOKEN_' + agentCreds.email);
|
|
return getToken('graph-agent');
|
|
}
|
|
throw new Error('Unknown token type');
|
|
}
|
|
|
|
async function refreshToken(type) {
|
|
if (type === 'graph-user' && pb) {
|
|
try {
|
|
const authData = await pb.collection('users').authRefresh();
|
|
const meta = authData?.meta || pb?.authStore?.model?.meta || {};
|
|
const token = meta.graphAccessToken || meta.graph_token || meta.graphToken || meta.accessToken || meta.access_token || meta.token || meta.rawToken || meta?.authData?.access_token || '';
|
|
setToken('graph-user', token);
|
|
return token || '';
|
|
} catch (err) {
|
|
return '';
|
|
}
|
|
}
|
|
// Add refresh logic for other types as needed
|
|
return '';
|
|
}
|
|
|
|
// --- Reauth Popup ---
|
|
function createPopup() {
|
|
popup = document.createElement('div');
|
|
popup.id = 'authPopup';
|
|
popup.style = 'display:none;position:fixed;top:0;left:0;width:100vw;height:100vh;background:rgba(0,0,0,0.4);z-index:9999;align-items:center;justify-content:center;';
|
|
popup.innerHTML = `
|
|
<div style="background:#fff;padding:2em 2.5em;border-radius:1em;box-shadow:0 2px 24px #0002;text-align:center;max-width:350px;margin:auto;">
|
|
<h2 style="font-size:1.3em;margin-bottom:1em;">Sign In Required</h2>
|
|
<div id="authPopupForms"></div>
|
|
<div id="authPopupError" style="color:#b91c1c;margin-top:1em;display:none;"></div>
|
|
</div>
|
|
`;
|
|
document.body.appendChild(popup);
|
|
}
|
|
|
|
function showForm(type) {
|
|
const forms = {
|
|
'pb-user': `<input id="pbUserEmail" type="email" placeholder="Email" style="width:90%;margin-bottom:0.5em;"><br><input id="pbUserPassword" type="password" placeholder="Password" style="width:90%;margin-bottom:0.5em;"><br><button id="authPopupBtn" style="background:#2563eb;color:#fff;padding:0.7em 2em;border:none;border-radius:0.5em;font-size:1em;cursor:pointer;">Sign in</button>`,
|
|
'pb-user-oauth': `<button id="authPopupBtn" style="background:#2563eb;color:#fff;padding:0.7em 2em;border:none;border-radius:0.5em;font-size:1em;cursor:pointer;">Sign in with Microsoft</button>`,
|
|
'pb-superuser': `<input id="pbSuperuserEmail" type="email" placeholder="Superuser Email" style="width:90%;margin-bottom:0.5em;"><br><input id="pbSuperuserPassword" type="password" placeholder="Superuser Password" style="width:90%;margin-bottom:0.5em;"><br><button id="authPopupBtn" style="background:#d97706;color:#fff;padding:0.7em 2em;border:none;border-radius:0.5em;font-size:1em;cursor:pointer;">Sign in as Superuser</button>`,
|
|
'pb-agent': `<input id="pbAgentEmail" type="email" placeholder="Agent Email" style="width:90%;margin-bottom:0.5em;"><br><input id="pbAgentPassword" type="password" placeholder="Agent Password" style="width:90%;margin-bottom:0.5em;"><br><button id="authPopupBtn" style="background:#059669;color:#fff;padding:0.7em 2em;border:none;border-radius:0.5em;font-size:1em;cursor:pointer;">Sign in as Agent</button>`,
|
|
'graph-user': `<button id="authPopupBtn" style="background:#2563eb;color:#fff;padding:0.7em 2em;border:none;border-radius:0.5em;font-size:1em;cursor:pointer;">Sign in with Microsoft</button>`,
|
|
'graph-agent': `<input id="graphAgentEmail" type="email" placeholder="Agent Email" style="width:90%;margin-bottom:0.5em;"><br><input id="graphAgentPassword" type="password" placeholder="Agent Password" style="width:90%;margin-bottom:0.5em;"><br><button id="authPopupBtn" style="background:#059669;color:#fff;padding:0.7em 2em;border:none;border-radius:0.5em;font-size:1em;cursor:pointer;">Sign in as Graph Agent</button>`
|
|
};
|
|
document.getElementById('authPopupForms').innerHTML = forms[type] || '';
|
|
}
|
|
|
|
async function promptReauth(type) {
|
|
showForm(type);
|
|
popup.style.display = 'flex';
|
|
return new Promise((resolve, reject) => {
|
|
document.getElementById('authPopupBtn').onclick = async () => {
|
|
try {
|
|
if (type === 'pb-user') {
|
|
const email = document.getElementById('pbUserEmail').value;
|
|
const password = document.getElementById('pbUserPassword').value;
|
|
const authData = await pb.collection('users').authWithPassword(email, password);
|
|
setToken('pb-user', pb.authStore.token);
|
|
popup.style.display = 'none';
|
|
resolve(pb.authStore.token);
|
|
} else if (type === 'pb-user-oauth') {
|
|
const authData = await pb.collection('users').authWithOAuth2({ provider: 'microsoft' });
|
|
setToken('pb-user', authData?.meta?.graphAccessToken || '');
|
|
popup.style.display = 'none';
|
|
resolve(authData?.meta?.graphAccessToken || '');
|
|
} else if (type === 'pb-superuser') {
|
|
superuserCreds.email = document.getElementById('pbSuperuserEmail').value;
|
|
superuserCreds.password = document.getElementById('pbSuperuserPassword').value;
|
|
popup.style.display = 'none';
|
|
resolve();
|
|
} else if (type === 'pb-agent') {
|
|
agentCreds.email = document.getElementById('pbAgentEmail').value;
|
|
agentCreds.password = document.getElementById('pbAgentPassword').value;
|
|
popup.style.display = 'none';
|
|
resolve();
|
|
} else if (type === 'graph-user') {
|
|
const authData = await pb.collection('users').authWithOAuth2({ provider: 'microsoft' });
|
|
setToken('graph-user', authData?.meta?.graphAccessToken || '');
|
|
popup.style.display = 'none';
|
|
resolve(authData?.meta?.graphAccessToken || '');
|
|
} else if (type === 'graph-agent') {
|
|
agentCreds.email = document.getElementById('graphAgentEmail').value;
|
|
agentCreds.password = document.getElementById('graphAgentPassword').value;
|
|
popup.style.display = 'none';
|
|
resolve();
|
|
}
|
|
} catch (err) {
|
|
document.getElementById('authPopupError').textContent = err.message || 'Authentication failed.';
|
|
document.getElementById('authPopupError').style.display = '';
|
|
}
|
|
};
|
|
});
|
|
}
|
|
|
|
// --- API ---
|
|
return {
|
|
use,
|
|
getToken,
|
|
setToken,
|
|
clearToken,
|
|
getUserInfo,
|
|
ensureToken,
|
|
refreshToken,
|
|
promptReauth
|
|
};
|
|
})();
|
|
|
|
window.Auth = Auth;
|
|
|
|
// Example usage:
|
|
// Auth.use('pb-graph');
|
|
// const token = await Auth.ensureToken('graph');
|
|
// const user = Auth.getUserInfo();
|
|
// All login prompts are handled via the popup, which is hidden unless needed.
|