Files
boc/quixzoom-sso-auth/sso-client.js
T
Bernt 48ea61cdcc docs: add quixzoom-auth-core product to AAMOS
- Product documentation in docs/products/
- Updated MEMORY.md with product info
- quiXzoom Auth Core as AAMOS Identity product
2026-07-14 09:58:53 +00:00

224 lines
4.7 KiB
JavaScript

/**
* quiXzoom SSO Client
* Universal auth client for all quixzoom properties
*
* Usage:
* import { quixzoomAuth } from './sso-client.js';
*
* // Check if logged in
* const user = await quixzoomAuth.getUser();
*
* // Login
* await quixzoomAuth.login(email, password);
*
* // Logout
* await quixzoomAuth.logout();
*/
const AUTH_BASE_URL = 'https://auth.quixzoom.com';
const COOKIE_NAMES = ['qz_access_token', 'qz_refresh_token'];
class QuixzoomAuth {
constructor() {
this.user = null;
this.tokenRefreshTimer = null;
}
/**
* Initialize auth - call on page load
*/
async init() {
// Check if we have a valid session
const user = await this.getUser();
if (user) {
this.startTokenRefresh();
}
return user;
}
/**
* Get current user (checks cookie automatically)
*/
async getUser() {
try {
const response = await fetch(`${AUTH_BASE_URL}/auth/me`, {
method: 'GET',
credentials: 'include', // Important: sends cookies
headers: {
'Accept': 'application/json',
}
});
if (response.ok) {
const data = await response.json();
this.user = data.user;
return data.user;
}
// Try to refresh if access token expired
if (response.status === 401) {
const refreshed = await this.refreshToken();
if (refreshed) {
return this.getUser();
}
}
return null;
} catch (error) {
console.error('Auth check failed:', error);
return null;
}
}
/**
* Login with email/password
*/
async login(email, password, deviceInfo = {}) {
const response = await fetch(`${AUTH_BASE_URL}/auth/login`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
email,
password,
device_info: deviceInfo,
redirect_url: window.location.href,
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Login failed');
}
const data = await response.json();
this.user = data.user;
this.startTokenRefresh();
return data;
}
/**
* Refresh access token
*/
async refreshToken() {
try {
const response = await fetch(`${AUTH_BASE_URL}/auth/refresh`, {
method: 'POST',
credentials: 'include',
headers: {
'Accept': 'application/json',
}
});
if (response.ok) {
const data = await response.json();
this.user = data.user;
return true;
}
return false;
} catch (error) {
console.error('Token refresh failed:', error);
return false;
}
}
/**
* Logout
*/
async logout() {
try {
await fetch(`${AUTH_BASE_URL}/auth/logout`, {
method: 'POST',
credentials: 'include',
});
} catch (error) {
console.error('Logout error:', error);
}
this.user = null;
this.stopTokenRefresh();
// Clear any local storage
localStorage.removeItem('quixzoom_user');
return true;
}
/**
* Check if user is authenticated
*/
isAuthenticated() {
return this.user !== null;
}
/**
* Redirect to login if not authenticated
*/
async requireAuth(redirectUrl = window.location.href) {
const user = await this.getUser();
if (!user) {
// Store intended URL
sessionStorage.setItem('auth_redirect', redirectUrl);
// Redirect to login page
const loginUrl = `https://auth.quixzoom.com/login?redirect=${encodeURIComponent(redirectUrl)}`;
window.location.href = loginUrl;
return null;
}
return user;
}
/**
* Start automatic token refresh
*/
startTokenRefresh() {
// Refresh 2 minutes before expiry
const refreshInterval = (15 - 2) * 60 * 1000; // 13 minutes
this.stopTokenRefresh();
this.tokenRefreshTimer = setInterval(() => {
this.refreshToken();
}, refreshInterval);
}
/**
* Stop automatic token refresh
*/
stopTokenRefresh() {
if (this.tokenRefreshTimer) {
clearInterval(this.tokenRefreshTimer);
this.tokenRefreshTimer = null;
}
}
/**
* Get auth headers for API requests
*/
getAuthHeaders() {
// Cookies are sent automatically, but we can add Bearer if needed
return {
'Accept': 'application/json',
};
}
}
// Singleton instance
export const quixzoomAuth = new QuixzoomAuth();
// Auto-init on import
quixzoomAuth.init();
// Also expose for non-module usage
if (typeof window !== 'undefined') {
window.quixzoomAuth = quixzoomAuth;
}