mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-26 18:26:38 +00:00
153 lines
5.6 KiB
TypeScript
153 lines
5.6 KiB
TypeScript
import { json, redirect, type LoaderFunctionArgs, type MetaFunction } from '@remix-run/cloudflare';
|
|
import { useLoaderData } from '@remix-run/react';
|
|
import {
|
|
AuthError,
|
|
createUserSession,
|
|
exchangeCodeForToken,
|
|
fetchGitHubUser,
|
|
verifyState,
|
|
} from '~/lib/auth/github-oauth.server';
|
|
import BackgroundRays from '~/components/ui/BackgroundRays';
|
|
|
|
export const meta: MetaFunction = () => {
|
|
return [
|
|
{ title: 'Authenticating...' },
|
|
{ name: 'description', content: 'Completing GitHub authentication' },
|
|
];
|
|
};
|
|
|
|
export async function loader({ request, context }: LoaderFunctionArgs) {
|
|
// Get URL parameters
|
|
const url = new URL(request.url);
|
|
const code = url.searchParams.get('code');
|
|
const state = url.searchParams.get('state');
|
|
const error = url.searchParams.get('error');
|
|
const errorDescription = url.searchParams.get('error_description');
|
|
|
|
// Get the redirect URL from state or default to home
|
|
const redirectTo = url.searchParams.get('redirectTo') || '/';
|
|
|
|
// Handle GitHub OAuth errors
|
|
if (error) {
|
|
return json({
|
|
success: false,
|
|
error: error,
|
|
errorDescription: errorDescription || 'An error occurred during authentication',
|
|
redirectTo: '/',
|
|
});
|
|
}
|
|
|
|
// Validate required parameters
|
|
if (!code || !state) {
|
|
const errorParams = new URLSearchParams([
|
|
['error', 'invalid_request'],
|
|
['error_description', 'Missing required parameters']
|
|
]);
|
|
return redirect(`/auth/login?${errorParams}`);
|
|
}
|
|
|
|
try {
|
|
console.log('=== CALLBACK FLOW DEBUG ===');
|
|
console.log('Step 1: Starting state verification...');
|
|
|
|
// Verify state parameter to prevent CSRF attacks
|
|
const cookieHeader = await verifyState(request, state, context);
|
|
console.log('Step 1: ✅ State verification successful');
|
|
|
|
console.log('Step 2: Starting token exchange...');
|
|
// Exchange authorization code for access token
|
|
const { accessToken } = await exchangeCodeForToken(code, state, context);
|
|
console.log('Step 2: ✅ Token exchange successful');
|
|
|
|
console.log('Step 3: Starting user data fetch...');
|
|
// Fetch user data from GitHub API
|
|
const user = await fetchGitHubUser(accessToken);
|
|
console.log('Step 3: ✅ User data fetch successful');
|
|
|
|
console.log('Step 4: Creating user session...');
|
|
// Create user session and redirect
|
|
const result = await createUserSession(
|
|
request,
|
|
{
|
|
accessToken,
|
|
user,
|
|
},
|
|
redirectTo,
|
|
context
|
|
);
|
|
console.log('Step 4: ✅ User session created successfully');
|
|
console.log('============================');
|
|
return result;
|
|
} catch (error) {
|
|
console.error('=== AUTHENTICATION FAILURE ===');
|
|
console.error('Error type:', error.constructor.name);
|
|
console.error('Error message:', error.message);
|
|
console.error('Error stack:', error.stack);
|
|
console.error('===============================');
|
|
|
|
let errorMessage = 'An unexpected error occurred during authentication';
|
|
let errorCode = 'server_error';
|
|
|
|
if (error instanceof AuthError) {
|
|
errorMessage = error.message;
|
|
errorCode = 'auth_error';
|
|
}
|
|
|
|
// For authentication failures, redirect to login with error params
|
|
const errorParams = new URLSearchParams([
|
|
['error', errorCode],
|
|
['error_description', errorMessage]
|
|
]);
|
|
|
|
return redirect(`/auth/login?${errorParams}`);
|
|
}
|
|
}
|
|
|
|
export default function AuthCallback() {
|
|
const data = useLoaderData<typeof loader>();
|
|
|
|
// Only render the component if we have error data
|
|
// On successful auth, the server will redirect immediately
|
|
if (data && 'success' in data && !data.success) {
|
|
return (
|
|
<div className="flex flex-col items-center justify-center min-h-screen bg-bolt-elements-background-depth-1">
|
|
<BackgroundRays />
|
|
|
|
<div className="w-full max-w-md p-8 space-y-8 bg-bolt-elements-background-depth-2 rounded-lg shadow-lg text-center">
|
|
<div className="text-red-500">
|
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-16 w-16 mx-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
<h1 className="text-2xl font-bold mt-4">Authentication Failed</h1>
|
|
<p className="mt-2 text-bolt-content-secondary">{data.errorDescription}</p>
|
|
</div>
|
|
|
|
<p className="text-sm text-bolt-content-tertiary">
|
|
Redirecting you back in a moment...
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Loading state
|
|
return (
|
|
<div className="flex flex-col items-center justify-center min-h-screen bg-bolt-elements-background-depth-1">
|
|
<BackgroundRays />
|
|
|
|
<div className="w-full max-w-md p-8 space-y-8 bg-bolt-elements-background-depth-2 rounded-lg shadow-lg text-center">
|
|
<div className="text-bolt-content-primary">
|
|
<svg className="animate-spin h-16 w-16 mx-auto text-bolt-content-accent" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
|
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
</svg>
|
|
<h1 className="text-2xl font-bold mt-4">Authenticating...</h1>
|
|
<p className="mt-2 text-bolt-content-secondary">
|
|
Completing your sign-in with GitHub
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|