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

# API Authentication

> Learn how to authenticate with the Harbor Parking API using JWT tokens

## Overview

Harbor Parking API uses **Bearer token authentication** with JWT (JSON Web Tokens) issued by Supabase Auth. All API endpoints require authentication except for public documentation.

## Authentication Flow

<Steps>
  <Step title="User Registration">
    Users sign up through the Harbor Parking web interface with email and apartment details.
  </Step>

  <Step title="Email Verification">
    Users must verify their email address before proceeding.
  </Step>

  <Step title="Admin Approval">
    Building administrators review and approve new users for security.
  </Step>

  <Step title="Token Acquisition">
    Approved users can obtain JWT tokens through login or direct API authentication.
  </Step>
</Steps>

## Getting a JWT Token

### Method 1: Web Application Login

The easiest way to get a token for testing:

1. Visit [Harbor Parking Login](https://harbor-parking.vercel.app/auth/login)
2. Sign up and get admin approval
3. Log in and extract the token from:
   * Browser Developer Tools → Application → Local Storage
   * Network tab during API requests
   * Browser console: `localStorage.getItem('supabase.auth.token')`

### Method 2: Direct API Authentication

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://YOUR_SUPABASE_URL/auth/v1/token?grant_type=password" \
    -H "apikey: YOUR_SUPABASE_ANON_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "email": "your.email@example.com",
      "password": "your_password"
    }'
  ```

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

  const supabase = createClient(
    'YOUR_SUPABASE_URL',
    'YOUR_SUPABASE_ANON_KEY'
  )

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

  if (data.session) {
    const token = data.session.access_token
    console.log('JWT Token:', token)
  }
  ```

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

  url = "https://YOUR_SUPABASE_URL/auth/v1/token?grant_type=password"
  headers = {
      "apikey": "YOUR_SUPABASE_ANON_KEY",
      "Content-Type": "application/json"
  }
  data = {
      "email": "your.email@example.com",
      "password": "your_password"
  }

  response = requests.post(url, headers=headers, json=data)
  auth_data = response.json()

  if "access_token" in auth_data:
      token = auth_data["access_token"]
      print(f"JWT Token: {token}")
  ```
</CodeGroup>

## Using Your Token

Include the JWT token in the `Authorization` header for all API requests:

```http theme={null}
Authorization: Bearer YOUR_JWT_TOKEN
```

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://harbor-parking.vercel.app/api/profile" \
    -H "Authorization: Bearer YOUR_JWT_TOKEN" \
    -H "Content-Type: application/json"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://harbor-parking.vercel.app/api/profile', {
    headers: {
      'Authorization': `Bearer ${yourJwtToken}`,
      'Content-Type': 'application/json'
    }
  });

  const data = await response.json();
  ```

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

  headers = {
      'Authorization': f'Bearer {your_jwt_token}',
      'Content-Type': 'application/json'
  }

  response = requests.get(
      'https://harbor-parking.vercel.app/api/profile',
      headers=headers
  )
  ```
</CodeGroup>

## Token Properties

JWT tokens contain user information and permissions:

```json theme={null}
{
  "aud": "authenticated",
  "exp": 1640995200,
  "sub": "123e4567-e89b-12d3-a456-426614174000",
  "email": "john.doe@example.com",
  "role": "authenticated",
  "app_metadata": {
    "provider": "email"
  },
  "user_metadata": {
    "email": "john.doe@example.com"
  }
}
```

**Key Fields:**

* `sub` - User ID (UUID)
* `email` - User's email address
* `exp` - Token expiration (Unix timestamp)
* `aud` - Audience ("authenticated")

## Token Expiration

JWT tokens expire after **1 hour** for security. Handle expiration gracefully:

<CodeGroup>
  ```javascript Token Refresh theme={null}
  function isTokenExpired(token) {
    try {
      const payload = JSON.parse(atob(token.split('.')[1]));
      return Date.now() >= payload.exp * 1000;
    } catch {
      return true;
    }
  }

  async function makeAuthenticatedRequest(url, options = {}) {
    let token = getStoredToken();
    
    if (isTokenExpired(token)) {
      // Refresh token
      const { data } = await supabase.auth.refreshSession();
      token = data.session?.access_token;
      storeToken(token);
    }
    
    return fetch(url, {
      ...options,
      headers: {
        ...options.headers,
        'Authorization': `Bearer ${token}`
      }
    });
  }
  ```

  ```python Token Validation theme={null}
  import jwt
  import time
  from datetime import datetime

  def is_token_expired(token):
      try:
          payload = jwt.decode(token, options={"verify_signature": False})
          return datetime.now().timestamp() >= payload['exp']
      except:
          return True

  def make_authenticated_request(url, **kwargs):
      token = get_stored_token()
      
      if is_token_expired(token):
          token = refresh_token()
          store_token(token)
      
      headers = kwargs.get('headers', {})
      headers['Authorization'] = f'Bearer {token}'
      
      return requests.request(url=url, headers=headers, **kwargs)
  ```
</CodeGroup>

## Permission Levels

Different user roles have varying API access:

### Regular User (Approved)

```json theme={null}
{
  "is_approved": true,
  "is_admin": false
}
```

**API Access:**

* ✅ Profile management
* ✅ Own parking spots CRUD
* ✅ Create availabilities for owned spots
* ✅ View all available spots
* ✅ Create and manage own claims
* ❌ Admin endpoints

### Building Admin

```json theme={null}
{
  "is_approved": true,
  "is_admin": true
}
```

**API Access:**

* ✅ All regular user permissions
* ✅ User approval/rejection
* ✅ Verify parking spot ownership
* ✅ View all claims in building
* ✅ Access admin dashboard
* ❌ Multi-building management

### Unapproved User

```json theme={null}
{
  "is_approved": false,
  "is_admin": false
}
```

**API Access:**

* ✅ View own profile only
* ❌ All other endpoints return 403

## Security Best Practices

<Warning>
  **Never expose JWT tokens** in client-side code, URLs, or logs. Treat them like passwords.
</Warning>

### Client-Side Security

* **Secure Storage**: Use httpOnly cookies or secure storage APIs
* **HTTPS Only**: Never send tokens over HTTP in production
* **Token Rotation**: Implement automatic token refresh
* **Logout Cleanup**: Clear tokens on user logout
* **Scope Validation**: Check user permissions before UI actions

### Server-Side Validation

* **Verify Signature**: Validate token signature with Supabase public key
* **Check Expiration**: Reject expired tokens
* **Validate Claims**: Verify audience, issuer, and custom claims
* **Rate Limiting**: Implement per-user rate limits
* **Audit Logging**: Log authentication events

## Common Authentication Errors

<AccordionGroup>
  <Accordion title="401 Unauthorized - Missing Token">
    ```json theme={null}
    {
      "error": "Authentication required"
    }
    ```

    **Cause**: No Authorization header provided\
    **Solution**: Include `Authorization: Bearer YOUR_TOKEN`
  </Accordion>

  <Accordion title="401 Unauthorized - Invalid Token">
    ```json theme={null}
    {
      "error": "Invalid token"
    }
    ```

    **Cause**: Token is malformed, expired, or revoked\
    **Solution**: Refresh token or re-authenticate
  </Accordion>

  <Accordion title="403 Forbidden - Account Not Approved">
    ```json theme={null}
    {
      "error": "Account pending approval. Contact your building administrator."
    }
    ```

    **Cause**: Valid token but user not approved\
    **Solution**: Wait for admin approval
  </Accordion>

  <Accordion title="403 Forbidden - Insufficient Permissions">
    ```json theme={null}
    {
      "error": "You do not have permission to perform this action"
    }
    ```

    **Cause**: User lacks required role for endpoint\
    **Solution**: Check if admin permissions needed
  </Accordion>
</AccordionGroup>

## Testing Authentication

### Test Tokens

For development, you can create test users:

```bash theme={null}
# Create test user via Supabase CLI
supabase auth signup --email test@example.com --password TestPassword123!

# Get auth token for testing
supabase auth token
```

### Postman Setup

1. **Create Environment Variables**:
   * `base_url`: `https://harbor-parking.vercel.app/api`
   * `jwt_token`: Your JWT token

2. **Set Authorization Header**:
   * Type: Bearer Token
   * Token: `{{jwt_token}}`

3. **Pre-request Script** (for auto-refresh):
   ```javascript theme={null}
   // Check if token is expired and refresh if needed
   const token = pm.environment.get("jwt_token");

   if (isTokenExpired(token)) {
       // Implement token refresh logic
       refreshToken();
   }
   ```

### cURL Examples

```bash theme={null}
# Store token in variable
export HARBOR_TOKEN="your-jwt-token-here"

# Test authenticated request
curl -H "Authorization: Bearer $HARBOR_TOKEN" \
     https://harbor-parking.vercel.app/api/profile

# Test with invalid token (should return 401)
curl -H "Authorization: Bearer invalid-token" \
     https://harbor-parking.vercel.app/api/profile
```

## Next Steps

<CardGroup cols={2}>
  <Card title="API Introduction" icon="book-open" href="/api-reference/introduction">
    Learn about API endpoints and usage
  </Card>

  <Card title="Quick Start" icon="rocket" href="/api-reference/quickstart">
    Make your first authenticated API call
  </Card>
</CardGroup>
