This commit is contained in:
@@ -2,98 +2,72 @@
|
|||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie';
|
||||||
import { AuthContext } from './AuthContext';
|
import { AuthContext } from './AuthContext';
|
||||||
import { sha512 } from '../utils/crypto';
|
import { sha512 } from '../utils/crypto';
|
||||||
import type { AuthResponse } from '../types/auth';
|
|
||||||
|
|
||||||
const TOKEN_KEY = 'ktty_shared_token';
|
const TOKEN_KEY = 'ktty_shared_token';
|
||||||
|
|
||||||
/**
|
|
||||||
* Konfiguracja ciasteczek dla SSO.
|
|
||||||
* Na localhost ciasteczka domenowe nie działają, więc używamy undefined.
|
|
||||||
* Na produkcji używamy '.ktty.is', co pozwala współdzielić sesję między subdomenami.
|
|
||||||
*/
|
|
||||||
const getCookieConfig = () => {
|
const getCookieConfig = () => {
|
||||||
const hostname = window.location.hostname;
|
const hostname = window.location.hostname;
|
||||||
const isLocal = hostname === 'localhost' || hostname === '127.0.0.1';
|
const isLocal = hostname === 'localhost' || hostname === '127.0.0.1';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
domain: isLocal ? undefined : '.ktty.is',
|
domain: isLocal ? undefined : '.ktty.is',
|
||||||
secure: !isLocal, // Wymagane HTTPS na produkcji
|
secure: !isLocal,
|
||||||
sameSite: 'Lax' as const,
|
sameSite: 'Lax' as const,
|
||||||
expires: 7 // Token ważny przez 7 dni
|
path: '/',
|
||||||
|
expires: 7
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSubdomain = (): string | null => {
|
|
||||||
const hostname = window.location.hostname;
|
|
||||||
const parts = hostname.split('.');
|
|
||||||
if (parts.length <= 2) return null;
|
|
||||||
return parts[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
export function AuthProvider({ children }: { children: ReactNode }) {
|
export function AuthProvider({ children }: { children: ReactNode }) {
|
||||||
const config = getCookieConfig();
|
|
||||||
const subdomain = getSubdomain();
|
|
||||||
|
|
||||||
// Inicjalizacja stanu: sprawdzamy ciasteczko natychmiast przy ładowaniu strony.
|
|
||||||
// Dzięki temu SSO działa bez opóźnień.
|
|
||||||
const [token, setToken] = useState<string | null>(() => Cookies.get(TOKEN_KEY) || null);
|
const [token, setToken] = useState<string | null>(() => Cookies.get(TOKEN_KEY) || null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null); // DODANE
|
||||||
|
|
||||||
const authRequest = useCallback(async (endpoint: 'signUp' | 'signIn', name: string, pass: string) => {
|
const authRequest = useCallback(async (endpoint: 'signIn' | 'signUp', name: string, pass: string) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const hashedPassword = await sha512(pass);
|
const hashedPassword = await sha512(pass);
|
||||||
// Vite Proxy przekaże to na https://www.ktty.is/api/...
|
const response = await fetch(`/api/v1/user/${endpoint}`, {
|
||||||
const fullUrl = `/api/v1/user/${endpoint}`;
|
|
||||||
|
|
||||||
const response = await fetch(fullUrl, {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: { 'Content-Type': 'application/json' },
|
||||||
'accept': 'application/json',
|
credentials: 'include',
|
||||||
'Content-Type': 'application/json',
|
body: JSON.stringify({ name, password: hashedPassword }),
|
||||||
'X-Subdomain': subdomain || '',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ name, password: hashedPassword, ttl: 86400 }),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const data: AuthResponse = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(data?.error || data?.message || 'Błąd autoryzacji');
|
throw new Error(data?.message || 'Błąd autoryzacji');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data?.token) {
|
if (data?.token) {
|
||||||
// Zapisujemy token z flagą domain: '.ktty.is'
|
Cookies.set(TOKEN_KEY, data.token, getCookieConfig());
|
||||||
Cookies.set(TOKEN_KEY, data.token, config);
|
|
||||||
setToken(data.token);
|
setToken(data.token);
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
const msg = err instanceof Error ? err.message : 'Wystąpił nieoczekiwany błąd';
|
const msg = err instanceof Error ? err.message : 'Wystąpił błąd';
|
||||||
setError(msg);
|
setError(msg);
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [subdomain, config]);
|
}, []);
|
||||||
|
|
||||||
const logout = useCallback(() => {
|
const logout = useCallback(() => {
|
||||||
// Usuwamy ciasteczko z tej samej domeny, na której zostało zapisane
|
Cookies.remove(TOKEN_KEY, { domain: getCookieConfig().domain, path: '/' });
|
||||||
Cookies.remove(TOKEN_KEY, { domain: config.domain });
|
|
||||||
setToken(null);
|
setToken(null);
|
||||||
}, [config]);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthContext.Provider value={{
|
<AuthContext.Provider value={{
|
||||||
isAuthenticated: !!token,
|
isAuthenticated: !!token,
|
||||||
token,
|
token,
|
||||||
loading,
|
loading,
|
||||||
error,
|
error, // TERAZ OBECNE
|
||||||
signIn: (n, p) => authRequest('signIn', n, p),
|
signIn: (n, p) => authRequest('signIn', n, p),
|
||||||
signUp: (n, p) => authRequest('signUp', n, p),
|
signUp: (n, p) => authRequest('signUp', n, p), // TERAZ OBECNE
|
||||||
logout
|
logout
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -14,26 +14,41 @@ export default defineConfig(({ mode }) => {
|
|||||||
const backendTarget = env.VITE_API_TARGET;
|
const backendTarget = env.VITE_API_TARGET;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
envDir: envDirectory,
|
envDir: envDirectory,
|
||||||
plugins: [
|
plugins: [
|
||||||
react() as PluginOption,
|
react() as PluginOption,
|
||||||
tailwindcss() as PluginOption,
|
tailwindcss() as PluginOption,
|
||||||
],
|
],
|
||||||
server: {
|
server: {
|
||||||
port: 6568,
|
port: 6568,
|
||||||
host: true,
|
host: true, // Pozwala na dostęp przez IP w sieci lokalnej
|
||||||
|
// Jeśli testujesz subdomeny lokalnie (np. app.local.ktty.is), dodaj to:
|
||||||
|
allowedHosts: [
|
||||||
|
'.ktty.is',
|
||||||
|
'localhost',
|
||||||
|
'127.0.0.1',
|
||||||
|
|
||||||
|
],
|
||||||
|
// vite.config.ts
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: backendTarget,
|
target: backendTarget,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
secure: false,
|
secure: false,
|
||||||
|
cookieDomainRewrite: {
|
||||||
|
"*": ""
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
preview: {
|
preview: {
|
||||||
port: 6568,
|
port: 6568,
|
||||||
allowedHosts: true,
|
allowedHosts: true,
|
||||||
},
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve(__dirname, './src'),
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
Reference in New Issue
Block a user