Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions GITHUB_AUTH_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# GitHub OAuth Implementation

## Changes Made

### Files Created:
1. `lib/auth.ts` - Server-side Better Auth configuration with GitHub provider
2. `app/api/auth/[...all]/route.ts` - Next.js API route handler
3. `.env.example` - Environment variables template

### Files Modified:
1. `lib/auth-client.ts` - Added exported sign-in methods
2. `components/login/sign-in.tsx` - Wired GitHub button with OAuth flow

## Setup Instructions

### 1. Create GitHub OAuth App

1. Go to: https://github.com/settings/developers
2. Click "New OAuth App"
3. Fill in:
- Application name: `Boundless Finance (Dev)`
- Homepage URL: `http://localhost:3000`
- Authorization callback URL: `http://localhost:3000/api/auth/callback/github`
4. Click "Register application"
5. Copy the **Client ID**
6. Click "Generate a new client secret" and copy the **Client Secret**

### 2. Set Environment Variables

Create `.env.local`:
```bash
NEXT_PUBLIC_APP_URL=http://localhost:3000
AUTH_SECRET=your-random-secret-key
GITHUB_CLIENT_ID=your_client_id_from_github
GITHUB_CLIENT_SECRET=your_client_secret_from_github
```

### 3. Install Dependencies (if needed)
```bash
npm install
```

### 4. Run Development Server
```bash
npm run dev
```

### 5. Test Authentication

1. Navigate to: http://localhost:3000/auth
2. Click "Sign in with Github"
3. Authorize the application
4. Should redirect back and create a session

## How It Works

1. User clicks "Sign in with Github" button
2. `authClient.signIn.social()` redirects to GitHub OAuth page
3. User authorizes the app
4. GitHub redirects to `/api/auth/callback/github`
5. Better Auth handles the callback and creates a session
6. User is redirected to the home page (`/`)

## Production Deployment

For production (staging-api.boundlessfi.xyz):

1. Create a new GitHub OAuth App for production
2. Set callback URL to: `https://staging-api.boundlessfi.xyz/api/auth/callback/github`
3. Update environment variables:
```bash
NEXT_PUBLIC_APP_URL=https://staging-api.boundlessfi.xyz
AUTH_SECRET=<generate-secure-random-string>
GITHUB_CLIENT_ID=<production-client-id>
GITHUB_CLIENT_SECRET=<production-client-secret>
```

## Files Changed

- ✅ `lib/auth.ts` (NEW)
- ✅ `lib/auth-client.ts` (MODIFIED)
- ✅ `app/api/auth/[...all]/route.ts` (NEW)
- ✅ `components/login/sign-in.tsx` (MODIFIED)
- ✅ `.env.example` (NEW)

## Testing Checklist

- [ ] GitHub button triggers OAuth flow
- [ ] Redirects to GitHub authorization page
- [ ] After authorization, returns to app
- [ ] Session is created
- [ ] User info is available
- [ ] Sign out works

## Notes

- Using in-memory sessions (no database required)
- Sessions expire after 7 days
- Easy to swap credentials for production
- Follows Better Auth best practices
4 changes: 4 additions & 0 deletions app/api/auth/[...all]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { auth } from "@/lib/auth"

export const GET = auth.handler
export const POST = auth.handler
48 changes: 34 additions & 14 deletions components/login/sign-in.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Label } from "@/components/ui/label";
import { Checkbox } from "@/components/ui/checkbox";
import { useState } from "react";
import { Loader2, Key } from "lucide-react";
// import { signIn } from "@/lib/auth-client";
import { authClient } from "@/lib/auth-client";
import Link from "next/link";
import { cn } from "@/lib/utils";

Expand All @@ -23,12 +23,27 @@ export default function SignIn() {
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);

const handleGitHubSignIn = async () => {
setLoading(true);
try {
await authClient.signIn.social({
provider: "github",
callbackURL: "/", // Redirect to home after sign-in
});
} catch (error) {
console.error("GitHub sign-in error:", error);
// Error will be handled by Better Auth
} finally {
setLoading(false);
}
};

return (
<Card className="max-w-md w-full px-6">
<CardHeader>
<CardTitle className="text-lg md:text-xl">Sign In</CardTitle>
<CardDescription className="text-xs md:text-sm">
Enter your email below to login to your account
Sign in with your GitHub account to continue
</CardDescription>
</CardHeader>
<CardContent>
Expand All @@ -43,19 +58,24 @@ export default function SignIn() {
variant="outline"
className={cn("w-full gap-2 text-white")}
disabled={loading}
onClick={handleGitHubSignIn}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M12 2A10 10 0 0 0 2 12c0 4.42 2.87 8.17 6.84 9.5c.5.08.66-.23.66-.5v-1.69c-2.77.6-3.36-1.34-3.36-1.34c-.46-1.16-1.11-1.47-1.11-1.47c-.91-.62.07-.6.07-.6c1 .07 1.53 1.03 1.53 1.03c.87 1.52 2.34 1.07 2.91.83c.09-.65.35-1.09.63-1.34c-2.22-.25-4.55-1.11-4.55-4.92c0-1.11.38-2 1.03-2.71c-.1-.25-.45-1.29.1-2.64c0 0 .84-.27 2.75 1.02c.79-.22 1.65-.33 2.5-.33s1.71.11 2.5.33c1.91-1.29 2.75-1.02 2.75-1.02c.55 1.35.2 2.39.1 2.64c.65.71 1.03 1.6 1.03 2.71c0 3.82-2.34 4.66-4.57 4.91c.36.31.69.92.69 1.85V21c0 .27.16.59.67.5C19.14 20.16 22 16.42 22 12A10 10 0 0 0 12 2"
></path>
</svg>
Sign in with Github
{loading ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M12 2A10 10 0 0 0 2 12c0 4.42 2.87 8.17 6.84 9.5c.5.08.66-.23.66-.5v-1.69c-2.77.6-3.36-1.34-3.36-1.34c-.46-1.16-1.11-1.47-1.11-1.47c-.91-.62.07-.6.07-.6c1 .07 1.53 1.03 1.53 1.03c.87 1.52 2.34 1.07 2.91.83c.09-.65.35-1.09.63-1.34c-2.22-.25-4.55-1.11-4.55-4.92c0-1.11.38-2 1.03-2.71c-.1-.25-.45-1.29.1-2.64c0 0 .84-.27 2.75 1.02c.79-.22 1.65-.33 2.5-.33s1.71.11 2.5.33c1.91-1.29 2.75-1.02 2.75-1.02c.55 1.35.2 2.39.1 2.64c.65.71 1.03 1.6 1.03 2.71c0 3.82-2.34 4.66-4.57 4.91c.36.31.69.92.69 1.85V21c0 .27.16.59.67.5C19.14 20.16 22 16.42 22 12A10 10 0 0 0 12 2"
></path>
</svg>
)}
{loading ? "Signing in..." : "Sign in with Github"}
</Button>
</div>
</div>
Expand Down
3 changes: 3 additions & 0 deletions lib/auth-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_APP_URL,
});

// Export auth methods for easy access
export const { signIn, signOut, useSession } = authClient;

const errorMessages: Record<string, string> = Object.fromEntries(
Object.keys(authClient.$ERROR_CODES).map((code) => [
code,
Expand Down
21 changes: 21 additions & 0 deletions lib/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { betterAuth } from "better-auth"

export const auth = betterAuth({
baseURL: process.env.NEXT_PUBLIC_APP_URL || "https://staging-api.boundlessfi.xyz",
secret: process.env.AUTH_SECRET || "your-secret-key-change-in-production",

socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID || "",
clientSecret: process.env.GITHUB_CLIENT_SECRET || "",
},
},

// Using in-memory session for now - can be replaced with database
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // 1 day
},
})

export type Session = typeof auth.$Infer.Session