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

# Authentication

# Authentication

Billbora uses a hybrid authentication system designed for both server-to-server integrations and frontend applications. This guide covers all authentication methods and best practices.

## Authentication Methods

<CardGroup cols={2}>
  <Card title="API Keys" icon="key" href="#api-keys">
    Simple authentication for server-to-server integrations
  </Card>

  <Card title="JWT Tokens" icon="fingerprint" href="#jwt-authentication">
    Secure authentication for frontend applications with organization context
  </Card>
</CardGroup>

## API Keys

API keys are the simplest way to authenticate with the Billbora API, ideal for server-to-server integrations.

### Getting API Keys

<Steps>
  <Step title="Log into Billbora">
    Navigate to [app.billbora.com](https://app.billbora.com) and log in.
  </Step>

  <Step title="Go to API Settings">
    Navigate to **Settings** → **API** → **Credentials**.
  </Step>

  <Step title="Generate API Key">
    Click **Generate New API Key** and copy the generated key.

    <Warning>
      Store this key securely - it won't be shown again!
    </Warning>
  </Step>
</Steps>

### Using API Keys

Include your API key in the `Authorization` header with the `Bearer` scheme:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET \
    https://api.billbora.com/api/v1/invoices/ \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.billbora.com/api/v1/invoices/', {
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY'
    }
  })
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      'https://api.billbora.com/api/v1/invoices/',
      headers={'Authorization': 'Bearer YOUR_API_KEY'}
  )
  ```
</CodeGroup>

### API Key Security

<AccordionGroup>
  <Accordion title="Best Practices">
    * Store API keys in environment variables, never in code
    * Use different keys for different environments (development, staging, production)
    * Rotate keys regularly (every 90 days recommended)
    * Monitor API key usage in your dashboard
    * Revoke compromised keys immediately
  </Accordion>

  <Accordion title="Key Management">
    * Each organization can have multiple API keys
    * Keys can be scoped to specific permissions
    * Usage analytics are available per key
    * Keys can be temporarily disabled without deletion
  </Accordion>
</AccordionGroup>

## JWT Authentication

JWT authentication is designed for frontend applications and provides organization context switching capabilities.

### Authentication Flow

The JWT authentication flow involves multiple steps:

```mermaid theme={null}
sequenceDiagram
    participant Frontend
    participant Supabase
    participant Billbora API
    
    Frontend->>Supabase: Login with email/password
    Supabase-->>Frontend: Supabase JWT token
    Frontend->>Billbora API: Exchange token + org context
    Billbora API-->>Frontend: Billbora JWT + refresh token
    Frontend->>Billbora API: API calls with Billbora JWT
```

### Step 1: Supabase Authentication

First, authenticate with Supabase to get the initial JWT token:

<CodeGroup>
  ```javascript JavaScript theme={null}
  import { createClient } from '@supabase/supabase-js'

  const supabase = createClient(
    'https://your-project.supabase.co',
    'your-supabase-anon-key'
  )

  const { data, error } = await supabase.auth.signInWithPassword({
    email: 'user@example.com',
    password: 'secure-password'
  })

  const supabaseToken = data.session?.access_token
  ```

  ```typescript TypeScript theme={null}
  import { createClient, SupabaseClient } from '@supabase/supabase-js'

  const supabase: SupabaseClient = createClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  )

  const { data, error } = await supabase.auth.signInWithPassword({
    email: 'user@example.com',
    password: 'secure-password'
  })

  if (error) {
    throw new Error(`Authentication failed: ${error.message}`)
  }

  const supabaseToken = data.session?.access_token
  ```
</CodeGroup>

### Step 2: Token Exchange

Exchange the Supabase token for a Billbora token with organization context:

<CodeGroup>
  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.billbora.com/api/v1/users/auth/exchange/', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      supabase_token: supabaseToken,
      organization_id: 'org_1234567890' // Optional
    })
  })

  const tokens = await response.json()
  // tokens.access, tokens.refresh, tokens.organization
  ```

  ```typescript TypeScript theme={null}
  interface TokenExchangeRequest {
    supabase_token: string
    organization_id?: string
  }

  interface TokenExchangeResponse {
    access: string
    refresh: string
    access_expires: number
    organization: {
      id: string
      name: string
      role: 'owner' | 'admin' | 'member'
    }
  }

  const exchangeTokens = async (
    supabaseToken: string, 
    organizationId?: string
  ): Promise<TokenExchangeResponse> => {
    const response = await fetch('https://api.billbora.com/api/v1/users/auth/exchange/', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        supabase_token: supabaseToken,
        organization_id: organizationId
      })
    })

    if (!response.ok) {
      const error = await response.json()
      throw new Error(`Token exchange failed: ${error.error}`)
    }

    return response.json()
  }
  ```
</CodeGroup>

### Step 3: Using JWT Tokens

Use the Billbora JWT token for API requests:

<CodeGroup>
  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.billbora.com/api/v1/invoices/', {
    headers: {
      'Authorization': `Bearer ${tokens.access}`,
      'Content-Type': 'application/json'
    }
  })
  ```

  ```typescript TypeScript theme={null}
  class BillboraAPIClient {
    private accessToken: string
    private refreshToken: string
    
    async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
      const response = await fetch(`https://api.billbora.com/api/v1${endpoint}`, {
        ...options,
        headers: {
          'Authorization': `Bearer ${this.accessToken}`,
          'Content-Type': 'application/json',
          ...options.headers
        }
      })
      
      if (response.status === 401) {
        await this.refreshTokens()
        // Retry the request with new token
        return this.request<T>(endpoint, options)
      }
      
      return response.json()
    }
  }
  ```
</CodeGroup>

### Token Refresh

JWT tokens expire after 1 hour. Use the refresh token to get a new access token:

<CodeGroup>
  ```javascript JavaScript theme={null}
  const refreshResponse = await fetch('https://api.billbora.com/api/v1/users/auth/refresh/', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      refresh: refreshToken
    })
  })

  const newTokens = await refreshResponse.json()
  // Update stored tokens
  ```

  ```typescript TypeScript theme={null}
  const refreshTokens = async (refreshToken: string): Promise<TokenExchangeResponse> => {
    const response = await fetch('https://api.billbora.com/api/v1/users/auth/refresh/', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        refresh: refreshToken
      })
    })

    if (!response.ok) {
      throw new Error('Token refresh failed - please log in again')
    }

    const data = await response.json()
    return {
      access: data.access,
      refresh: refreshToken, // Refresh token doesn't change
      access_expires: data.access_expires || 3600,
      organization: getCurrentOrganization() // Keep current org context
    }
  }
  ```
</CodeGroup>

## Organization Context

One of the key features of Billbora's authentication system is organization context switching.

### Switching Organizations

Users can belong to multiple organizations and switch between them:

<CodeGroup>
  ```javascript JavaScript theme={null}
  const switchOrganization = async (organizationId) => {
    // Get fresh Supabase token
    const { data } = await supabase.auth.getSession()
    const supabaseToken = data.session?.access_token
    
    // Exchange for new organization context
    const newTokens = await exchangeTokens(supabaseToken, organizationId)
    
    // Update stored tokens
    localStorage.setItem('billbora_auth', JSON.stringify(newTokens))
    
    return newTokens
  }
  ```

  ```typescript TypeScript theme={null}
  const switchOrganization = async (organizationId: string): Promise<TokenExchangeResponse> => {
    const { data } = await supabase.auth.getSession()
    
    if (!data.session?.access_token) {
      throw new Error('Supabase session expired')
    }
    
    const newTokens = await exchangeTokens(data.session.access_token, organizationId)
    
    // Update auth state
    setAuthTokens(newTokens)
    
    return newTokens
  }
  ```
</CodeGroup>

### Getting Available Organizations

Fetch organizations the current user has access to:

<CodeGroup>
  ```javascript JavaScript theme={null}
  const organizations = await fetch('https://api.billbora.com/api/v1/users/me/organizations/', {
    headers: {
      'Authorization': `Bearer ${accessToken}`
    }
  })

  const orgList = await organizations.json()
  ```

  ```typescript TypeScript theme={null}
  interface UserOrganization {
    id: string
    name: string
    role: 'owner' | 'admin' | 'member'
    is_active: boolean
  }

  const getUserOrganizations = async (accessToken: string): Promise<UserOrganization[]> => {
    const response = await fetch('https://api.billbora.com/api/v1/users/me/organizations/', {
      headers: {
        'Authorization': `Bearer ${accessToken}`
      }
    })
    
    return response.json()
  }
  ```
</CodeGroup>

## Error Handling

Handle authentication errors gracefully:

<AccordionGroup>
  <Accordion title="Common Error Codes">
    * `invalid_credentials`: Wrong email/password combination
    * `token_expired`: JWT token has expired, refresh needed
    * `invalid_token`: Malformed or invalid token
    * `organization_access_denied`: User doesn't have access to requested organization
    * `rate_limited`: Too many authentication attempts
  </Accordion>

  <Accordion title="Error Response Format">
    ```json theme={null}
    {
      "error": {
        "code": "token_expired",
        "message": "The provided token has expired",
        "details": {
          "expired_at": "2025-01-15T10:30:00Z"
        }
      }
    }
    ```
  </Accordion>
</AccordionGroup>

## Security Best Practices

<CardGroup cols={2}>
  <Card title="Token Storage" icon="database">
    * Store tokens securely (not in localStorage for sensitive apps)
    * Use httpOnly cookies for web applications when possible
    * Clear tokens on logout
    * Implement token expiration checking
  </Card>

  <Card title="Network Security" icon="shield">
    * Always use HTTPS in production
    * Implement proper CORS policies
    * Validate SSL certificates
    * Monitor for unusual authentication patterns
  </Card>

  <Card title="Application Security" icon="lock">
    * Implement proper session management
    * Use secure password requirements
    * Enable two-factor authentication
    * Implement account lockout policies
  </Card>

  <Card title="Monitoring" icon="chart-line">
    * Monitor authentication failures
    * Set up alerts for suspicious activity
    * Regularly audit API key usage
    * Implement request rate limiting
  </Card>
</CardGroup>

## Complete Authentication Example

Here's a complete example of implementing authentication in a React application:

<CodeGroup>
  ```typescript TypeScript - React Hook theme={null}
  import { useState, useEffect, useCallback, createContext, useContext } from 'react'
  import { createClient } from '@supabase/supabase-js'

  interface AuthState {
    isAuthenticated: boolean
    isLoading: boolean
    user: any
    organization: any
    error: string | null
  }

  const AuthContext = createContext<any>(null)

  export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
    const [state, setState] = useState<AuthState>({
      isAuthenticated: false,
      isLoading: true,
      user: null,
      organization: null,
      error: null
    })

    const supabase = createClient(
      process.env.REACT_APP_SUPABASE_URL!,
      process.env.REACT_APP_SUPABASE_ANON_KEY!
    )

    const login = useCallback(async (email: string, password: string) => {
      setState(prev => ({ ...prev, isLoading: true, error: null }))
      
      try {
        // Step 1: Supabase authentication
        const { data, error } = await supabase.auth.signInWithPassword({ email, password })
        if (error) throw error

        // Step 2: Token exchange
        const tokens = await exchangeTokens(data.session!.access_token)
        
        // Step 3: Get user info
        const user = await fetchUser(tokens.access)
        
        setState({
          isAuthenticated: true,
          isLoading: false,
          user,
          organization: tokens.organization,
          error: null
        })
        
        // Store tokens
        localStorage.setItem('billbora_auth', JSON.stringify(tokens))
        
      } catch (error: any) {
        setState(prev => ({
          ...prev,
          isLoading: false,
          error: error.message
        }))
      }
    }, [])

    const logout = useCallback(async () => {
      await supabase.auth.signOut()
      localStorage.removeItem('billbora_auth')
      
      setState({
        isAuthenticated: false,
        isLoading: false,
        user: null,
        organization: null,
        error: null
      })
    }, [])

    return (
      <AuthContext.Provider value={{ ...state, login, logout }}>
        {children}
      </AuthContext.Provider>
    )
  }

  export const useAuth = () => {
    const context = useContext(AuthContext)
    if (!context) {
      throw new Error('useAuth must be used within AuthProvider')
    }
    return context
  }
  ```
</CodeGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Frontend Integration" icon="browser" href="/guides/frontend-integration">
    Learn how to integrate authentication in your frontend application
  </Card>

  <Card title="API Reference" icon="book" href="/api-reference/authentication">
    Complete authentication API reference
  </Card>

  <Card title="Organization Management" icon="building" href="/resources/organizations">
    Learn about multi-tenant organization features
  </Card>

  <Card title="Security Guide" icon="shield" href="/guides/security">
    Advanced security practices and configurations
  </Card>
</CardGroup>
