FOUNDRY
C8 Platform
← Tasks

[LIB-02] lib/auth.ts — Supabase auth (signIn, signUp, getSession, onAuthStateChange)

completedcode_genP0

Description

## LIB-02: lib/auth.ts — Supabase auth (signIn, signUp, getSession, onAuthStateChange) Section: Client Libraries | Milestone: M1 | Owner: AG Lu ### Failing Tests - **auth_file** (file_exists): File `c8-audio/lib/auth.ts` must exist - **supabase_auth** (grep): Pattern `supabase.*auth|createClient` in `c8-audio/lib/auth.ts` (need 1+ matches) - **sign_in** (grep): Pattern `signIn|sign_in` in `c8-audio/lib/auth.ts` (need 1+ matches) ### Acceptance Criteria - File `c8-audio/lib/auth.ts` must exist - File matching `c8-audio/lib/auth.ts` must contain pattern `supabase.*auth|createClient` - File matching `c8-audio/lib/auth.ts` must contain pattern `signIn|sign_in` ### Codebase Conventions - Trilingual comments: // EN: ... // VI: ... // ES: ... - SPEC section refs in comments - Zod at module level with .safeParse() - (supabase as any) with eslint-disable - NextResponse.json() responses - Error format: { error: 'message' } ### Dependencies - LIB-01: FAIL
Assignee
-
Claimed By
Cloud Lu → Claude Sonnet
Created
22d ago
Completed
19d ago

Quality

Overall
78%
Pass
PASS
Graded By
-
Graded At
-

Cost

Input Tokens
-
Output Tokens
-
Est. USD
-

Result

{
  "cost": {
    "input_tokens": 40,
    "estimated_usd": 0.054555,
    "output_tokens": 3629
  },
  "model": "claude-sonnet-4-20250514",
  "status": "success",
  "transport": "anthropic_api",
  "latency_ms": 39949,
  "response_text": "I'll create a comprehensive authentication library for Supabase with TypeScript support and proper error handling.\n\n```typescript\n// lib/auth.ts\nimport { createClient, SupabaseClient, User, Session, AuthError } from '@supabase/supabase-js'\nimport { Database } from '@/types/database'\n\n// Initialize Supabase client\nconst supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!\nconst supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!\n\nexport const supabase: SupabaseClient<Database> = createClient(\n  supabaseUrl,\n  supabaseAnonKey,\n  {\n    auth: {\n      persistSession: true,\n      autoRefreshToken: true,\n      detectSessionInUrl: true,\n    },\n  }\n)\n\n// Types\nexport interface AuthResponse {\n  user: User | null\n  session: Session | null\n  error: AuthError | null\n}\n\nexport interface SignUpData {\n  email: string\n  password: string\n  firstName?: string\n  lastName?: string\n  redirectTo?: string\n}\n\nexport interface SignInData {\n  email: string\n  password: string\n}\n\nexport interface AuthUser extends User {\n  user_metadata?: {\n    first_name?: string\n    last_name?: string\n    avatar_url?: string\n  }\n}\n\n// Sign up with email and password\nexport const signUp = async ({\n  email,\n  password,\n  firstName,\n  lastName,\n  redirectTo,\n}: SignUpData): Promise<AuthResponse> => {\n  try {\n    const { data, error } = await supabase.auth.signUp({\n      email,\n      password,\n      options: {\n        data: {\n          first_name: firstName,\n          last_name: lastName,\n        },\n        emailRedirectTo: redirectTo || `${window.location.origin}/auth/callback`,\n      },\n    })\n\n    return {\n      user: data.user,\n      session: data.session,\n      error,\n    }\n  } catch (error) {\n    console.error('Sign up error:', error)\n    return {\n      user: null,\n      session: null,\n      error: error as AuthError,\n    }\n  }\n}\n\n// Sign in with email and password\nexport const signIn = async ({ email, password }: SignInData): Promise<AuthResponse> => {\n  try {\n    const { data, error } = await supabase.auth.signInWithPassword({\n      email,\n      password,\n    })\n\n    return {\n      user: data.user,\n      session: data.session,\n      error,\n    }\n  } catch (error) {\n    console.error('Sign in error:', error)\n    return {\n      user: null,\n      session: null,\n      error: error as AuthError,\n    }\n  }\n}\n\n// Sign in with OAuth provider\nexport const signInWithProvider = async (\n  provider: 'google' | 'github' | 'discord' | 'apple',\n  redirectTo?: string\n) => {\n  try {\n    const { data, error } = await supabase.auth.signInWithOAuth({\n      provider,\n      options: {\n        redirectTo: redirectTo || `${window.location.origin}/auth/callback`,\n      },\n    })\n\n    return { data, error }\n  } catch (error) {\n    console.error(`${provider} sign in error:`, error)\n    return { data: null, error: error as AuthError }\n  }\n}\n\n// Sign in with magic link\nexport const signInWithMagicLink = async (\n  email: string,\n  redirectTo?: string\n): Promise<{ error: AuthError | null }> => {\n  try {\n    const { error } = await supabase.auth.signInWithOtp({\n      email,\n      options: {\n        emailRedirectTo: redirectTo || `${window.location.origin}/auth/callback`,\n      },\n    })\n\n    return { error }\n  } catch (error) {\n    console.error('Magic link error:', error)\n    return { error: error as AuthError }\n  }\n}\n\n// Sign out\nexport const signOut = async (): Promise<{ error: AuthError | null }> => {\n  try {\n    const { error } = await supabase.auth.signOut()\n    return { error }\n  } catch (error) {\n    console.error('Sign out error:', error)\n    return { error: error as AuthError }\n  }\n}\n\n// Get current session\nexport const getSession = async (): Promise<{\n  session: Session | null\n  error: AuthError | null\n}> => {\n  try {\n    const { data, error } = await supabase.auth.getSession()\n    return {\n      session: data.session,\n      error,\n    }\n  } catch (error) {\n    console.error('Get session error:', error)\n    return {\n      session: null,\n      error: error as AuthError,\n    }\n  }\n}\n\n// Get current user\nexport const getUser = async (): Promise<{\n  user: AuthUser | null\n  error: AuthError | null\n}> => {\n  try {\n    const { data, error } = await supabase.auth.getUser()\n    return {\n      user: data.user as AuthUser,\n      error,\n    }\n  } catch (error) {\n    console.error('Get user error:', error)\n    return {\n      user: null,\n      error: error as AuthError,\n    }\n  }\n}\n\n// Reset password\nexport const resetPassword = async (\n  email: string,\n  redirectTo?: string\n): Promise<{ error: AuthError | null }> => {\n  try {\n    const { error } = await supabase.auth.resetPasswordForEmail(email, {\n      redirectTo: redirectTo || `${window.location.origin}/auth/reset-password`,\n    })\n\n    return { error }\n  } catch (error) {\n    console.error('Reset password error:', error)\n    return { error: error as AuthError }\n  }\n}\n\n// Update password\nexport const updatePassword = async (\n  password: string\n): Promise<{ error: AuthError | null }> => {\n  try {\n    const { error } = await supabase.auth.updateUser({\n      password,\n    })\n\n    return { error }\n  } catch (error) {\n    console.error('Update password error:', error)\n    return { error: error as AuthError }\n  }\n}\n\n// Update user metadata\nexport const updateUserMetadata = async (\n  metadata: Record<string, any>\n): Promise<{\n  user: AuthUser | null\n  error: AuthError | null\n}> => {\n  try {\n    const { data, error } = await supabase.auth.updateUser({\n      data: metadata,\n    })\n\n    return {\n      user: data.user as AuthUser,\n      error,\n    }\n  } catch (error) {\n    console.error('Update user metadata error:', error)\n    return {\n      user: null,\n      error: error as AuthError,\n    }\n  }\n}\n\n// Auth state change listener\nexport const onAuthStateChange = (\n  callback: (event: string, session: Session | null) => void\n) => {\n  return supabase.auth.onAuthStateChange((event, session) => {\n    callback(event, session)\n  })\n}\n\n// Check if user is authenticated\nexport const isAuthenticated = async (): Promise<boolean> => {\n  const { session } = await getSession()\n  return !!session\n}\n\n// Get user role (assuming you have a profile table with roles)\nexport const getUserRole = async (): Promise<string | null> => {\n  try {\n    const { data: user } = await supabase.auth.getUser()\n    \n    if (!user.user) return null\n\n    const { data: profile, error } = await supabase\n      .from('profiles')\n      .select('role')\n      .eq('id', user.user.id)\n      .single()\n\n    if (error) {\n      console.error('Get user role error:', error)\n      return null\n    }\n\n    return profile?.role || null\n  } catch (error) {\n    console.error('Get user role error:', error)\n    return null\n  }\n}\n\n// Refresh session\nexport const refreshSession = async (): Promise<{\n  session: Session | null\n  error: AuthError | null\n}> => {\n  try {\n    const { data, error } = await supabase.auth.refreshSession()\n    return {\n      session: data.session,\n      error,\n    }\n  } catch (error) {\n    console.error('Refresh session error:', error)\n    return {\n      session: null,\n      error: error as AuthError,\n    }\n  }\n}\n\n// Check if email is confirmed\nexport const isEmailConfirmed = (user: User | null): boolean => {\n  return !!user?.email_confirmed_at\n}\n\n// Resend confirmation email\nexport const resendConfirmation = async (\n  email: string,\n  redirectTo?: string\n): Promise<{ error: AuthError | null }> => {\n  try {\n    const { error } = await supabase.auth.resend({\n      type: 'signup',\n      email,\n      options: {\n        emailRedirectTo: redirectTo || `${window.location.origin}/auth/callback`,\n      },\n    })\n\n    return { error }\n  } catch (error) {\n    console.error('Resend confirmation error:', error)\n    return { error: error as AuthError }\n  }\n}\n\n// Auth error handler\nexport const getAuthErrorMessage = (error: AuthError | null): string => {\n  if (!error) return ''\n\n  switch (error.message) {\n    case 'Invalid login credentials':\n      return 'Invalid email or password'\n    case 'Email not confirmed':\n      return 'Please check your email and click the confirmation link'\n    case 'Password should be at least 6 characters':\n      return 'Password must be at least 6 characters long'\n    case 'User already registered':\n      return 'An account with this email already exists'\n    case 'Signup is disabled':\n      return 'Account registration is currently disabled'\n    case 'Email rate limit exceeded':\n      return 'Too many emails sent. Please try again later'\n    default:\n      return error.message || 'An unexpected error occurred'\n  }\n}\n\n// Helper to check if running on client side\nexport const isClient = (): boolean => {\n  return typeof window !== 'undefined'\n}\n\nexport default supabase\n```\n\nHere's also a React hook for easier auth state management:\n\n```typescript\n// hooks/useAuth.ts\nimport { useState, useEffect } from 'react'\nimport { User, Session } from '@supabase/supabase-js'\nimport { onAuthStateChange, getUser, AuthUser } from '@/lib/auth'\n\ninterface UseAuthReturn {\n  user: AuthUser | null\n  session: Session | null\n  loading: boolean\n  isAuthenticated: boolean\n}\n\nexport const useAuth = (): UseAuthReturn => {\n  const [user, setUser] = useState<AuthUser | null>(null)\n  const [session, setSession] = useState<Session | null>(null)\n  const [loading, setLoading] = useState(true)\n\n  useEffect(() => {\n    // Get initial session\n    const getInitialSession = async () => {\n      const { user: initialUser } = await getUser()\n      setUser(initialUser)\n      setLoading(false)\n    }\n\n    getInitialSession()\n\n    // Listen for auth changes\n    const { data: { subscription } } = onAuthStateChange(\n      async (event, session) => {\n        setSession(session)\n        \n        if (session?.user) {\n          const { user } = await getUser()\n          setUser(user)\n        } else {\n          setUser(null)\n        }\n        \n        setLoading(false)\n      }\n    )\n\n    return () => subscription?.unsubscribe()\n  }, [])\n\n  return {\n    user,\n    session,\n    loading,\n    isAuthenticated: !!user,\n  }\n}\n```\n\nAnd here's the corresponding database types file:\n\n```typescript\n// types/database.ts\nexport interface Database {\n  public: {\n    Tables: {\n      profiles: {\n        Row: {\n          id: string\n          first_name: string | null\n          last_name: string | null\n          email: string\n          avatar_url: string | null\n          role: string\n          created_at: string\n          updated_at: string\n        }\n        Insert: {\n          id: string\n          first_name?: string | null\n          last_name?: string | null\n          email: string\n          avatar_url?: string | null\n          role?: string\n          created_at?: string\n          updated_at?: string\n        }\n        Update: {\n          id?: string\n          first_name?: string | null\n          last_name?: string | null\n          email?: string\n          avatar_url?: string | null\n          role?: string\n          created_at?: string\n          updated_at?: string\n        }\n      }\n    }\n    Views: {\n      [_ in never]: never\n    }\n    Functions: {\n      [_ in never]: never\n    }\n    Enums: {\n      [_ in never]: never\n    }\n  }\n}\n```\n\n**Features included:**\n\n1. **Complete auth methods**: Sign up, sign in, sign out, magic links, OAuth\n2. **Session management**: Get session, refresh session, auth state changes\n3. **Password management**: Reset and update passwords\n4. **User management**: Get user, update metadata, roles\n5. **Error handling**: Comprehensive error messages and types\n6. **TypeScript support**: Full type safety with proper interfaces\n7. **React hook**: Easy integration with React components\n8. **Validation helpers**: Email confirmation, authentication checks\n9. **OAuth providers**: Google, GitHub, Discord, Apple support\n10. **Metadata handling**: User profiles and custom data\n\n**Usage example:**\n\n```typescript\n// In a component\nimport { signIn, signUp, signOut } from '@/lib/auth'\nimport { useAuth } from '@/hooks/useAuth'\n\nexport function AuthComponent() {\n  const { user, loading, isAuthenticated } = useAuth()\n\n  const handleSignIn = async (email: string, password: string) => {\n    const { user, error } = await signIn({ email, password })\n    if (error) {\n      console.error('Sign in failed:', error.message)\n    }\n  }\n\n  if (loading) return <div>Loading...</div>\n  if (isAuthenticated) return <div>Welcome {user?.email}</div>\n  \n  return <div>Please sign in</div>\n}\n```"
}

Audit Trail (1)

19d agodependents_unblockedfoundry_pipeline
Task ID: 9bbfc1cf-c6a9-411e-b7b6-c5fafe6212ea