Graphical PDS migrator for AT Protocol
1import { useEffect, useState } from "preact/hooks";
2import { IS_BROWSER } from "fresh/runtime";
3
4/**
5 * The OAuth callback props.
6 * @type {OAuthCallbackProps}
7 */
8interface OAuthCallbackProps {
9 error?: string;
10}
11
12/**
13 * The OAuth callback component.
14 * @param props - The OAuth callback props
15 * @returns The OAuth callback component
16 * @component
17 */
18export default function OAuthCallback(
19 { error: initialError }: OAuthCallbackProps,
20) {
21 const [error, setError] = useState<string | null>(initialError || null);
22 const [message, setMessage] = useState<string>(
23 "Completing authentication...",
24 );
25
26 useEffect(() => {
27 if (!IS_BROWSER) return;
28
29 console.log("OAuth callback page reached");
30 setMessage("OAuth callback page reached. Checking authentication...");
31
32 const checkAuth = async () => {
33 try {
34 // Check if there's an error in the URL
35 const params = new URLSearchParams(globalThis.location.search);
36 const errorParam = params.get("error");
37 if (errorParam) {
38 console.error("Auth error detected in URL params:", errorParam);
39 setError(`Authentication failed: ${errorParam}`);
40 return;
41 }
42
43 // Give cookies a moment to be processed
44 await new Promise((resolve) => setTimeout(resolve, 1500)); // Increased delay
45 setMessage("Checking if we're authenticated...");
46
47 // Check if we're authenticated by fetching current user
48 try {
49 const cookies = document.cookie
50 .split(";")
51 .map((c) => c.trim())
52 .filter((c) => c.length > 0);
53
54 console.log("Current cookies:", cookies);
55
56 const response = await fetch("/api/me", {
57 credentials: "include", // Explicitly include credentials
58 headers: {
59 "Accept": "application/json",
60 },
61 });
62
63 if (!response.ok) {
64 console.error(
65 "Profile API error:",
66 response.status,
67 response.statusText,
68 );
69 const text = await response.text();
70 console.error("Response body:", text);
71 throw new Error(`API returned ${response.status}`);
72 }
73
74 const user = await response.json();
75 console.log("Current user check result:", user);
76
77 if (user) {
78 console.log("Successfully authenticated", user);
79 setMessage("Authentication successful! Redirecting...");
80 // Redirect to home after a short delay
81 setTimeout(() => {
82 globalThis.location.href = "/";
83 }, 1000);
84 } else {
85 console.error("Auth check returned empty user object:", user);
86 setError("Authentication session not found - no user data");
87 }
88 } catch (apiErr: unknown) {
89 console.error("API error during auth check:", apiErr);
90 setError(
91 `Failed to verify authentication: ${
92 apiErr instanceof Error ? apiErr.message : "Unknown error"
93 }`,
94 );
95 }
96 } catch (err: unknown) {
97 console.error("General error in OAuth callback:", err);
98 setError(
99 `Failed to complete authentication: ${
100 err instanceof Error ? err.message : "Unknown error"
101 }`,
102 );
103 }
104 };
105
106 checkAuth();
107 }, []);
108
109 return (
110 <div class="flex items-center justify-center py-16">
111 <div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm p-8 max-w-md w-full text-center">
112 {error
113 ? (
114 <div>
115 <h2 class="text-2xl font-bold text-red-500 mb-4">
116 Authentication Failed
117 </h2>
118 <p class="text-red-500 mb-6">{error}</p>
119 <a
120 href="/login"
121 class="px-4 py-2 bg-blue-500 dark:bg-blue-600 text-white rounded-md hover:bg-blue-600 dark:hover:bg-blue-700 transition-colors"
122 >
123 Try Again
124 </a>
125 </div>
126 )
127 : (
128 <div>
129 <h2 class="text-2xl font-bold text-gray-800 dark:text-gray-200 mb-4">
130 Authentication in Progress
131 </h2>
132 <div class="flex justify-center mb-4">
133 <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 dark:border-blue-400">
134 </div>
135 </div>
136 <p class="text-gray-600 dark:text-gray-400">{message}</p>
137 </div>
138 )}
139 </div>
140 </div>
141 );
142}