OAuth Configuration for Auth Service
This guide explains how to configure OAuth providers (GitHub and Google) for the auth.do.dev unified authentication service.
Required Environment Variables
Add these to your Convex dashboard for the auth deployment (dependable-pika-747):
# Resend (Email OTP)
RESEND_API_KEY=re_xxxxxxxxxxxx
AUTH_RESEND_FROM=auth@notifications.do.dev
# GitHub OAuth
AUTH_GITHUB_ID=xxxxxxxxxxxx
AUTH_GITHUB_SECRET=xxxxxxxxxxxx
# Google OAuth
AUTH_GOOGLE_ID=xxxxxxxxxxxx.apps.googleusercontent.com
AUTH_GOOGLE_SECRET=xxxxxxxxxxxx
# JWT Secret (generate a random string)
JWT_SECRET=your-random-jwt-secret-string-hereGitHub OAuth Setup
- Go to GitHub Settings > Developer settings > OAuth Apps
- Click "New OAuth App"
- Fill in the application details:
- Application name: do.dev Auth
- Homepage URL: https://auth.do.dev (or http://localhost:3030 for dev)
- Authorization callback URL:
- Development:
http://localhost:3030/api/auth/callback/github - Production:
https://auth.do.dev/api/auth/callback/github
- Development:
- Click "Register application"
- Copy the Client ID and generate a new Client Secret
- Add to Convex environment variables:
AUTH_GITHUB_ID= Your Client IDAUTH_GITHUB_SECRET= Your Client Secret
Google OAuth Setup
- Go to Google Cloud Console
- Create a new project or select existing
- Enable the Google+ API
- Go to "Credentials" and click "Create Credentials" > "OAuth client ID"
- Configure the OAuth consent screen first if prompted
- For Application type, choose "Web application"
- Add authorized JavaScript origins:
- Development:
http://localhost:3030 - Production:
https://auth.do.dev
- Development:
- Add authorized redirect URIs:
- Development:
http://localhost:3030/api/auth/callback/google - Production:
https://auth.do.dev/api/auth/callback/google
- Development:
- Copy the Client ID and Client Secret
- Add to Convex environment variables:
AUTH_GOOGLE_ID= Your Client IDAUTH_GOOGLE_SECRET= Your Client Secret
Resend Email Setup
- Sign up for Resend
- Verify your domain (do.dev) if sending from custom domain
- Create an API key
- Add to Convex environment variables:
RESEND_API_KEY= Your API keyAUTH_RESEND_FROM= auth@notifications.do.dev
Testing Configuration
Development Testing
-
Start the auth service:
cd apps/auth pnpm dev --port 3030 -
Navigate to test URLs:
- GitHub:
http://localhost:3030/login?provider=github&app=dodev&return=http://localhost:3005/dashboard - Google:
http://localhost:3030/login?provider=google&app=dodev&return=http://localhost:3005/dashboard
- GitHub:
Production Testing
- Deploy auth service to
auth.do.dev - Test OAuth flows:
- GitHub:
https://auth.do.dev/login?provider=github&app=dodev&return=https://do.dev/dashboard - Google:
https://auth.do.dev/login?provider=google&app=dodev&return=https://do.dev/dashboard
- GitHub:
OAuth Flow Implementation
Route Handlers
// apps/auth/app/api/auth/oauth/github/route.ts
import { NextRequest } from "next/server";
import { api } from "@/convex/_generated/api";
import { fetchMutation } from "convex/nextjs";
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const app = searchParams.get("app") || "unknown";
const returnUrl = searchParams.get("return") || "/";
// Initiate OAuth state
const { state } = await fetchMutation(api.auth.initiateOAuth, {
provider: "github",
appId: app,
returnUrl,
});
// Redirect to GitHub OAuth
const githubUrl = new URL("https://github.com/login/oauth/authorize");
githubUrl.searchParams.set("client_id", process.env.AUTH_GITHUB_ID!);
githubUrl.searchParams.set("redirect_uri", `${process.env.NEXT_PUBLIC_URL}/api/auth/callback/github`);
githubUrl.searchParams.set("scope", "user:email");
githubUrl.searchParams.set("state", state);
return Response.redirect(githubUrl.toString());
}// apps/auth/app/api/auth/callback/github/route.ts
import { NextRequest } from "next/server";
import { api } from "@/convex/_generated/api";
import { fetchMutation, fetchQuery } from "convex/nextjs";
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const code = searchParams.get("code");
const state = searchParams.get("state");
if (!code || !state) {
return Response.redirect("/error?message=Invalid OAuth response");
}
// Validate state and get app info
const oauthState = await fetchQuery(api.auth.validateOAuthState, { state });
if (!oauthState) {
return Response.redirect("/error?message=Invalid OAuth state");
}
// Exchange code for token
const tokenResponse = await fetch("https://github.com/login/oauth/access_token", {
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
client_id: process.env.AUTH_GITHUB_ID,
client_secret: process.env.AUTH_GITHUB_SECRET,
code,
}),
});
const { access_token } = await tokenResponse.json();
// Get user info from GitHub
const userResponse = await fetch("https://api.github.com/user", {
headers: {
"Authorization": `Bearer ${access_token}`,
},
});
const userData = await userResponse.json();
// Create or update user and session
const session = await fetchMutation(api.auth.completeOAuth, {
provider: "github",
providerId: userData.id.toString(),
email: userData.email,
name: userData.name || userData.login,
image: userData.avatar_url,
appId: oauthState.appId,
returnUrl: oauthState.returnUrl,
});
// Redirect with session token
const redirectUrl = new URL(oauthState.returnUrl);
redirectUrl.searchParams.set("token", session.token);
return Response.redirect(redirectUrl.toString());
}Client Integration
// Client-side OAuth trigger
function handleOAuthLogin(provider: "github" | "google") {
const authUrl = new URL(`${AUTH_SERVICE_URL}/api/auth/oauth/${provider}`);
authUrl.searchParams.set("app", APP_ID);
authUrl.searchParams.set("return", window.location.href);
window.location.href = authUrl.toString();
}Security Considerations
Development
- Use localhost URLs for development
- Never commit OAuth secrets to version control
- Use different OAuth apps for development and production
Production
- Use HTTPS URLs only
- Set up proper CORS policies
- Implement rate limiting
- Monitor for unusual OAuth activity
- Regularly rotate OAuth secrets
Troubleshooting
Common Issues
"redirect_uri_mismatch" Error
- Check that OAuth app redirect URIs exactly match the URLs being used
- Ensure protocol (http/https) matches
- Verify port numbers in development
"invalid_client" Error
- Verify CLIENT_ID and CLIENT_SECRET are correct
- Ensure OAuth app is enabled
- Check that secrets are properly set in Convex environment
"access_denied" Error
- User cancelled OAuth flow
- Check OAuth app permissions/scopes
- Verify OAuth consent screen configuration
Debugging
Enable OAuth debugging by adding logs:
// In OAuth handlers
console.log("OAuth initiated:", { provider, app, returnUrl });
console.log("OAuth callback received:", { code: !!code, state });
console.log("User data received:", { email: userData.email, name: userData.name });View logs in:
- Convex dashboard for backend logs
- Browser network tab for client-side issues
- OAuth provider logs (GitHub/Google Cloud Console)
Migration from Existing Systems
From Clerk
- Export user data from Clerk
- Map OAuth provider IDs to new system
- Update OAuth app redirect URLs
- Test OAuth flows thoroughly
- Migrate users in batches
From Custom Auth
- Audit existing OAuth configurations
- Update redirect URLs to auth.do.dev
- Migrate user accounts with OAuth provider links
- Verify all OAuth flows work correctly
Last updated: January 2025