|
|
|
|
|
|
|
|
const STORAGE_KEY = 'hf_oauth_token'; |
|
|
const USER_INFO_KEY = 'hf_user_info'; |
|
|
const DEV_MODE_KEY = 'hf_dev_mode'; |
|
|
const API_BASE = '/api'; |
|
|
|
|
|
|
|
|
const isDevelopment = typeof window !== 'undefined' && |
|
|
(window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'); |
|
|
|
|
|
export interface OAuthUserInfo { |
|
|
id?: string; |
|
|
sub?: string; |
|
|
name: string; |
|
|
preferred_username?: string; |
|
|
preferredUsername?: string; |
|
|
picture?: string; |
|
|
avatarUrl?: string; |
|
|
} |
|
|
|
|
|
export interface OAuthResult { |
|
|
accessToken: string; |
|
|
accessTokenExpiresAt: Date; |
|
|
userInfo: OAuthUserInfo; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export async function initializeOAuth(): Promise<OAuthResult | null> { |
|
|
try { |
|
|
|
|
|
if (isDevelopment && isDevModeEnabled()) { |
|
|
const storedToken = getStoredToken(); |
|
|
const storedUserInfo = getStoredUserInfo(); |
|
|
|
|
|
if (storedToken && storedUserInfo) { |
|
|
return { |
|
|
accessToken: storedToken, |
|
|
accessTokenExpiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), |
|
|
userInfo: storedUserInfo, |
|
|
}; |
|
|
} |
|
|
return null; |
|
|
} |
|
|
|
|
|
|
|
|
const urlParams = new URLSearchParams(window.location.search); |
|
|
const sessionToken = urlParams.get('session'); |
|
|
|
|
|
if (sessionToken) { |
|
|
|
|
|
try { |
|
|
const response = await fetch(`${API_BASE}/auth/session?session=${sessionToken}`); |
|
|
if (response.ok) { |
|
|
const data = await response.json(); |
|
|
|
|
|
|
|
|
const userInfo: OAuthUserInfo = { |
|
|
id: data.user_info.sub || data.user_info.id, |
|
|
name: data.user_info.name, |
|
|
preferredUsername: data.user_info.preferred_username || data.user_info.preferredUsername, |
|
|
avatarUrl: data.user_info.picture || data.user_info.avatarUrl, |
|
|
}; |
|
|
|
|
|
const oauthResult: OAuthResult = { |
|
|
accessToken: data.access_token, |
|
|
accessTokenExpiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), |
|
|
userInfo, |
|
|
}; |
|
|
|
|
|
|
|
|
storeOAuthData(oauthResult); |
|
|
|
|
|
|
|
|
window.history.replaceState({}, document.title, window.location.pathname); |
|
|
|
|
|
return oauthResult; |
|
|
} |
|
|
} catch (error) { |
|
|
console.error('Failed to fetch session:', error); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const storedToken = getStoredToken(); |
|
|
const storedUserInfo = getStoredUserInfo(); |
|
|
|
|
|
if (storedToken && storedUserInfo) { |
|
|
return { |
|
|
accessToken: storedToken, |
|
|
accessTokenExpiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), |
|
|
userInfo: storedUserInfo, |
|
|
}; |
|
|
} |
|
|
|
|
|
return null; |
|
|
} catch (error) { |
|
|
console.error('OAuth initialization error:', error); |
|
|
return null; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export async function loginWithHuggingFace(): Promise<void> { |
|
|
try { |
|
|
|
|
|
const response = await fetch(`${API_BASE}/auth/login`); |
|
|
if (!response.ok) { |
|
|
throw new Error('Failed to get login URL'); |
|
|
} |
|
|
|
|
|
const data = await response.json(); |
|
|
|
|
|
window.location.href = data.login_url; |
|
|
} catch (error) { |
|
|
console.error('Failed to initiate OAuth login:', error); |
|
|
throw new Error('Failed to start login process'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function logout(): void { |
|
|
if (typeof window !== 'undefined') { |
|
|
localStorage.removeItem(STORAGE_KEY); |
|
|
localStorage.removeItem(USER_INFO_KEY); |
|
|
localStorage.removeItem(DEV_MODE_KEY); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function storeOAuthData(result: OAuthResult): void { |
|
|
if (typeof window !== 'undefined') { |
|
|
localStorage.setItem(STORAGE_KEY, result.accessToken); |
|
|
localStorage.setItem(USER_INFO_KEY, JSON.stringify(result.userInfo)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function getStoredToken(): string | null { |
|
|
if (typeof window !== 'undefined') { |
|
|
return localStorage.getItem(STORAGE_KEY); |
|
|
} |
|
|
return null; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function getStoredUserInfo(): OAuthUserInfo | null { |
|
|
if (typeof window !== 'undefined') { |
|
|
const userInfoStr = localStorage.getItem(USER_INFO_KEY); |
|
|
if (userInfoStr) { |
|
|
try { |
|
|
return JSON.parse(userInfoStr); |
|
|
} catch { |
|
|
return null; |
|
|
} |
|
|
} |
|
|
} |
|
|
return null; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function isAuthenticated(): boolean { |
|
|
return getStoredToken() !== null; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export async function validateAuthentication(): Promise<boolean> { |
|
|
const token = getStoredToken(); |
|
|
if (!token) { |
|
|
return false; |
|
|
} |
|
|
|
|
|
|
|
|
if (isDevelopment && token.startsWith('dev_token_')) { |
|
|
return true; |
|
|
} |
|
|
|
|
|
try { |
|
|
const response = await fetch(`${API_BASE}/auth/status`, { |
|
|
headers: { |
|
|
'Authorization': `Bearer ${token}`, |
|
|
}, |
|
|
}); |
|
|
|
|
|
if (response.status === 401) { |
|
|
|
|
|
logout(); |
|
|
return false; |
|
|
} |
|
|
|
|
|
if (!response.ok) { |
|
|
return false; |
|
|
} |
|
|
|
|
|
const data = await response.json(); |
|
|
return data.authenticated === true; |
|
|
} catch (error) { |
|
|
console.error('Failed to validate authentication:', error); |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function loginDevMode(username: string): OAuthResult { |
|
|
const mockToken = `dev_token_${username}_${Date.now()}`; |
|
|
const mockUserInfo: OAuthUserInfo = { |
|
|
id: `dev_${Date.now()}`, |
|
|
name: username, |
|
|
preferredUsername: username.toLowerCase().replace(/\s+/g, '_'), |
|
|
avatarUrl: `https://ui-avatars.com/api/?name=${encodeURIComponent(username)}&background=random&size=128`, |
|
|
}; |
|
|
|
|
|
const result: OAuthResult = { |
|
|
accessToken: mockToken, |
|
|
accessTokenExpiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), |
|
|
userInfo: mockUserInfo, |
|
|
}; |
|
|
|
|
|
|
|
|
storeOAuthData(result); |
|
|
|
|
|
if (typeof window !== 'undefined') { |
|
|
localStorage.setItem(DEV_MODE_KEY, 'true'); |
|
|
} |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function isDevModeEnabled(): boolean { |
|
|
if (typeof window !== 'undefined') { |
|
|
return localStorage.getItem(DEV_MODE_KEY) === 'true'; |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function isDevelopmentMode(): boolean { |
|
|
return isDevelopment; |
|
|
} |
|
|
|
|
|
|