setup-oauth
Access: /template setup-oauth or /t setup-oauth
Configures OAuth authentication providers (Google, GitHub, etc.) in your MakerKit project with proper callback handling and user profile management.
What It Does
The setup-oauth command helps you:
- Configure OAuth providers in Supabase
- Set up callback handling
- Implement user profile synchronization
- Add provider-specific UI components
- Handle account linking for existing users
Usage
/template setup-oauth "Description"
# or
/t setup-oauth "Description"When prompted, specify:
- OAuth provider (Google, GitHub, Facebook, etc.)
- Scopes required
- Whether to allow account linking
- Profile data to sync
Prerequisites
- A MakerKit project with authentication configured
- OAuth app credentials from the provider
- Commercial MakerKit license from MakerKit
What Gets Created
1. Environment Configuration
Updates .env.local with provider credentials:
# Google OAuth
NEXT_PUBLIC_AUTH_PROVIDER_GOOGLE_ENABLED=true
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret
# GitHub OAuth
NEXT_PUBLIC_AUTH_PROVIDER_GITHUB_ENABLED=true
GITHUB_CLIENT_ID=your-client-id
GITHUB_CLIENT_SECRET=your-client-secret2. Supabase Configuration
SQL migrations for provider setup:
-- Enable OAuth provider
update auth.providers
set enabled = true
where provider = 'google';
-- Configure provider settings
update auth.providers
set settings = jsonb_build_object(
'client_id', 'your-client-id',
'client_secret', 'your-client-secret',
'authorized_redirect_uris', array['https://your-app.com/auth/callback']
)
where provider = 'google';3. Auth Components
Enhanced auth UI with OAuth buttons:
// components/auth/oauth-providers.tsx
import { Button } from '@kit/ui/button';
import { createBrowserClient } from '@supabase/ssr';
import { FcGoogle } from 'react-icons/fc';
import { FaGithub } from 'react-icons/fa';
export function OAuthProviders() {
const supabase = createBrowserClient();
const signInWithProvider = async (provider: 'google' | 'github') => {
const { error } = await supabase.auth.signInWithOAuth({
provider,
options: {
redirectTo: `${window.location.origin}/auth/callback`,
scopes: provider === 'github' ? 'read:user user:email' : undefined,
},
});
if (error) {
console.error('OAuth error:', error);
}
};
return (
<div className="space-y-2">
{process.env.NEXT_PUBLIC_AUTH_PROVIDER_GOOGLE_ENABLED === 'true' && (
<Button
variant="outline"
className="w-full"
onClick={() => signInWithProvider('google')}
>
<FcGoogle className="mr-2 h-4 w-4" />
Continue with Google
</Button>
)}
{process.env.NEXT_PUBLIC_AUTH_PROVIDER_GITHUB_ENABLED === 'true' && (
<Button
variant="outline"
className="w-full"
onClick={() => signInWithProvider('github')}
>
<FaGithub className="mr-2 h-4 w-4" />
Continue with GitHub
</Button>
)}
</div>
);
}4. Callback Handler
Processes OAuth callbacks:
// app/auth/callback/route.ts
import { NextResponse } from 'next/server';
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
import { cookies } from 'next/headers';
export async function GET(request: Request) {
const requestUrl = new URL(request.url);
const code = requestUrl.searchParams.get('code');
const next = requestUrl.searchParams.get('next') ?? '/dashboard';
if (code) {
const supabase = createRouteHandlerClient({ cookies });
const { error } = await supabase.auth.exchangeCodeForSession(code);
if (!error) {
// Sync user profile data
await syncUserProfile(supabase);
return NextResponse.redirect(new URL(next, requestUrl.origin));
}
}
// Handle error
return NextResponse.redirect(
new URL('/auth/error', requestUrl.origin)
);
}5. Profile Synchronization
Syncs OAuth provider data:
// lib/auth/sync-profile.ts
async function syncUserProfile(supabase: SupabaseClient) {
const { data: { user } } = await supabase.auth.getUser();
if (!user) return;
const profile = {
id: user.id,
email: user.email,
display_name: user.user_metadata.full_name ||
user.user_metadata.name ||
user.email?.split('@')[0],
picture_url: user.user_metadata.avatar_url ||
user.user_metadata.picture,
provider: user.app_metadata.provider,
provider_id: user.user_metadata.provider_id,
};
// Update user profile
await supabase
.from('users')
.upsert(profile, {
onConflict: 'id',
});
}Provider-Specific Setup
Google OAuth
Create OAuth 2.0 credentials at Google Cloud Console
Add authorized redirect URIs:
https://your-project.supabase.co/auth/v1/callbackhttp://localhost:54321/auth/v1/callback(for local dev)
Enable required APIs:
- Google+ API (for user profile)
- Gmail API (if email access needed)
GitHub OAuth
Create OAuth App at GitHub Settings
Set Authorization callback URL:
https://your-project.supabase.co/auth/v1/callback
Request appropriate scopes:
read:user- Read user profileuser:email- Access email addresses
Microsoft/Azure AD
- Register app in Azure Portal
- Configure redirect URIs
- Set up required permissions:
User.Read- Sign in and read user profileemail- View user's email address
Account Linking
Handle existing accounts with same email:
// Handle account linking in auth callback
const { data: existingUser } = await supabase
.from('users')
.select('id')
.eq('email', user.email)
.single();
if (existingUser && existingUser.id !== user.id) {
// Link accounts or handle conflict
await handleAccountLinking(existingUser.id, user.id);
}Custom OAuth Flows
Popup Authentication
export function useOAuthPopup() {
const signInWithPopup = async (provider: string) => {
const width = 500;
const height = 600;
const left = window.screenX + (window.outerWidth - width) / 2;
const top = window.screenY + (window.outerHeight - height) / 2;
const popup = window.open(
`/auth/oauth/${provider}`,
'oauth',
`width=${width},height=${height},left=${left},top=${top}`
);
// Listen for completion
window.addEventListener('message', (event) => {
if (event.data.type === 'oauth-success') {
popup?.close();
window.location.href = '/dashboard';
}
});
};
return { signInWithPopup };
}Mobile App Deep Linking
// Configure deep linking for mobile OAuth
const { error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: 'myapp://auth/callback',
skipBrowserRedirect: true,
},
});
// Handle the returned URL
if (data?.url) {
// Open in system browser or in-app browser
await Linking.openURL(data.url);
}Error Handling
Common OAuth Errors
// app/auth/error/page.tsx
export default function AuthError({
searchParams,
}: {
searchParams: { error?: string; error_description?: string };
}) {
const error = searchParams.error;
const description = searchParams.error_description;
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<Card className="w-full max-w-md">
<CardHeader>
<CardTitle>Authentication Error</CardTitle>
</CardHeader>
<CardContent>
{error === 'access_denied' && (
<p>You denied access to your account.</p>
)}
{error === 'invalid_request' && (
<p>The authentication request was invalid.</p>
)}
{description && (
<p className="text-sm text-muted-foreground mt-2">
{description}
</p>
)}
<Button asChild className="w-full mt-4">
<Link href="/auth/sign-in">Try Again</Link>
</Button>
</CardContent>
</Card>
</div>
);
}Testing OAuth
Local Development
# Use Supabase CLI for local OAuth testing
supabase start
# Configure OAuth providers in local config
supabase functions env set GOOGLE_CLIENT_ID=your-client-id
supabase functions env set GOOGLE_CLIENT_SECRET=your-client-secretIntegration Tests
describe('OAuth Authentication', () => {
it('redirects to Google OAuth', async () => {
const { getByText } = render(<OAuthProviders />);
const googleButton = getByText('Continue with Google');
fireEvent.click(googleButton);
await waitFor(() => {
expect(mockSupabase.auth.signInWithOAuth).toHaveBeenCalledWith({
provider: 'google',
options: expect.any(Object),
});
});
});
});Security Considerations
- State Parameter: Always use state parameter to prevent CSRF
- Nonce Validation: Validate nonce for OpenID Connect providers
- Scope Limitations: Only request necessary scopes
- Token Storage: Let Supabase handle token storage securely
- HTTPS Only: Never use OAuth over HTTP in production
License Requirement
Important: This command requires a commercial MakerKit license from https://makerkit.dev?atp=MqaGgc MakerKit is a premium SaaS starter kit and requires proper licensing for commercial use.
