Spaces:
Running
Running
import { writable, get } from "svelte/store"; | |
import { | |
oauthLoginUrl, | |
oauthHandleRedirectIfPresent, | |
type UserInfo, | |
} from "@huggingface/hub"; | |
export interface AuthState { | |
isAuthenticated: boolean; | |
user: UserInfo | null; | |
accessToken: string | null; | |
expiresAt: number | null; | |
loading: boolean; | |
error: string | null; | |
} | |
function createAuthStore() { | |
const { subscribe, set, update } = writable<AuthState>({ | |
isAuthenticated: false, | |
user: null, | |
accessToken: null, | |
expiresAt: null, | |
loading: true, | |
error: null, | |
}); | |
const getOAuthConfig = () => { | |
const isProduction = window.location.hostname.includes("hf.space"); | |
const clientId = "87f5f1d1-6e9e-4962-98f0-e5f3831ec988"; | |
let redirectUrl: string; | |
if (isProduction) { | |
redirectUrl = `https://${window.location.hostname}/auth/callback`; | |
} else { | |
redirectUrl = `http://localhost:7860/auth/callback`; | |
} | |
return { | |
clientId, | |
redirectUrl, | |
scopes: "openid profile inference-api", | |
}; | |
}; | |
return { | |
subscribe, | |
async init() { | |
update((state) => ({ ...state, loading: true })); | |
try { | |
const oauthResult = await oauthHandleRedirectIfPresent(); | |
if (oauthResult) { | |
const { accessToken, accessTokenExpiresAt, userInfo } = oauthResult; | |
set({ | |
isAuthenticated: true, | |
user: userInfo, | |
accessToken, | |
expiresAt: accessTokenExpiresAt | |
? accessTokenExpiresAt.getTime() | |
: null, | |
loading: false, | |
error: null, | |
}); | |
sessionStorage.setItem( | |
"hf_auth", | |
JSON.stringify({ | |
accessToken, | |
expiresAt: accessTokenExpiresAt | |
? accessTokenExpiresAt.getTime() | |
: null, | |
user: userInfo, | |
}), | |
); | |
return true; | |
} | |
const stored = sessionStorage.getItem("hf_auth"); | |
if (stored) { | |
const authData = JSON.parse(stored); | |
if (!authData.expiresAt || authData.expiresAt > Date.now()) { | |
set({ | |
isAuthenticated: true, | |
user: authData.user, | |
accessToken: authData.accessToken, | |
expiresAt: authData.expiresAt, | |
loading: false, | |
error: null, | |
}); | |
return true; | |
} else { | |
sessionStorage.removeItem("hf_auth"); | |
} | |
} | |
set({ | |
isAuthenticated: false, | |
user: null, | |
accessToken: null, | |
expiresAt: null, | |
loading: false, | |
error: null, | |
}); | |
return false; | |
} catch (error) { | |
console.error("Auth initialization error:", error); | |
set({ | |
isAuthenticated: false, | |
user: null, | |
accessToken: null, | |
expiresAt: null, | |
loading: false, | |
error: | |
error instanceof Error ? error.message : "Authentication failed", | |
}); | |
return false; | |
} | |
}, | |
async login() { | |
const config = getOAuthConfig(); | |
try { | |
const url = await oauthLoginUrl({ | |
clientId: config.clientId, | |
redirectUrl: config.redirectUrl, | |
scopes: config.scopes, | |
}); | |
window.location.href = url; | |
} catch (error) { | |
console.error("Login error:", error); | |
update((state) => ({ | |
...state, | |
error: error instanceof Error ? error.message : "Login failed", | |
})); | |
} | |
}, | |
logout() { | |
sessionStorage.removeItem("hf_auth"); | |
set({ | |
isAuthenticated: false, | |
user: null, | |
accessToken: null, | |
expiresAt: null, | |
loading: false, | |
error: null, | |
}); | |
}, | |
getToken(): string | null { | |
const state = get({ subscribe }); | |
return state.accessToken; | |
}, | |
isTokenValid(): boolean { | |
const state = get({ subscribe }); | |
if (!state.accessToken) return false; | |
if (!state.expiresAt) return true; | |
return state.expiresAt > Date.now(); | |
}, | |
}; | |
} | |
export const authStore = createAuthStore(); | |