| | |
| | | this.nonces = {}; |
| | | |
| | | this.subscribers = new Set(); |
| | | this.storageKey = 'jvb_auth_state'; |
| | | this.cacheMetaKey = 'jvb_auth_meta'; |
| | | this.storageKey = `${jvbBase.base}auth_state`; |
| | | this.cacheMetaKey = `${jvbBase.base}auth_meta`; |
| | | this.cacheExpiry = 5 * 60 * 1000; // 5 minutes |
| | | |
| | | this.init(); |
| | |
| | | */ |
| | | async init() { |
| | | if (this.isAuthenticating) { |
| | | // Wait for existing auth to complete |
| | | return new Promise(resolve => { |
| | | const checkAuth = setInterval(() => { |
| | | if (this.initialized) { |
| | | clearInterval(checkAuth); |
| | | resolve(); |
| | | } |
| | | }, 50); |
| | | }); |
| | | return this.ready(); |
| | | } |
| | | |
| | | this.isAuthenticating = true; |
| | | |
| | | try { |
| | | // Check if we have cached auth and cookie hasn't changed |
| | | const cached = this.getCachedAuth(); |
| | | if (cached) { |
| | | this.setAuthData(cached); |
| | |
| | | return; |
| | | } |
| | | |
| | | // Fetch fresh auth data |
| | | await this.fetchAuth(); |
| | | |
| | | } catch (error) { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Refresh nonce if authentication fails |
| | | */ |
| | | async refreshNonce(action = 'wp_rest') { |
| | | try { |
| | | await this.fetchAuth(); |
| | | return this.getNonce(action); |
| | | } catch (error) { |
| | | console.error('Failed to refresh nonce:', error); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Fetch with automatic nonce refresh on auth failure |
| | | * Use this for all authenticated API requests |
| | | */ |
| | | async fetch(url, options = {}) { |
| | | const attempt = async (retryCount = 0) => { |
| | | const isFormData = options.body instanceof FormData; |
| | | |
| | | const headers = { |
| | | ...(!isFormData && { 'Content-Type': 'application/json' }), |
| | | ...options.headers, |
| | | 'X-WP-Nonce': this.getNonce() |
| | | }; |
| | | |
| | | const response = await fetch(url, { |
| | | ...options, |
| | | credentials: 'same-origin', |
| | | headers |
| | | }); |
| | | |
| | | if ((response.status === 403 || response.status === 401) && retryCount === 0) { |
| | | const result = await response.clone().json(); |
| | | if (result.code === 'rest_cookie_invalid_nonce' || result.message?.includes('Cookie check')) { |
| | | console.log('Nonce invalid, refreshing auth...'); |
| | | await this.refresh(); |
| | | return attempt(retryCount + 1); |
| | | } |
| | | } |
| | | |
| | | return response; |
| | | }; |
| | | |
| | | return attempt(); |
| | | } |
| | | |
| | | /** |
| | | * Fetch authentication status from API |
| | | */ |
| | | async fetchAuth() { |
| | |
| | | * Set authentication data |
| | | */ |
| | | setAuthData(authData) { |
| | | const wasAuthenticated = this.initialized && this.authenticated; |
| | | |
| | | this.authenticated = authData.authenticated || false; |
| | | this.user = authData.user || false; |
| | | this.nonces = authData.nonces || {}; |
| | | |
| | | // Session expired — was logged in, now isn't |
| | | if (wasAuthenticated && !this.authenticated) { |
| | | window.location.href = `/login?redirect_to=${encodeURIComponent(window.location.href)}`; |
| | | } |
| | | } |
| | | |
| | | /** |