> ## Documentation Index
> Fetch the complete documentation index at: https://docs.8space.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> How Supabase Auth works in 8Space with code examples

8Space uses Supabase Auth for user authentication. The app supports email/password signup, password login, and Google OAuth.

## Authentication Methods

### Email/Password Signup

Create a new user account with email and password.

**Endpoint:** `POST /auth/v1/signup`

<ParamField body="email" type="string" required>
  User's email address (must be valid email format)
</ParamField>

<ParamField body="password" type="string" required>
  User's password (minimum 6 characters)
</ParamField>

<ParamField body="options.data.name" type="string">
  User's display name (stored in user metadata)
</ParamField>

**Example Request:**

```typescript theme={null}
const { error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'secure_password',
  options: {
    data: {
      name: 'John Doe',
    },
  },
});

if (error) {
  throw error;
}
```

**Response:**

```json theme={null}
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "refreshTokenString",
  "expires_in": 3600,
  "token_type": "bearer",
  "user": {
    "id": "9b7f3c6e-8e4a-4d3b-9f1a-2c5e6b8d7a9f",
    "email": "user@example.com",
    "user_metadata": {
      "name": "John Doe"
    },
    "app_metadata": {}
  }
}
```

<Note>
  The signup method is implemented in `use-auth.tsx:155-168` as `signUpWithPassword`.
</Note>

### Password Login

Sign in with existing credentials.

**Endpoint:** `POST /auth/v1/token?grant_type=password`

<ParamField query="grant_type" type="string" required>
  Must be `password` for password grant flow
</ParamField>

<ParamField body="email" type="string" required>
  User's email address
</ParamField>

<ParamField body="password" type="string" required>
  User's password
</ParamField>

**Example Request:**

```typescript theme={null}
const { error } = await supabase.auth.signInWithPassword({
  email: 'user@example.com',
  password: 'secure_password',
});

if (error) {
  throw error;
}
```

**Response:**

```json theme={null}
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer",
  "expires_in": 3600,
  "refresh_token": "refreshTokenString",
  "user": {
    "id": "9b7f3c6e-8e4a-4d3b-9f1a-2c5e6b8d7a9f",
    "email": "user@example.com"
  }
}
```

<Note>
  The password sign-in method is implemented in `use-auth.tsx:149-153` as `signInWithPassword`.
</Note>

### Google OAuth

Sign in with Google OAuth provider.

**Endpoint:** `GET /auth/v1/authorize?provider=google`

<ParamField query="provider" type="string" required>
  OAuth provider, must be `google`
</ParamField>

<ParamField query="redirect_to" type="string">
  Callback URL after OAuth completes
</ParamField>

**Example Request:**

```typescript theme={null}
const callbackUrl = new URL(
  `${import.meta.env.BASE_URL}auth/callback`,
  window.location.origin
).toString();

const { error } = await supabase.auth.signInWithOAuth({
  provider: 'google',
  options: {
    redirectTo: callbackUrl,
  },
});

if (error) {
  throw error;
}
```

**Response:** 302 redirect to Google OAuth consent screen

<Note>
  The Google OAuth method is implemented in `use-auth.tsx:170-180` as `signInWithGoogle`.
</Note>

## Auth Callback

After OAuth completes, Google redirects to the callback route.

**Endpoint:** `GET /auth/callback`

<ParamField query="code" type="string">
  Supabase auth code to exchange for session
</ParamField>

<ParamField query="next" type="string" default="/app">
  Relative redirect path after successful exchange
</ParamField>

**Response:** 302 redirect to `next` parameter (default `/app`)

<Note>
  The callback route exchanges the OAuth code for a session and sets cookies before redirecting the user.
</Note>

## Session Management

### Get Current User

Retrieve the authenticated user's information.

**Endpoint:** `GET /auth/v1/user`

**Headers:**

<ParamField header="apikey" type="string" required>
  Supabase anon/service key
</ParamField>

<ParamField header="Authorization" type="string" required>
  Bearer token: `Bearer {access_token}`
</ParamField>

**Response:**

```json theme={null}
{
  "id": "9b7f3c6e-8e4a-4d3b-9f1a-2c5e6b8d7a9f",
  "email": "user@example.com",
  "user_metadata": {
    "name": "John Doe"
  },
  "app_metadata": {}
}
```

### Sign Out

Log out the current user session.

**Endpoint:** `POST /auth/v1/logout`

**Headers:**

<ParamField header="apikey" type="string" required>
  Supabase anon/service key
</ParamField>

<ParamField header="Authorization" type="string" required>
  Bearer token: `Bearer {access_token}`
</ParamField>

**Example Request:**

```typescript theme={null}
const { error } = await supabase.auth.signOut();

if (error) {
  // Fallback to local sign out if remote fails
  const { error: localError } = await supabase.auth.signOut({ scope: 'local' });
  if (localError) {
    throw localError;
  }
}
```

**Response:** 200 with empty body

<Note>
  The sign-out method is implemented in `use-auth.tsx:182-189` with a fallback to local sign-out if the remote call fails.
</Note>

## Auth Context

8Space provides a React context for managing authentication state throughout the app.

### AuthProvider

Wrap your app with `AuthProvider` to provide auth context:

```tsx theme={null}
import { AuthProvider } from '@/hooks/use-auth';

function App() {
  return (
    <AuthProvider>
      {/* Your app components */}
    </AuthProvider>
  );
}
```

### useAuth Hook

Access authentication state and methods:

```tsx theme={null}
import { useAuth } from '@/hooks/use-auth';

function MyComponent() {
  const {
    session,      // Current session object
    user,         // Current user object
    profile,      // User profile with display_name and avatar_url
    loading,      // Loading state during session bootstrap
    signInWithPassword,
    signUpWithPassword,
    signInWithGoogle,
    signOut,
  } = useAuth();

  if (loading) {
    return <div>Loading...</div>;
  }

  if (!user) {
    return <div>Not authenticated</div>;
  }

  return <div>Welcome, {profile?.displayName}!</div>;
}
```

**Context Values:**

<ResponseField name="session" type="Session | null">
  Current Supabase session object
</ResponseField>

<ResponseField name="user" type="User | null">
  Current authenticated user
</ResponseField>

<ResponseField name="profile" type="UserProfile | null">
  User profile with `id`, `displayName`, and `avatarUrl`
</ResponseField>

<ResponseField name="loading" type="boolean">
  True during initial session bootstrap (max 15 seconds)
</ResponseField>

<ResponseField name="signInWithPassword" type="function">
  `(email: string, password: string) => Promise<void>`
</ResponseField>

<ResponseField name="signUpWithPassword" type="function">
  `(email: string, password: string, name: string) => Promise<void>`
</ResponseField>

<ResponseField name="signInWithGoogle" type="function">
  `() => Promise<void>`
</ResponseField>

<ResponseField name="signOut" type="function">
  `() => Promise<void>`
</ResponseField>

## Session Bootstrap

The `AuthProvider` automatically restores sessions on mount:

```typescript theme={null}
const { data, error } = await supabase.auth.getSession();

if (data.session?.user?.id) {
  // Fetch user profile
  const { data: profile } = await supabase
    .from('profiles')
    .select('id,display_name,avatar_url')
    .eq('id', data.session.user.id)
    .maybeSingle();
}
```

<Warning>
  Session bootstrap has a 15-second timeout. If bootstrap takes longer, the local session is cleared and loading completes.
</Warning>

**Bootstrap Process:**

1. Call `supabase.auth.getSession()` to restore session from cookies/localStorage
2. If session exists, fetch user profile from `profiles` table
3. Set `session`, `user`, and `profile` in context
4. Set `loading` to `false`
5. Subscribe to auth state changes

**Timeout Handling:**

```typescript theme={null}
const loadingFallbackTimer = window.setTimeout(() => {
  console.warn('Auth bootstrap timed out, clearing stale local session.');
  void supabase.auth.signOut({ scope: 'local' });
  setSession(null);
  setUser(null);
  setProfile(null);
  setLoading(false);
}, 15000);
```

<Note>
  The bootstrap logic is implemented in `use-auth.tsx:74-113`.
</Note>

## Auth State Changes

The `AuthProvider` listens for auth state changes and updates context:

```typescript theme={null}
const authSubscription = supabase.auth.onAuthStateChange(
  async (_event, nextSession) => {
    setSession(nextSession);
    setUser(nextSession?.user ?? null);

    if (nextSession?.user?.id) {
      const profileData = await fetchProfile(nextSession.user.id);
      setProfile(profileData);
    } else {
      setProfile(null);
    }
  }
);
```

<Note>
  Auth state change listener is implemented in `use-auth.tsx:115-132`.
</Note>

## Profile Fetching

User profiles are fetched from the `profiles` table:

```typescript theme={null}
async function fetchProfile(userId: string): Promise<UserProfile | null> {
  const { data, error } = await supabase
    .from('profiles')
    .select('id,display_name,avatar_url')
    .eq('id', userId)
    .maybeSingle();

  if (error) {
    throw error;
  }

  if (!data) {
    return null;
  }

  return {
    id: data.id,
    displayName: data.display_name,
    avatarUrl: data.avatar_url,
  };
}
```

<Note>
  The `fetchProfile` function is implemented in `use-auth.tsx:21-41`.
</Note>

## Making Authenticated Requests

All Supabase Data and RPC endpoints require authentication:

**Required Headers:**

<ParamField header="apikey" type="string" required>
  Supabase anon key from project settings
</ParamField>

<ParamField header="Authorization" type="string" required>
  Bearer token with format: `Bearer {access_token}`
</ParamField>

**Example:**

```typescript theme={null}
// The Supabase client automatically adds these headers
const { data, error } = await supabase
  .from('tasks')
  .select('*')
  .eq('project_id', projectId);
```

<Warning>
  Row Level Security (RLS) policies enforce access control. A 403 response indicates the user lacks permission for the requested resource.
</Warning>

## Error Handling

**Common Error Codes:**

<ResponseField name="400" type="Bad Request">
  Invalid credentials or validation error
</ResponseField>

<ResponseField name="401" type="Unauthorized">
  Missing or invalid authentication token
</ResponseField>

<ResponseField name="403" type="Forbidden">
  User lacks permission (RLS policy denied access)
</ResponseField>

**Error Response Format:**

```json theme={null}
{
  "message": "Invalid login credentials",
  "code": "invalid_credentials"
}
```

## Security Best Practices

<Warning>
  Never expose the Supabase service role key in client-side code. Only use the anon key.
</Warning>

1. **Use Row Level Security (RLS):** Enable RLS policies on all tables to enforce access control
2. **Validate inputs:** Always validate user inputs before sending to the API
3. **Handle token refresh:** Supabase automatically refreshes tokens, but handle errors gracefully
4. **Secure redirects:** Validate redirect URLs to prevent open redirect vulnerabilities
5. **Use HTTPS:** Always use HTTPS in production to protect tokens in transit

## Next Steps

<Card title="API Overview" icon="book" href="/api/overview">
  Explore all available API endpoints
</Card>
