{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "social-auth-react-router", "type": "registry:block", "title": "Social Auth flow for React Router and Supabase", "description": "Social Auth flow for React Router and Supabase", "dependencies": [ "@supabase/ssr@latest", "@react-router/dev@latest", "@react-router/fs-routes@latest", "@supabase/supabase-js@latest" ], "registryDependencies": [ "button", "card" ], "files": [ { "path": "registry/default/blocks/social-auth-react-router/app/routes/auth.error.tsx", "content": "import { Card, CardContent, CardHeader, CardTitle } from '@/registry/default/components/ui/card'\nimport { useSearchParams } from 'react-router'\n\nexport default function Page() {\n let [searchParams] = useSearchParams()\n\n return (\n
\n
\n
\n \n \n Sorry, something went wrong.\n \n \n {searchParams?.get('error') ? (\n

\n Code error: {searchParams?.get('error')}\n

\n ) : (\n

An unspecified error occurred.

\n )}\n
\n
\n
\n
\n
\n )\n}\n", "type": "registry:file", "target": "app/routes/auth.error.tsx" }, { "path": "registry/default/blocks/social-auth-react-router/app/routes/auth.oauth.tsx", "content": "import { createClient } from '@/registry/default/clients/react-router/lib/supabase/server'\nimport { type LoaderFunctionArgs, redirect } from 'react-router'\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const requestUrl = new URL(request.url)\n const code = requestUrl.searchParams.get('code')\n const _next = requestUrl.searchParams.get('next')\n const next = _next?.startsWith('/') ? _next : '/'\n if (code) {\n const { supabase, headers } = createClient(request)\n\n const { error } = await supabase.auth.exchangeCodeForSession(code)\n if (!error) {\n return redirect(next, { headers })\n } else {\n return redirect(`/auth/error?error=${error?.message}`)\n }\n }\n // redirect the user to an error page with some instructions\n return redirect(`/auth/error`)\n}\n", "type": "registry:file", "target": "app/routes/auth.oauth.tsx" }, { "path": "registry/default/blocks/social-auth-react-router/app/routes/login.tsx", "content": "import { createClient } from '@/registry/default/clients/react-router/lib/supabase/server'\nimport { Button } from '@/registry/default/components/ui/button'\nimport {\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n} from '@/registry/default/components/ui/card'\nimport { type ActionFunctionArgs, redirect, useFetcher } from 'react-router'\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { supabase } = createClient(request)\n const origin = new URL(request.url).origin\n\n const { data, error } = await supabase.auth.signInWithOAuth({\n provider: 'github',\n options: {\n redirectTo: `${origin}/auth/oauth?next=/protected`,\n },\n })\n\n if (data.url) {\n return redirect(data.url)\n }\n\n if (error) {\n return {\n error: error instanceof Error ? error.message : 'An error occurred',\n }\n }\n}\n\nexport default function Login() {\n const fetcher = useFetcher()\n\n const error = fetcher.data?.error\n const loading = fetcher.state === 'submitting'\n\n return (\n
\n
\n
\n \n \n Welcome!\n Sign in to your account to continue\n \n \n \n
\n {error &&

{error}

}\n \n
\n
\n
\n
\n
\n
\n
\n )\n}\n", "type": "registry:file", "target": "app/routes/login.tsx" }, { "path": "registry/default/blocks/social-auth-react-router/app/routes/logout.tsx", "content": "import { createClient } from '@/registry/default/clients/react-router/lib/supabase/server'\nimport { type ActionFunctionArgs, redirect } from 'react-router'\n\nexport async function loader({ request }: ActionFunctionArgs) {\n const { supabase, headers } = createClient(request)\n\n const { error } = await supabase.auth.signOut()\n\n if (error) {\n console.error(error)\n return { success: false, error: error.message }\n }\n\n // Redirect to dashboard or home page after successful sign-in\n return redirect('/', { headers })\n}\n", "type": "registry:file", "target": "app/routes/logout.tsx" }, { "path": "registry/default/blocks/social-auth-react-router/app/routes/protected.tsx", "content": "import { createClient } from '@/registry/default/clients/react-router/lib/supabase/server'\nimport { Button } from '@/registry/default/components/ui/button'\nimport { type LoaderFunctionArgs, redirect, useLoaderData } from 'react-router'\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { supabase } = createClient(request)\n\n const { data, error } = await supabase.auth.getUser()\n if (error || !data?.user) {\n return redirect('/login')\n }\n\n return data\n}\n\nexport default function ProtectedPage() {\n let data = useLoaderData()\n\n return (\n
\n

\n Hello {data.user.email}\n

\n \n \n \n
\n )\n}\n", "type": "registry:file", "target": "app/routes/protected.tsx" }, { "path": "registry/default/blocks/social-auth-react-router/app/routes.ts", "content": "import { type RouteConfig } from '@react-router/dev/routes'\nimport { flatRoutes } from '@react-router/fs-routes'\n\nexport default flatRoutes() satisfies RouteConfig\n", "type": "registry:file", "target": "app/routes.ts" }, { "path": "registry/default/clients/react-router/lib/supabase/client.ts", "content": "/// \nimport { createBrowserClient } from '@supabase/ssr'\n\nexport function createClient() {\n return createBrowserClient(\n import.meta.env.VITE_SUPABASE_URL!,\n import.meta.env.VITE_SUPABASE_PUBLISHABLE_OR_ANON_KEY!\n )\n}\n", "type": "registry:lib" }, { "path": "registry/default/clients/react-router/lib/supabase/server.ts", "content": "import { createServerClient, parseCookieHeader, serializeCookieHeader } from '@supabase/ssr'\n\nexport function createClient(request: Request) {\n const headers = new Headers()\n\n const supabase = createServerClient(\n process.env.VITE_SUPABASE_URL!,\n process.env.VITE_SUPABASE_PUBLISHABLE_OR_ANON_KEY!,\n {\n cookies: {\n getAll() {\n return parseCookieHeader(request.headers.get('Cookie') ?? '') as {\n name: string\n value: string\n }[]\n },\n setAll(cookiesToSet) {\n cookiesToSet.forEach(({ name, value, options }) =>\n headers.append('Set-Cookie', serializeCookieHeader(name, value, options))\n )\n },\n },\n }\n )\n\n return { supabase, headers }\n}\n", "type": "registry:lib" } ], "envVars": { "VITE_SUPABASE_URL": "", "VITE_SUPABASE_PUBLISHABLE_OR_ANON_KEY": "" }, "docs": "You'll need to set the following environment variables in your project: `VITE_SUPABASE_URL` and `VITE_SUPABASE_PUBLISHABLE_OR_ANON_KEY`." }