Skip to main content
Glama

Sentry MCP

Official
by getsentry
auth-context.tsx6.41 kB
import { createContext, useContext, useState, useEffect, useCallback, useRef, type ReactNode, } from "react"; import type { AuthContextType } from "../components/chat/types"; import { isOAuthSuccessMessage, isOAuthErrorMessage, } from "../components/chat/types"; const POPUP_CHECK_INTERVAL = 1000; const AuthContext = createContext<AuthContextType | undefined>(undefined); interface AuthProviderProps { children: ReactNode; } export function AuthProvider({ children }: AuthProviderProps) { const [isLoading, setIsLoading] = useState(true); const [isAuthenticated, setIsAuthenticated] = useState(false); const [isAuthenticating, setIsAuthenticating] = useState(false); const [authError, setAuthError] = useState(""); // Keep refs for cleanup const popupRef = useRef<Window | null>(null); const intervalRef = useRef<number | null>(null); // Check if authenticated by making a request to the server useEffect(() => { // Check authentication status fetch("/api/auth/status", { credentials: "include" }) .then((res) => res.ok) .then((authenticated) => { setIsAuthenticated(authenticated); setIsLoading(false); }) .catch(() => { setIsAuthenticated(false); setIsLoading(false); }); }, []); // Process OAuth result from localStorage const processOAuthResult = useCallback((data: unknown) => { if (isOAuthSuccessMessage(data)) { // Verify session on server before marking authenticated fetch("/api/auth/status", { credentials: "include" }) .then((res) => res.ok) .then((authenticated) => { if (authenticated) { // Fully reload the app to pick up new auth context/cookies // This avoids intermediate/loading states and ensures a clean session window.location.reload(); } else { setIsAuthenticated(false); setAuthError( "Authentication not completed. Please finish sign-in.", ); setIsAuthenticating(false); } }) .catch(() => { setIsAuthenticated(false); setAuthError("Failed to verify authentication."); setIsAuthenticating(false); }); // Cleanup interval and popup reference if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } if (popupRef.current) { popupRef.current = null; } } else if (isOAuthErrorMessage(data)) { setAuthError(data.error || "Authentication failed"); setIsAuthenticating(false); // Cleanup interval and popup reference if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } if (popupRef.current) { popupRef.current = null; } } }, []); // Cleanup on unmount useEffect(() => { return () => { if (intervalRef.current) { clearInterval(intervalRef.current); } }; }, []); const handleOAuthLogin = useCallback(() => { setIsAuthenticating(true); setAuthError(""); const desiredWidth = Math.max(Math.min(window.screen.availWidth, 900), 600); const desiredHeight = Math.min(window.screen.availHeight, 900); const windowFeatures = `width=${desiredWidth},height=${desiredHeight},resizable=yes,scrollbars=yes`; // Clear any stale results before opening popup try { localStorage.removeItem("oauth_result"); } catch { // ignore storage errors } const popup = window.open( "/api/auth/authorize", "sentry-oauth", windowFeatures, ); if (!popup) { setAuthError("Popup blocked. Please allow popups and try again."); setIsAuthenticating(false); return; } popupRef.current = popup; // Poll for OAuth result in localStorage // We don't check popup.closed as it's unreliable with cross-origin windows intervalRef.current = window.setInterval(() => { // Check localStorage for auth result const storedResult = localStorage.getItem("oauth_result"); if (storedResult) { try { const result = JSON.parse(storedResult); localStorage.removeItem("oauth_result"); processOAuthResult(result); // Clear interval since we got a result if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } popupRef.current = null; } catch (e) { // Invalid stored result, continue polling } } }, POPUP_CHECK_INTERVAL); // Stop polling after 5 minutes (safety timeout) setTimeout(() => { if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; // Final check if we're authenticated fetch("/api/auth/status", { credentials: "include" }) .then((res) => res.ok) .then((authenticated) => { if (authenticated) { window.location.reload(); } else { setIsAuthenticating(false); setAuthError("Authentication timed out. Please try again."); } }) .catch(() => { setIsAuthenticating(false); setAuthError("Authentication timed out. Please try again."); }); } }, 300000); // 5 minutes }, [processOAuthResult]); const handleLogout = useCallback(async () => { try { await fetch("/api/auth/logout", { method: "POST", credentials: "include", }); } catch { // Ignore errors, proceed with local logout } setIsAuthenticated(false); }, []); const clearAuthState = useCallback(() => { setIsAuthenticated(false); setAuthError(""); }, []); const value: AuthContextType = { isLoading, isAuthenticated, authToken: "", // Keep for backward compatibility isAuthenticating, authError, handleOAuthLogin, handleLogout, clearAuthState, }; return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>; } export function useAuth(): AuthContextType { const context = useContext(AuthContext); if (context === undefined) { throw new Error("useAuth must be used within an AuthProvider"); } return context; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/getsentry/sentry-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server