diff --git a/GITHUB_AUTH_README.md b/GITHUB_AUTH_README.md new file mode 100644 index 0000000..c3ae2fb --- /dev/null +++ b/GITHUB_AUTH_README.md @@ -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= + GITHUB_CLIENT_ID= + GITHUB_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 diff --git a/app/api/auth/[...all]/route.ts b/app/api/auth/[...all]/route.ts new file mode 100644 index 0000000..1b96f4f --- /dev/null +++ b/app/api/auth/[...all]/route.ts @@ -0,0 +1,4 @@ +import { auth } from "@/lib/auth" + +export const GET = auth.handler +export const POST = auth.handler diff --git a/components/login/sign-in.tsx b/components/login/sign-in.tsx index 12147e4..432825e 100644 --- a/components/login/sign-in.tsx +++ b/components/login/sign-in.tsx @@ -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"; @@ -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 ( Sign In - Enter your email below to login to your account + Sign in with your GitHub account to continue @@ -43,19 +58,24 @@ export default function SignIn() { variant="outline" className={cn("w-full gap-2 text-white")} disabled={loading} + onClick={handleGitHubSignIn} > - - - - Sign in with Github + {loading ? ( + + ) : ( + + + + )} + {loading ? "Signing in..." : "Sign in with Github"} diff --git a/lib/auth-client.ts b/lib/auth-client.ts index ff3fb1b..c96e5c2 100644 --- a/lib/auth-client.ts +++ b/lib/auth-client.ts @@ -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 = Object.fromEntries( Object.keys(authClient.$ERROR_CODES).map((code) => [ code, diff --git a/lib/auth.ts b/lib/auth.ts new file mode 100644 index 0000000..ed21c39 --- /dev/null +++ b/lib/auth.ts @@ -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