GitHub OAuth

Implement GitHub sign-in for your application

GitHub OAuth allows users to sign in with their GitHub account. New users are automatically registered without email verification.

Prerequisites

Before implementing GitHub OAuth, complete the GitHub OAuth setup in the Environment Setup guide.

OAuth Flow

User clicks GitHub button

The login page includes a GitHub sign-in button that redirects to GitHub's authorization page.

const githubAuthUrl = `https://github.com/login/oauth/authorize?client_id=${clientId}&scope=user:email`;
window.location.href = githubAuthUrl;

User authorizes application

GitHub shows a consent screen. The user grants permission to access their profile and email.

GitHub redirects with code

GitHub redirects to /api/oauth/github?code=xxx with an authorization code.

Backend exchanges code for token

const tokenResponse = await fetch('https://github.com/login/oauth/access_token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
  body: JSON.stringify({
    client_id: process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID,
    client_secret: process.env.NEXT_PUBLIC_GITHUB_CLIENT_SECRET,
    code,
  }),
});

Fetch user data from GitHub

const userResponse = await fetch('https://api.github.com/user', {
  headers: { Authorization: `Bearer ${accessToken}` },
});

const emailsResponse = await fetch('https://api.github.com/user/emails', {
  headers: { Authorization: `Bearer ${accessToken}` },
});

Create or login user

The backend checks if a user exists with this email:

  • Existing GitHub user: Logs them in
  • New user: Creates account (auto-verified) and profile
  • Email exists with different auth: Returns error

Redirect with tokens

const redirectUrl = new URL('/oauth-callback', appUrl);
redirectUrl.searchParams.set('accessToken', accessToken);
redirectUrl.searchParams.set('refreshToken', refreshToken);
redirectUrl.searchParams.set('sessionId', session.sessionId);
return NextResponse.redirect(redirectUrl);

Callback Handler

The callback handler at src/app/api/oauth/github/route.ts processes the OAuth response.

User Data Mapping

GitHub FieldFastly Field
loginusername
name (split) or loginfirstName, lastName
Primary emailemail
avatar_urlavatar

Account Linking Rules

ScenarioResult
New emailCreate new account with authMethod: 'github'
Existing GitHub accountLogin to existing account
Email exists with password authError: use_email_login
Email exists with Google authError: use_google_login

Users cannot link multiple OAuth providers to the same email. Each email is tied to one authentication method.

Error Handling

The callback may redirect with error codes in the URL:

Error CodeDescription
oauth_cancelledUser clicked "Cancel" on GitHub consent screen
missing_codeNo authorization code in callback URL
no_email_accessUser's GitHub account has no accessible email
use_email_loginEmail registered with password authentication
use_google_loginEmail registered with Google OAuth
token_exchange_failedFailed to exchange code for access token
fetch_user_failedFailed to fetch user profile from GitHub

Frontend Error Handling

src/app/(auth)/oauth-callback/page.tsx
'use client';

import { useSearchParams, useRouter } from 'next/navigation';
import { useEffect } from 'react';
import { useAuth } from '@/providers/auth-provider';

export default function OAuthCallback() {
  const searchParams = useSearchParams();
  const router = useRouter();
  const { login } = useAuth();

  useEffect(() => {
    const error = searchParams.get('error');
    const accessToken = searchParams.get('accessToken');
    const refreshToken = searchParams.get('refreshToken');
    const sessionId = searchParams.get('sessionId');

    if (error) {
      // Handle error - show message and redirect to login
      router.push(`/log-in?error=${error}`);
      return;
    }

    if (accessToken && refreshToken) {
      // Store tokens and redirect to dashboard
      login(accessToken, refreshToken, null, { sessionId });
      router.push('/dashboard');
    }
  }, [searchParams]);

  return <div>Processing...</div>;
}

Frontend Implementation

GitHub Sign-In Button

src/app/(auth)/components/github-button.tsx
'use client';

import { Button } from '@/components/ui/button';
import { FaGithub } from 'react-icons/fa';

export function GitHubButton() {
  const handleClick = () => {
    const clientId = process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID;
    const redirectUri = `${window.location.origin}/api/oauth/github`;
    const scope = 'user:email';

    const url = new URL('https://github.com/login/oauth/authorize');
    url.searchParams.set('client_id', clientId);
    url.searchParams.set('redirect_uri', redirectUri);
    url.searchParams.set('scope', scope);

    window.location.href = url.toString();
  };

  return (
    <Button variant="outline" onClick={handleClick} className="w-full">
      <FaGithub className="mr-2 h-4 w-4" />
      Continue with GitHub
    </Button>
  );
}

Session Metadata

GitHub OAuth sessions include the following metadata:

Prop

Type

On this page