Skip to content

OAuth Authentication

Overview

OAuth authentication in ClawPlate is seamlessly integrated with Supabase Auth, providing secure and easy-to-implement social login functionality. The OAuthButtons component handles all the UI interactions while Supabase manages the authentication flow behind the scenes.

How It Works

1. Supabase Integration

ClawPlate uses Supabase Auth as the authentication backend, which provides:

  • Built-in OAuth providers (GitHub, Google, Apple, X/Twitter)
  • Secure token management with automatic refresh
  • User session handling across your application
  • Database integration for user profiles and metadata

2. OAuthButtons Component

The OAuthButtons component provides:

  • Provider-specific styling with brand colors
  • Loading states during authentication
  • Error handling with user feedback
  • Responsive design for all devices

Setup Guide

Step 1: Configure Supabase

First, enable OAuth providers in your Supabase dashboard:

  1. Go to Authentication > Providers in your Supabase dashboard
  2. Enable the providers you want to use:
    • GitHub: Requires GitHub OAuth App
    • Google: Requires Google Cloud Console setup
    • Apple: Requires Apple Developer account
    • X (Twitter): Requires X Developer account

Step 2: Environment Variables

Add your OAuth credentials to your Supabase dashboard

Step 3: Use OAuthButtons Component

Simply import and use the component in your authentication pages:

vue
<!-- OAuth automatique avec Supabase -->
<OAuthButton provider="github" @error="handleError" />
<OAuthButton provider="google" @error="handleError" />
<OAuthButton provider="apple" @error="handleError" />
<OAuthButton provider="x" @error="handleError" />

<!-- With Custom Methods -->
<OAuthButton 
  provider="github" 
  :onClick="customSignIn" 
  @error="handleError" 
/>

<!-- Manage your errors -->
<OAuthButton 
  provider="google" 
  @error="(message) => showToast({ title: 'Error', description: message, variant: 'error' })" 
/>

Supported Providers

GitHub

  • Setup: Create a GitHub OAuth App
  • Scopes: user:email, read:user
  • User Data: Username, email, avatar, profile URL

Google

  • Setup: Configure Google Cloud Console
  • Scopes: email, profile
  • User Data: Name, email, profile picture, locale

Apple

  • Setup: Apple Developer account required
  • Scopes: name, email
  • User Data: Name, email (if provided by user)

X (Twitter)

  • Setup: X Developer account required
  • Scopes: tweet.read, users.read
  • User Data: Username, display name, profile picture

Authentication Flow

1. User Clicks OAuth Button

vue
<OAuthButtons 
  provider="github" 
  @click="handleGitHubAuth"
/>

2. Supabase Redirects to Provider

typescript
const { error } = await supabase.auth.signInWithOAuth({
  provider: 'github',
  options: {
    redirectTo: `${window.location.origin}/auth/callback`
  }
})

3. User Authorizes on Provider Site

  • User is redirected to GitHub/Google/etc.
  • User grants permissions to your app
  • Provider redirects back to your callback URL

4. Supabase Handles the Callback

vue
<!-- pages/auth/callback.vue -->
<template>
  <div>Processing authentication...</div>
</template>

<script setup>
const supabase = useSupabaseClient()

onMounted(async () => {
  const { data, error } = await supabase.auth.getSession()
  
  if (error) {
    console.error('Auth error:', error)
    await navigateTo('/login?error=auth_failed')
  } else if (data.session) {
    await navigateTo('/dashboard')
  }
})
</script>

5. User Session is Created

  • Supabase creates a user session
  • User data is stored in your database
  • User is redirected to your app

User Data Structure

After successful authentication, Supabase provides user data in this format:

typescript
interface User {
  id: string
  email: string
  user_metadata: {
    full_name?: string
    avatar_url?: string
    provider_id?: string
    provider?: string
  }
  app_metadata: {
    provider?: string
    providers?: string[]
  }
}

Advanced Configuration

Custom Redirect URLs

typescript
const { error } = await supabase.auth.signInWithOAuth({
  provider: 'github',
  options: {
    redirectTo: `${window.location.origin}/auth/callback`,
    queryParams: {
      access_type: 'offline',
      prompt: 'consent'
    }
  }
})

Scopes Configuration

Configure additional scopes in your Supabase dashboard:

  1. Go to Authentication > Providers
  2. Select your provider
  3. Add custom scopes in the Additional Scopes field

User Profile Updates

typescript
// Update user profile after authentication
const updateUserProfile = async (userData) => {
  const { error } = await supabase.auth.updateUser({
    data: {
      full_name: userData.full_name,
      avatar_url: userData.avatar_url
    }
  })
  
  if (error) {
    console.error('Profile update error:', error)
  }
}

Error Handling

Common Errors

typescript
const handleAuthError = (error) => {
  switch (error.message) {
    case 'Invalid login credentials':
      // Handle invalid credentials
      break
    case 'Email not confirmed':
      // Handle unconfirmed email
      break
    case 'Too many requests':
      // Handle rate limiting
      break
    default:
      // Handle generic errors
      console.error('Authentication error:', error)
  }
}

User Feedback

vue
<template>
  <div class="auth-container">
    <OAuthButtons 
      provider="github" 
      :loading="isLoading"
      @click="signInWithGitHub"
    />
    
    <div v-if="error" class="error-message">
      {{ error }}
    </div>
  </div>
</template>

<script setup>
const isLoading = ref(false)
const error = ref('')

const signInWithGitHub = async () => {
  isLoading.value = true
  error.value = ''
  
  try {
    const { error: authError } = await supabase.auth.signInWithOAuth({
      provider: 'github',
      options: {
        redirectTo: `${window.location.origin}/auth/callback`
      }
    })
    
    if (authError) {
      error.value = 'Authentication failed. Please try again.'
    }
  } catch (err) {
    error.value = 'An unexpected error occurred.'
  } finally {
    isLoading.value = false
  }
}
</script>

Security Considerations

1. HTTPS Required

OAuth providers require HTTPS in production. Ensure your domain uses SSL certificates.

2. Redirect URL Validation

Always validate redirect URLs in your Supabase dashboard to prevent open redirect attacks.

3. State Parameter

Supabase automatically handles the state parameter for CSRF protection.

4. Token Storage

Supabase securely stores tokens in HTTP-only cookies, preventing XSS attacks.

Testing

Development Testing

typescript
// Test OAuth flow in development
const testOAuth = async () => {
  const { data, error } = await supabase.auth.signInWithOAuth({
    provider: 'github',
    options: {
      redirectTo: 'http://localhost:3000/auth/callback'
    }
  })
  
  console.log('OAuth test result:', { data, error })
}

Production Checklist

  • [ ] HTTPS enabled
  • [ ] Redirect URLs configured
  • [ ] OAuth apps created for all providers
  • [ ] Environment variables set
  • [ ] Error handling implemented
  • [ ] User feedback provided

Troubleshooting

Common Issues

OAuth button not working

  • Check if provider is enabled in Supabase
  • Verify environment variables
  • Ensure redirect URLs are configured

User not created in database

  • Check if user is created in Supabase Auth
  • Verify database policies
  • Check for RLS (Row Level Security) issues

Redirect loop

  • Verify callback URL configuration
  • Check for infinite redirects in callback handler
  • Ensure proper error handling

Debug Mode

Enable debug mode in your Supabase client:

typescript
const supabase = createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_ANON_KEY,
  {
    auth: {
      debug: true
    }
  }
)

Best Practices

  1. Always handle errors gracefully with user feedback
  2. Use loading states to improve user experience
  3. Validate redirect URLs for security
  4. Test thoroughly in development before production
  5. Monitor authentication metrics in Supabase dashboard
  6. Keep tokens secure by letting Supabase handle storage
  7. Provide fallback options for users who prefer email/password

Example Implementation

Here's a complete authentication page example:

vue
<template>
  <div class="min-h-screen flex items-center justify-center bg-gray-50">
    <div class="max-w-md w-full space-y-8">
      <div>
        <h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900">
          Sign in to your account
        </h2>
        <p class="mt-2 text-center text-sm text-gray-600">
          Choose your preferred authentication method
        </p>
      </div>
      
      <div class="space-y-4">
        <OAuthButtons 
          provider="github" 
          size="lg"
          :loading="isGitHubLoading"
          @click="signInWithGitHub"
        />
        
        <OAuthButtons 
          provider="google" 
          size="lg"
          :loading="isGoogleLoading"
          @click="signInWithGoogle"
        />
        
        <OAuthButtons 
          provider="apple" 
          size="lg"
          :loading="isAppleLoading"
          @click="signInWithApple"
        />
      </div>
      
      <div v-if="error" class="bg-red-50 border border-red-200 rounded-md p-4">
        <p class="text-sm text-red-600">{{ error }}</p>
      </div>
      
      <div class="text-center">
        <p class="text-sm text-gray-600">
          By continuing, you agree to our Terms of Service and Privacy Policy
        </p>
      </div>
    </div>
  </div>
</template>

<script setup>
const supabase = useSupabaseClient()
const router = useRouter()

const isGitHubLoading = ref(false)
const isGoogleLoading = ref(false)
const isAppleLoading = ref(false)
const error = ref('')

const signInWithProvider = async (provider) => {
  const loadingRef = {
    github: isGitHubLoading,
    google: isGoogleLoading,
    apple: isAppleLoading
  }[provider]
  
  if (loadingRef) {
    loadingRef.value = true
  }
  
  error.value = ''
  
  try {
    const { error: authError } = await supabase.auth.signInWithOAuth({
      provider: provider,
      options: {
        redirectTo: `${window.location.origin}/auth/callback`
      }
    })
    
    if (authError) {
      error.value = 'Authentication failed. Please try again.'
    }
  } catch (err) {
    error.value = 'An unexpected error occurred.'
  } finally {
    if (loadingRef) {
      loadingRef.value = false
    }
  }
}

const signInWithGitHub = () => signInWithProvider('github')
const signInWithGoogle = () => signInWithProvider('google')
const signInWithApple = () => signInWithProvider('apple')
</script>

OAuth authentication in ClawPlate is powered by Supabase Auth and the OAuthButtons component, providing a secure and user-friendly authentication experience.

Built with love by mhdevfr