Skip to main content
GitHub Wrapped uses Better Auth with GitHub OAuth to authenticate users and access private repositories.

OAuth Configuration

The authentication system is configured in lib/auth.ts:
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  socialProviders: {
    github: {
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
      scope: ["repo", "read:user"],
    },
  },
});

Required Scopes

  • repo - Access to public and private repositories
  • read:user - Read user profile information

Authentication Flow

1. Initiate OAuth

Redirect users to the GitHub OAuth URL:
GET /api/auth/signin/github
This endpoint is automatically handled by Better Auth and redirects to GitHub’s OAuth page.

2. OAuth Callback

After user authorization, GitHub redirects back to:
GET /api/auth/callback/github
Better Auth handles the callback and establishes a session.

3. Session Management

Sessions are stored in cookies with the following configuration:
  • Duration: 7 days
  • Strategy: JWE (JSON Web Encryption)
  • Refresh: Automatic stateless refresh

Using Authentication in Requests

Once authenticated, API requests automatically use the session cookie:
curl "https://your-domain.com/api/wrapped?owner=myorg&repo=private-repo" \
  -H "Cookie: better-auth.session_token=..."

Access Token Parameter

For programmatic access, you can pass an access token directly:
curl "https://your-domain.com/api/wrapped?owner=myorg&repo=private-repo&accessToken=ghp_xxxxx"
Never expose your GitHub personal access token in client-side code or public repositories.

Authenticated Endpoints

Some endpoints require authentication:

Required Authentication

  • GET /api/performance - User performance metrics
  • GET /api/summary - Monthly user summary
  • GET /api/user/repos - List user repositories

Optional Authentication

  • GET /api/wrapped - Repository wrapped (required for private repos)
  • GET /api/wrapped/user - User wrapped (required for private activity)
  • GET /api/validate - Repository validation (required for private repos)

Getting the Current Session

To check if a user is authenticated:
import { auth } from "@/lib/auth";
import { headers } from "next/headers";

const session = await auth.api.getSession({
  headers: await headers(),
});

if (session?.user) {
  // User is authenticated
  console.log(session.user);
}

Getting the Access Token

To retrieve the GitHub access token for API calls:
import { auth } from "@/lib/auth";
import { headers } from "next/headers";

const tokenData = await auth.api.getAccessToken({
  headers: await headers(),
  body: { providerId: "github" },
});

const accessToken = tokenData?.accessToken;

Error Responses

401 Unauthorized

Returned when authentication is required but not provided:
{
  "error": "Authentication required"
}

403 Forbidden

Returned when the GitHub token lacks necessary permissions:
{
  "error": "GitHub denied access to this data. The user or repos may be private, or the token has insufficient scopes."
}

Client Setup

For Next.js applications, use the Better Auth React client:
import { createAuthClient } from "better-auth/react";

export const authClient = createAuthClient({
  baseURL: "http://localhost:3000",
});
Then use the provided hooks:
import { authClient } from "@/lib/auth-client";

function LoginButton() {
  const { signIn } = authClient.useSession();
  
  return (
    <button onClick={() => signIn.social({ provider: "github" })}>
      Sign in with GitHub
    </button>
  );
}

Environment Variables

Configure the following environment variables:
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret
BETTER_AUTH_SECRET=your_random_secret_key
BETTER_AUTH_URL=https://your-domain.com

Next Steps

Repository Wrapped

Use authentication to access private repositories

User Performance

Authenticated endpoint for user metrics

Build docs developers (and LLMs) love