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

# Login

> Authenticate user and return JWT token

# Login

Authenticate a user with email and password credentials and return JWT tokens.

## 📝 Endpoint

```http theme={"dark"}
POST /auth/login
```

## 📋 Request Body

<ParamField body="email" type="string" required>
  The user's email address.
</ParamField>

<ParamField body="password" type="string" required>
  The user's password.
</ParamField>

## 📤 Request Example

```bash theme={"dark"}
curl -X POST https://api.posthoot.com/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "john.doe@example.com",
    "password": "secure_password123"
  }'
```

```javascript theme={"dark"}
const response = await fetch('https://api.posthoot.com/auth/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    email: 'john.doe@example.com',
    password: 'secure_password123'
  })
});

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

```python theme={"dark"}
import requests

response = requests.post(
    'https://api.posthoot.com/auth/login',
    json={
        'email': 'john.doe@example.com',
        'password': 'secure_password123'
    }
)

data = response.json()
```

## 📥 Response

### Success (200 OK)

```json theme={"dark"}
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "user_123",
    "email": "john.doe@example.com",
    "first_name": "John",
    "last_name": "Doe",
    "role": "ADMIN",
    "team_id": "team_456",
    "created_at": "2024-01-01T00:00:00Z",
    "updated_at": "2024-01-01T00:00:00Z"
  },
  "team": {
    "id": "team_456",
    "name": "John's Team",
    "created_at": "2024-01-01T00:00:00Z"
  },
  "expires_in": 86400
}
```

### Error Responses

#### Invalid Credentials (401 Unauthorized)

```json theme={"dark"}
{
  "error": "unauthorized",
  "message": "Invalid email or password",
  "code": "INVALID_CREDENTIALS"
}
```

#### Account Locked (401 Unauthorized)

```json theme={"dark"}
{
  "error": "unauthorized",
  "message": "Account is locked due to too many failed attempts",
  "code": "ACCOUNT_LOCKED"
}
```

#### Missing Fields (400 Bad Request)

```json theme={"dark"}
{
  "error": "validation_error",
  "message": "Email and password are required",
  "code": "VALIDATION_ERROR"
}
```

## 🔐 Token Information

### Access Token

* **Lifetime**: 24 hours
* **Usage**: Include in `Authorization: Bearer <token>` header
* **Purpose**: Authenticate API requests

### Refresh Token

* **Lifetime**: 7 days
* **Usage**: Obtain new access tokens
* **Purpose**: Maintain session without re-authentication

## 🛡️ Security Features

### Rate Limiting

* **Login attempts**: 5 per minute per IP
* **Account lockout**: Temporary lock after 10 failed attempts
* **Lockout duration**: 15 minutes

### Password Security

* **Bcrypt hashing**: Passwords are securely hashed
* **Salt**: Each password has a unique salt
* **Timing attacks**: Constant-time comparison

### Session Management

* **Token rotation**: Refresh tokens are rotated on use
* **Revocation**: Tokens can be revoked if compromised
* **Audit trail**: Login attempts are logged

## 🔄 Using the Tokens

### Make API Requests

```javascript theme={"dark"}
const response = await fetch('https://api.posthoot.com/api/v1/campaigns', {
  headers: {
    'Authorization': `Bearer ${data.access_token}`
  }
});
```

### Refresh Token

```javascript theme={"dark"}
const refreshResponse = await fetch('https://api.posthoot.com/auth/refresh', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    refresh_token: data.refresh_token
  })
});
```

## 🚨 Error Handling

### Handle Authentication Errors

```javascript theme={"dark"}
async function login(email, password) {
  try {
    const response = await fetch('/auth/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password })
    });

    if (!response.ok) {
      const error = await response.json();
      
      switch (error.code) {
        case 'INVALID_CREDENTIALS':
          throw new Error('Invalid email or password');
        case 'ACCOUNT_LOCKED':
          throw new Error('Account is temporarily locked');
        default:
          throw new Error(error.message);
      }
    }

    return await response.json();
  } catch (error) {
    console.error('Login error:', error);
    throw error;
  }
}
```

## 📋 Best Practices

### 1. Secure Token Storage

```javascript theme={"dark"}
// Store tokens securely (not in localStorage)
const secureStorage = {
  setTokens: (tokens) => {
    // Use secure HTTP-only cookies or encrypted storage
    document.cookie = `access_token=${tokens.access_token}; Secure; HttpOnly; SameSite=Strict`;
    document.cookie = `refresh_token=${tokens.refresh_token}; Secure; HttpOnly; SameSite=Strict`;
  },
  
  getTokens: () => {
    // Retrieve tokens from secure storage
    return {
      access_token: getCookie('access_token'),
      refresh_token: getCookie('refresh_token')
    };
  }
};
```

### 2. Automatic Token Refresh

```javascript theme={"dark"}
class TokenManager {
  constructor() {
    this.accessToken = null;
    this.refreshToken = null;
  }

  async refreshAccessToken() {
    const response = await fetch('/auth/refresh', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ refresh_token: this.refreshToken })
    });

    if (response.ok) {
      const data = await response.json();
      this.accessToken = data.access_token;
      return this.accessToken;
    } else {
      // Redirect to login
      window.location.href = '/login';
    }
  }

  async makeAuthenticatedRequest(url, options = {}) {
    if (!this.accessToken) {
      throw new Error('No access token available');
    }

    const response = await fetch(url, {
      ...options,
      headers: {
        ...options.headers,
        'Authorization': `Bearer ${this.accessToken}`
      }
    });

    if (response.status === 401) {
      // Token expired, try to refresh
      await this.refreshAccessToken();
      return this.makeAuthenticatedRequest(url, options);
    }

    return response;
  }
}
```

### 3. Handle Logout

```javascript theme={"dark"}
async function logout() {
  // Clear tokens from storage
  secureStorage.clearTokens();
  
  // Optionally call logout endpoint to invalidate tokens
  try {
    await fetch('/auth/logout', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${currentToken}`
      }
    });
  } catch (error) {
    console.error('Logout error:', error);
  }
  
  // Redirect to login page
  window.location.href = '/login';
}
```

## 📚 Related Endpoints

* [Register](/auth/register) - Create new account
* [Google OAuth](/auth/google) - Login with Google
* [Refresh Token](/auth/refresh) - Get new access token
* [Password Reset](/auth/password-reset) - Reset forgotten password
* [Get Current User](/users/me) - Get user details
