Skip to main content

Overview

Add social authentication to your TanStack Start app with support for GitHub and Google OAuth. Built on Convex Auth with automatic user profile creation, session management, and protected routes.

Features

  • GitHub OAuth integration
  • Google OAuth integration
  • Automatic user profile creation
  • Profile pictures from OAuth providers
  • Session management
  • Protected routes
  • Loading states
  • Error handling
  • Type-safe

Installation

1

Install the component

npx shadcn@latest add https://convex-ui.vercel.app/r/social-auth-tanstack
2

Configure OAuth providers

Set up OAuth apps in provider dashboards:GitHub:
  1. Go to GitHub Settings → Developer settings → OAuth Apps
  2. Create new OAuth app
  3. Set callback URL: https://YOUR_CONVEX_URL/api/auth/callback/github
Google:
  1. Go to Google Cloud Console → APIs & Services → Credentials
  2. Create OAuth 2.0 Client ID
  3. Add authorized redirect URI: https://YOUR_CONVEX_URL/api/auth/callback/google
3

Add environment variables

In Convex dashboard (Settings → Environment Variables):
AUTH_GITHUB_ID=your_github_client_id
AUTH_GITHUB_SECRET=your_github_client_secret
AUTH_GOOGLE_ID=your_google_client_id
AUTH_GOOGLE_SECRET=your_google_client_secret
4

Start development

npx convex dev

What Gets Installed

Components

  • login-form.tsx - Social login buttons for GitHub and Google
  • logout-button.tsx - Sign out button

Routes

  • routes/auth/login.tsx - Login page with social options
  • routes/_protected/index.tsx - Example protected route

Backend (Convex)

  • convex/schema.ts - User schema with OAuth fields
  • convex/auth.ts - Auth query endpoints
  • convex/auth.config.ts - OAuth provider configuration
  • convex/users.ts - User profile queries
  • convex/http.ts - OAuth callback handlers

Environment Variables

Required (Convex Dashboard)

AUTH_GITHUB_ID
string
GitHub OAuth App Client ID
AUTH_GITHUB_SECRET
string
GitHub OAuth App Client Secret
AUTH_GOOGLE_ID
string
Google OAuth Client ID
AUTH_GOOGLE_SECRET
string
Google OAuth Client Secret
At least one OAuth provider (GitHub or Google) must be configured for authentication to work.

Usage

Basic Social Login

routes/auth/login.tsx
import { SocialLoginForm } from "@/components/login-form";

export default function LoginPage() {
  return (
    <div className="flex min-h-screen items-center justify-center">
      <SocialLoginForm />
    </div>
  );
}

Specify Providers

Choose which OAuth providers to display:
import { SocialLoginForm } from "@/components/login-form";

// GitHub only
<SocialLoginForm providers={["github"]} />

// Google only
<SocialLoginForm providers={["google"]} />

// Both (default)
<SocialLoginForm providers={["github", "google"]} />

Protected Routes

Check authentication before rendering protected content:
routes/_protected.tsx
import { createFileRoute, redirect, Outlet } from "@tanstack/react-router";
import { convexQuery, api } from "@/lib/convex/server";

export const Route = createFileRoute("/_protected")(
  beforeLoad: async ({ context }) => {
    const user = await context.queryClient.fetchQuery(
      convexQuery(api.users.current, {})
    );
    
    if (!user) {
      throw redirect({ to: "/auth/login" });
    }
  },
  component: ProtectedLayout,
});

function ProtectedLayout() {
  return <Outlet />;
}

Display User Info

Access authenticated user data:
import { useQuery } from "@tanstack/react-query";
import { convexQuery, api } from "@/lib/convex/server";

export function UserProfile() {
  const { data: user } = useQuery(
    convexQuery(api.users.current, {})
  );

  if (!user) return null;

  return (
    <div>
      <img src={user.image} alt={user.name} />
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}

Sign Out

import { LogoutButton } from "@/components/logout-button";

export function Header() {
  return (
    <header>
      <h1>My App</h1>
      <LogoutButton />
    </header>
  );
}

OAuth Flow

1

User Clicks Provider Button

User clicks “Continue with GitHub” or “Continue with Google”
2

Redirect to Provider

User is redirected to the OAuth provider’s authorization page
3

User Grants Permission

User authorizes your app to access their profile
4

Callback to Convex

Provider redirects to Convex with authorization code
5

Profile Creation

Convex creates or updates user profile with OAuth data
6

Session Created

User is signed in and redirected to the app

API Reference

SocialLoginForm Props

interface SocialLoginFormProps {
  providers?: Array<"github" | "google">;
}
providers
array
default:"[\"github\", \"google\"]"
Array of OAuth providers to display. Available options: "github", "google"
Example:
// Show only GitHub
<SocialLoginForm providers={["github"]} />

// Show only Google
<SocialLoginForm providers={["google"]} />

// Show both (default)
<SocialLoginForm />

Auth Actions

Use Convex Auth hooks for programmatic authentication:
import { useAuthActions } from "@convex-dev/auth/react";

function LoginButton() {
  const { signIn } = useAuthActions();

  return (
    <button onClick={() => signIn("github")}>
      Sign in with GitHub
    </button>
  );
}

Check Auth Status

import { Authenticated, Unauthenticated } from "convex/react";

function AuthStatus() {
  return (
    <>
      <Authenticated>
        <p>You are signed in</p>
      </Authenticated>
      <Unauthenticated>
        <p>You are signed out</p>
      </Unauthenticated>
    </>
  );
}

Backend Implementation

User Schema

OAuth providers populate these fields:
convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { authTables } from "@convex-dev/auth/server";
import { v } from "convex/values";

export default defineSchema({
  ...authTables,
  users: defineTable({
    name: v.optional(v.string()),
    image: v.optional(v.string()),
    email: v.optional(v.string()),
  }),
});

User Queries

Access user data securely:
convex/users.ts
import { query } from "./_generated/server";
import { getAuthUserId } from "@convex-dev/auth/server";

export const current = query({
  args: {},
  handler: async (ctx) => {
    const userId = await getAuthUserId(ctx);
    if (!userId) return null;
    return await ctx.db.get(userId);
  },
});

Customization

Custom Button Styles

Modify the login form component:
<Button
  variant="outline"
  size="lg"
  className="w-full bg-black text-white hover:bg-gray-800" // Custom styles
  onClick={() => handleSocialLogin("github")}
>
  <GitHubIcon className="mr-2 h-5 w-5" />
  Continue with GitHub
</Button>

Add More Providers

Convex Auth supports additional OAuth providers. Update convex/auth.config.ts:
import { AuthConfig } from "convex/server";

export default {
  providers: [
    {
      domain: process.env.CONVEX_SITE_URL!,
      applicationID: "convex",
    },
    // Add Twitter, Discord, etc.
  ],
} satisfies AuthConfig;

Custom Redirect

Change where users go after login:
const handleSocialLogin = async (provider: "github" | "google") => {
  await signIn(provider);
  navigate({ to: "/dashboard" }); // Custom destination
};

Getting OAuth Credentials

GitHub

1

Go to GitHub Settings

Navigate to Settings → Developer settings → OAuth Apps
2

Create New OAuth App

Click “New OAuth App”
3

Fill in Details

  • Application name: Your app name
  • Homepage URL: Your app URL
  • Callback URL: https://YOUR_CONVEX_URL/api/auth/callback/github
4

Get Credentials

Copy the Client ID and generate a Client Secret

Google

1

Go to Google Cloud Console

2

Create Project

Create a new project or select existing
3

Enable Google+ API

APIs & Services → Enable APIs and Services → Google+ API
4

Create OAuth Credentials

APIs & Services → Credentials → Create Credentials → OAuth 2.0 Client ID
5

Configure OAuth Consent

Fill in application name and support email
6

Add Redirect URI

https://YOUR_CONVEX_URL/api/auth/callback/google
7

Get Credentials

Copy the Client ID and Client Secret

Security

  • OAuth tokens are never exposed to the client
  • Callback URLs must be configured in provider dashboards
  • State parameter prevents CSRF attacks
  • Sessions are HTTP-only cookies
  • Tokens expire automatically

Troubleshooting

Ensure the callback URL in your OAuth provider settings exactly matches: https://YOUR_CONVEX_URL/api/auth/callback/{provider}
Make sure your app is wrapped with ConvexClientProvider.
Verify environment variables are set in the Convex dashboard, not your local .env file.
Check that your OAuth app has permissions to access user email and profile.

Build docs developers (and LLMs) love