- Add Auth0 SPA SDK integration for authentication - Create login/logout flow with Auth0 - Add user dashboard page with: - Profile display and editing - Credits balance and transaction history - API key management (create/revoke) - Account settings - Add API client for backend communication - Update navbar with Login/Dashboard buttons - Preserve existing landing page design Configuration: - Auth0 Domain: dev-t13zhs74oltgqtfxf.auth0.com - API base: /api (proxied through Caddy)
177 lines
4.7 KiB
JavaScript
177 lines
4.7 KiB
JavaScript
/**
|
|
* Moxiegen Authentication Module
|
|
* Handles Auth0 authentication, login/logout, and token management
|
|
*/
|
|
|
|
class MoxieAuth {
|
|
constructor() {
|
|
this.auth0Client = null;
|
|
this.isAuthenticated = false;
|
|
this.user = null;
|
|
this.token = null;
|
|
this.isInitialized = false;
|
|
}
|
|
|
|
/**
|
|
* Initialize Auth0 client
|
|
*/
|
|
async init() {
|
|
if (this.isInitialized) return;
|
|
|
|
try {
|
|
// Dynamically load Auth0 SPA SDK if not already loaded
|
|
if (typeof auth0 === 'undefined') {
|
|
await this.loadScript('https://cdn.auth0.com/js/auth0-spa-js/2.0/auth0-spa-js.production.js');
|
|
}
|
|
|
|
// Create Auth0 client
|
|
this.auth0Client = await auth0.createAuth0Client({
|
|
domain: CONFIG.auth0.domain,
|
|
clientId: CONFIG.auth0.clientId,
|
|
authorizationParams: {
|
|
audience: CONFIG.auth0.audience,
|
|
redirect_uri: CONFIG.auth0.redirectUri
|
|
},
|
|
cacheLocation: 'localstorage',
|
|
useRefreshTokens: true
|
|
});
|
|
|
|
// Check if coming back from Auth0 redirect
|
|
const query = window.location.search;
|
|
if (query.includes('code=') && query.includes('state=')) {
|
|
await this.auth0Client.handleRedirectCallback();
|
|
// Remove query params from URL
|
|
window.history.replaceState({}, document.title, window.location.pathname);
|
|
}
|
|
|
|
// Check authentication status
|
|
this.isAuthenticated = await this.auth0Client.isAuthenticated();
|
|
|
|
if (this.isAuthenticated) {
|
|
this.user = await this.auth0Client.getUser();
|
|
this.token = await this.auth0Client.getTokenSilently();
|
|
}
|
|
|
|
this.isInitialized = true;
|
|
console.log('Auth0 initialized. Authenticated:', this.isAuthenticated);
|
|
|
|
} catch (error) {
|
|
console.error('Failed to initialize Auth0:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load external script
|
|
*/
|
|
loadScript(src) {
|
|
return new Promise((resolve, reject) => {
|
|
const script = document.createElement('script');
|
|
script.src = src;
|
|
script.onload = resolve;
|
|
script.onerror = reject;
|
|
document.head.appendChild(script);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Login - redirect to Auth0
|
|
*/
|
|
async login(returnTo = null) {
|
|
if (!this.auth0Client) {
|
|
await this.init();
|
|
}
|
|
|
|
await this.auth0Client.loginWithRedirect({
|
|
authorizationParams: {
|
|
audience: CONFIG.auth0.audience,
|
|
redirect_uri: returnTo || CONFIG.auth0.redirectUri
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Logout
|
|
*/
|
|
async logout() {
|
|
if (!this.auth0Client) {
|
|
await this.init();
|
|
}
|
|
|
|
// Clear local state
|
|
this.isAuthenticated = false;
|
|
this.user = null;
|
|
this.token = null;
|
|
|
|
// Logout from Auth0
|
|
await this.auth0Client.logout({
|
|
logoutParams: {
|
|
returnTo: CONFIG.auth0.logoutUri
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get access token (silently refreshes if needed)
|
|
*/
|
|
async getToken() {
|
|
if (!this.auth0Client) {
|
|
await this.init();
|
|
}
|
|
|
|
if (!this.isAuthenticated) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
this.token = await this.auth0Client.getTokenSilently();
|
|
return this.token;
|
|
} catch (error) {
|
|
console.error('Failed to get token:', error);
|
|
// Token might be expired, try to re-authenticate
|
|
this.isAuthenticated = false;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get current user
|
|
*/
|
|
async getUser() {
|
|
if (!this.auth0Client) {
|
|
await this.init();
|
|
}
|
|
return this.user;
|
|
}
|
|
|
|
/**
|
|
* Check if user is authenticated
|
|
*/
|
|
async checkAuth() {
|
|
if (!this.auth0Client) {
|
|
await this.init();
|
|
}
|
|
this.isAuthenticated = await this.auth0Client.isAuthenticated();
|
|
if (this.isAuthenticated) {
|
|
this.user = await this.auth0Client.getUser();
|
|
this.token = await this.auth0Client.getTokenSilently();
|
|
}
|
|
return this.isAuthenticated;
|
|
}
|
|
|
|
/**
|
|
* Check if user has specific role
|
|
*/
|
|
hasRole(role) {
|
|
if (!this.user) return false;
|
|
const roles = this.user['https://moxiegen.client.guacamolebox.net/roles'] || [];
|
|
return roles.includes(role);
|
|
}
|
|
}
|
|
|
|
// Create singleton instance
|
|
const moxieAuth = new MoxieAuth();
|
|
|
|
// Export for use in other modules
|
|
window.moxieAuth = moxieAuth;
|