examforge / src /engines /auth /services /TenantAuthService.ts
Benjahmin's picture
feat(ui): implement design token system and core components
bf954c9
Raw
History Blame Contribute Delete
3.07 kB
import { AuthenticatedSession, SuperRole } from '../types';
export class TenantAuthService {
private static SESSION_KEY = 'examforge_auth_session';
private static TOKEN_KEY = 'local_user_token';
/**
* Safe getter for current active session
*/
static getSession(): AuthenticatedSession | null {
try {
const val = localStorage.getItem(this.SESSION_KEY);
if (!val) return null;
const parsed = JSON.parse(val) as AuthenticatedSession;
// Revoke if expired
if (Date.now() > parsed.expiresAt) {
this.clearSession();
return null;
}
return parsed;
} catch {
return null;
}
}
/**
* Save active credentials locally
*/
static saveSession(session: AuthenticatedSession) {
try {
localStorage.setItem(this.SESSION_KEY, JSON.stringify(session));
localStorage.setItem(this.TOKEN_KEY, session.token);
} catch (e) {
console.warn("Storage failed to update session:", e);
}
}
/**
* Clear active trace credentials on logout
*/
static clearSession() {
localStorage.removeItem(this.SESSION_KEY);
localStorage.removeItem(this.TOKEN_KEY);
localStorage.removeItem('local_user_profile');
}
/**
* Execute authentication using Tenant boundaries
*/
static async loginTenant(payload: {
idOrEmail: string;
pinOrPass: string;
schoolId: string;
}): Promise<AuthenticatedSession> {
const isEmail = payload.idOrEmail.includes('@');
const endpoint = isEmail ? '/api/auth/login-email' : '/api/auth/login';
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Tenant-School-ID': payload.schoolId, // Strict school boundary header
},
body: JSON.stringify({
id: payload.idOrEmail,
email: payload.idOrEmail,
pin: payload.pinOrPass,
password: payload.pinOrPass,
schoolId: payload.schoolId
}),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({ error: 'Verification failed.' }));
throw new Error(errorData.error || 'Failed to establish tenant session.');
}
const { user, customToken } = await response.json();
const formedSession: AuthenticatedSession = {
uid: user.id || user.uid,
role: (user.role as SuperRole) || 'student',
schoolId: payload.schoolId || user.schoolId || 'pilot-default-school',
email: user.email,
fullName: user.fullName || 'Anonymous Candidate',
token: customToken || localStorage.getItem(this.TOKEN_KEY) || '',
issuedAt: Date.now(),
expiresAt: Date.now() + 24 * 60 * 60 * 1000, // 24 hours
};
this.saveSession(formedSession);
return formedSession;
}
/**
* Enforces role access verification dynamically in React routers or hooks
*/
static authorizeRole(requiredRoles: SuperRole[]): boolean {
const s = this.getSession();
if (!s) return false;
return requiredRoles.includes(s.role);
}
}