fix: Implement gallery-specific authentication tokens

- Fix issue where different galleries shared authentication
- Store gallery tokens with slug-specific keys in localStorage
- Remove global gallery_token cookie approach
- Each gallery now maintains its own authentication state
- Add cleanup for legacy authentication data

This ensures that accessing different galleries requires separate authentication
and prevents cross-gallery authentication leakage.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-09 08:05:04 +02:00
parent c8cfce3e36
commit cf32b01356
4 changed files with 73 additions and 27 deletions
+39 -12
View File
@@ -1,7 +1,7 @@
import React, { createContext, useContext, useState, useEffect } from 'react';
import type { ReactNode } from 'react';
import { getAuthToken } from '../config/api';
import { authService } from '../services';
import { cleanupOldGalleryAuth } from '../utils/cleanupGalleryAuth';
interface GalleryEvent {
id: number;
@@ -42,20 +42,42 @@ export const GalleryAuthProvider: React.FC<GalleryAuthProviderProps> = ({ childr
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// Get current gallery slug from URL
const getCurrentGallerySlug = () => {
const pathParts = window.location.pathname.split('/');
if (pathParts[1] === 'gallery' && pathParts[2]) {
return pathParts[2];
}
return null;
};
useEffect(() => {
// Clean up old authentication data on mount
cleanupOldGalleryAuth();
// Check if user has a valid token on mount
const token = getAuthToken(false);
if (token) {
// Try to restore event data from localStorage
const storedEvent = localStorage.getItem('gallery_event');
if (storedEvent) {
const currentSlug = getCurrentGallerySlug();
if (currentSlug) {
// Try to restore event data from localStorage with slug-specific key
const storedEvent = localStorage.getItem(`gallery_event_${currentSlug}`);
const storedToken = localStorage.getItem(`gallery_token_${currentSlug}`);
if (storedEvent && storedToken) {
try {
const eventData = JSON.parse(storedEvent);
setEvent(eventData);
setIsAuthenticated(true);
// Verify the stored event matches the current gallery slug
if (eventData && eventData.id) {
setEvent(eventData);
setIsAuthenticated(true);
} else {
// Clear invalid data
localStorage.removeItem(`gallery_event_${currentSlug}`);
localStorage.removeItem(`gallery_token_${currentSlug}`);
}
} catch (error) {
console.error('Failed to parse stored event data');
localStorage.removeItem('gallery_event');
localStorage.removeItem(`gallery_event_${currentSlug}`);
localStorage.removeItem(`gallery_token_${currentSlug}`);
}
}
}
@@ -70,8 +92,9 @@ export const GalleryAuthProvider: React.FC<GalleryAuthProviderProps> = ({ childr
setEvent(response.event);
setIsAuthenticated(true);
// Store event data in localStorage
localStorage.setItem('gallery_event', JSON.stringify(response.event));
// Store event data and token in localStorage with slug-specific key
localStorage.setItem(`gallery_event_${slug}`, JSON.stringify(response.event));
localStorage.setItem(`gallery_token_${slug}`, response.token);
} catch (err: any) {
setError(err.response?.data?.error || 'Invalid password');
throw err;
@@ -81,10 +104,14 @@ export const GalleryAuthProvider: React.FC<GalleryAuthProviderProps> = ({ childr
};
const logout = () => {
const currentSlug = getCurrentGallerySlug();
if (currentSlug) {
localStorage.removeItem(`gallery_event_${currentSlug}`);
localStorage.removeItem(`gallery_token_${currentSlug}`);
}
authService.galleryLogout();
setIsAuthenticated(false);
setEvent(null);
localStorage.removeItem('gallery_event');
};
return (