Archon/archon-ui-main/src/features/auth/services/authService.ts
Claude 65348bfed0
feat: Add Supabase authentication with login/signup pages
Implements complete authentication system using Supabase Auth SDK following Archon's vertical slice architecture.

Frontend Changes:
- Install @supabase/supabase-js dependency
- Create auth feature in vertical slice pattern:
  * AuthContext and Provider for global auth state
  * authService with Supabase Auth methods (signIn, signUp, signOut, etc.)
  * Auth query hooks with TanStack Query integration
  * TypeScript types for User, Session, AuthState
  * ProtectedRoute component for route guards
- Add LoginPage and SignUpPage with Tron-themed design
- Update App.tsx with AuthProvider and protected routes
- Configure Supabase client with environment variables

Backend Changes:
- Create auth_service.py for JWT token validation
- Create auth_middleware.py for protecting API routes (optional, commented by default)
- Create auth_api.py with endpoints:
  * POST /api/auth/verify - Verify JWT token
  * GET /api/auth/user - Get current user
  * GET /api/auth/health - Auth service health check
- Register auth router in main.py
- Add middleware configuration (disabled by default)

Configuration:
- Update .env.example with VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY
- Add comprehensive AUTHENTICATION_SETUP.md documentation

Features:
- Email/password authentication
- Persistent sessions with localStorage
- Auto token refresh
- Route protection with loading states
- Integration with existing TanStack Query patterns
- Optional backend middleware for API protection
- Row Level Security (RLS) ready

Architecture follows CLAUDE.md guidelines:
- Vertical slice architecture for auth feature
- TanStack Query for state management
- No backwards compatibility needed (beta)
- KISS principle
- Fail fast with detailed errors

Notes:
- Auth middleware is commented out by default to avoid breaking existing installations
- Users can enable it when ready by uncommenting in main.py
- Frontend auth works independently of backend middleware
- Comprehensive setup guide included in AUTHENTICATION_SETUP.md
2025-11-15 04:13:17 +00:00

112 lines
2.3 KiB
TypeScript

import { supabase } from "../config/supabaseClient";
import type { LoginCredentials, SignUpCredentials, User, Session } from "../types";
export const authService = {
async signIn(credentials: LoginCredentials): Promise<{ user: User; session: Session }> {
const { data, error } = await supabase.auth.signInWithPassword({
email: credentials.email,
password: credentials.password,
});
if (error) {
throw new Error(error.message);
}
if (!data.user || !data.session) {
throw new Error("Login failed: No user or session returned");
}
return {
user: data.user,
session: data.session,
};
},
async signUp(credentials: SignUpCredentials): Promise<{ user: User; session: Session | null }> {
const { data, error } = await supabase.auth.signUp({
email: credentials.email,
password: credentials.password,
options: {
data: credentials.metadata || {},
},
});
if (error) {
throw new Error(error.message);
}
if (!data.user) {
throw new Error("Sign up failed: No user returned");
}
return {
user: data.user,
session: data.session,
};
},
async signOut(): Promise<void> {
const { error } = await supabase.auth.signOut();
if (error) {
throw new Error(error.message);
}
},
async getCurrentUser(): Promise<User | null> {
const {
data: { user },
error,
} = await supabase.auth.getUser();
if (error) {
throw new Error(error.message);
}
return user;
},
async getSession(): Promise<Session | null> {
const {
data: { session },
error,
} = await supabase.auth.getSession();
if (error) {
throw new Error(error.message);
}
return session;
},
async resetPassword(email: string): Promise<void> {
const { error } = await supabase.auth.resetPasswordForEmail(email, {
redirectTo: `${window.location.origin}/reset-password`,
});
if (error) {
throw new Error(error.message);
}
},
async updatePassword(newPassword: string): Promise<void> {
const { error } = await supabase.auth.updateUser({
password: newPassword,
});
if (error) {
throw new Error(error.message);
}
},
onAuthStateChange(callback: (user: User | null, session: Session | null) => void) {
const {
data: { subscription },
} = supabase.auth.onAuthStateChange((_event, session) => {
callback(session?.user ?? null, session);
});
return subscription;
},
};