All files / app/api/auth/github route.ts

73.77% Statements 45/61
50% Branches 4/8
100% Functions 1/1
73.77% Lines 45/61

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 821x         1x 1x   3x 3x   3x                     3x 3x 3x 3x 3x   3x 1x 1x 1x 1x 1x 1x 1x       2x 2x 2x 2x 2x 2x     3x     3x 3x 3x 3x 3x 3x 3x   3x     3x 3x 3x 3x 3x 3x 3x 3x   3x 3x                 3x  
import { NextResponse } from 'next/server';
import { createClient } from '@/lib/supabase/server';
import crypto from 'crypto';
 
// GitHub OAuth configuration
const GITHUB_CLIENT_ID = process.env.GITHUB_OAUTH_CLIENT_ID;
const GITHUB_AUTHORIZE_URL = 'https://github.com/login/oauth/authorize';
 
export async function GET() {
  try {
    // Check if GitHub OAuth is configured
    if (!GITHUB_CLIENT_ID) {
      console.error('GitHub OAuth not configured: GITHUB_OAUTH_CLIENT_ID missing');
      return NextResponse.redirect(
        new URL(
          '/developer?error=github_not_configured',
          process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'
        )
      );
    }
 
    // Verify user is authenticated
    const supabase = await createClient();
    const {
      data: { user },
      error: authError,
    } = await supabase.auth.getUser();
 
    if (authError || !user) {
      return NextResponse.redirect(
        new URL(
          '/login?redirect=/developer',
          process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'
        )
      );
    }
 
    // Generate state parameter to prevent CSRF attacks
    // Include user ID in state for verification in callback
    const stateData = {
      userId: user.id,
      nonce: crypto.randomBytes(16).toString('hex'),
      timestamp: Date.now(),
    };
    const state = Buffer.from(JSON.stringify(stateData)).toString('base64url');
 
    // Store state in a cookie for verification
    const redirectUri = `${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/auth/github/callback`;
 
    // Build GitHub OAuth URL
    const params = new URLSearchParams({
      client_id: GITHUB_CLIENT_ID,
      redirect_uri: redirectUri,
      scope: 'read:user user:email repo', // Read user info and repos
      state: state,
      allow_signup: 'false', // Don't allow GitHub signup through our app
    });
 
    const githubAuthUrl = `${GITHUB_AUTHORIZE_URL}?${params.toString()}`;
 
    // Create response with redirect and set state cookie
    const response = NextResponse.redirect(githubAuthUrl);
    response.cookies.set('github_oauth_state', state, {
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'lax',
      maxAge: 60 * 10, // 10 minutes
      path: '/',
    });
 
    return response;
  } catch (error) {
    console.error('GitHub OAuth error:', error);
    return NextResponse.redirect(
      new URL(
        '/developer?error=oauth_failed',
        process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'
      )
    );
  }
}