Authentication

Eden implements a secure, token-based authentication system using JSON Web Tokens (JWT). This approach provides a stateless authentication mechanism that's both secure and scalable, allowing the API to maintain security without storing session data.

Understanding JWT AuthenticationCopied!

JSON Web Tokens are an industry standard (RFC 7519) for securely transmitting information between parties. Each token contains encoded JSON objects and is cryptographically signed to ensure the information hasn't been tampered with. In Eden, these tokens contain information about the user's identity and permissions.

A typical JWT token flow in Eden works as follows:

  1. The client authenticates using credentials and receives a JWT token

  2. For subsequent requests, the client includes this token in the Authorization header

  3. Eden validates the token signature and extracts user information without needing to query a database

  4. If valid, the request proceeds; if invalid or expired, authentication fails

Getting Started with AuthenticationCopied!

  1. Login to obtain a JWT token

    • Endpoint: POST /auth/login

    • This endpoint requires basic authentication (username/password) to verify identity

    • You must include the Organization ID in the X-Org-Id header to specify which organization you're accessing

    • Upon successful authentication, you'll receive a JWT token with an expiration time

    • Example request:

      POST /auth/loginHeaders:  X-Org-Id: YourOrgId  Authorization: Basic base64(username:password)
      
  2. Using your JWT token

    • Include the token in the Authorization header for all subsequent API calls

    • Use the Bearer authentication scheme

    • Example:

      GET /endpoints/myEndpointHeaders:  Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
      
  3. Refresh your token

    • Endpoint: GET /auth/refresh

    • JWT tokens have a limited lifespan to enhance security

    • Before expiration, call this endpoint to receive a new token

    • The current token must be valid to obtain a new one

    • This maintains continuous authentication without requiring re-login

  4. Password Reset

    • Endpoint: POST /auth/password/reset

    • Provides a secure way to reset passwords when forgotten

    • Initiates a reset process that typically involves email verification

    • This is separate from changing a known password

    • Follows security best practices for account recovery

Security ConsiderationsCopied!

  • JWT tokens should be treated as sensitive credentials and transmitted only over HTTPS

  • Store tokens securely on client systems, preferably in memory for web applications

  • Implement token refresh logic to handle expiration gracefully

  • Never send tokens in URL parameters to avoid accidental leakage in logs

  • The short lifespan of tokens limits the damage if a token is compromised

API ReferenceCopied!

Login

  • URL: /auth/login

  • Method: POST

  • Headers Required:

    • X-Org-Id: Organization ID (e.g., "TestOrg")

    • Basic Authentication credentials

  • Request Body: Organization details

  • Success Response: 200 OK with JWT token

  • Error Response: 401 Unauthorized

  • Example Response:

    {  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",  "expires": "2025-05-12T10:45:22Z",  "user": {    "username": "jane.doe@example.com",    "accessLevel": "Admin"  }}
    

Refresh Token

  • URL: /auth/refresh

  • Method: GET

  • Headers Required: Authorization with JWT token

  • Success Response: 200 OK with refreshed JWT token

  • Error Response: 401 Unauthorized if token is invalid or expired

  • Example Response:

    {  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",  "expires": "2025-05-12T12:45:22Z",  "user": {    "username": "jane.doe@example.com",    "accessLevel": "Admin"  }}
    

Request Password Reset

  • URL: /auth/password/reset

  • Method: POST

  • Request Body: Password reset request details

  • Example Request:

    {  "email": "jane.doe@example.com",  "redirectUrl": "https://your-app.example.com/reset-confirmation"}
    
  • Success Response: 200 OK with confirmation message

  • Example Response:

    {  "message": "Password reset instructions have been sent to your email address",  "requestId": "pr_123456"}
    

Common Authentication IssuesCopied!

Token Expiration

If your token expires, you'll receive a 401 Unauthorized response. Implement proactive token refresh by:

  • Tracking token expiration time

  • Refreshing tokens before they expire

  • Having a fallback to the login flow if refresh fails

Invalid Credentials

If login credentials are incorrect, the server returns a 401 Unauthorized response. To handle this:

  • Provide clear error messages to users

  • Implement account lockout policies for repeated failures

  • Log authentication failures for security monitoring

Missing Organization ID

If the X-Org-Id header is missing in the login request, authentication will fail. Ensure that:

  • Your application correctly stores organization IDs

  • The header is included in all authentication requests

  • Users are provided a way to select their organization if they belong to multiple

Integration ExamplesCopied!

Web Application Authentication Flow

// Login function
async function login(username, password, orgId) {
  const credentials = btoa(`${username}:${password}`);
  
  const response = await fetch('/auth/login', {
    method: 'POST',
    headers: {
      'Authorization': `Basic ${credentials}`,
      'X-Org-Id': orgId
    }
  });
  
  if (response.ok) {
    const data = await response.json();
    // Store token securely
    sessionStorage.setItem('token', data.token);
    sessionStorage.setItem('tokenExpires', data.expires);
    return data;
  } else {
    throw new Error('Authentication failed');
  }
}

// Token refresh function
async function refreshToken() {
  const currentToken = sessionStorage.getItem('token');
  
  const response = await fetch('/auth/refresh', {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${currentToken}`
    }
  });
  
  if (response.ok) {
    const data = await response.json();
    // Update stored token
    sessionStorage.setItem('token', data.token);
    sessionStorage.setItem('tokenExpires', data.expires);
    return data;
  } else {
    // Redirect to login page if refresh fails
    window.location.href = '/login';
  }
}

// Automatic token refresh setup
function setupTokenRefresh() {
  const tokenExpires = new Date(sessionStorage.getItem('tokenExpires'));
  const refreshTime = new Date(tokenExpires.getTime() - (5 * 60 * 1000)); // 5 minutes before expiry
  
  const now = new Date();
  const timeUntilRefresh = refreshTime - now;
  
  if (timeUntilRefresh > 0) {
    setTimeout(refreshToken, timeUntilRefresh);
  } else {
    refreshToken();
  }
}

API Client Integration

import requests
import base64
import time
from datetime import datetime, timedelta

class EdenClient:
    def __init__(self, base_url, org_id):
        self.base_url = base_url
        self.org_id = org_id
        self.token = None
        self.token_expiry = None
    
    def login(self, username, password):
        credentials = base64.b64encode(f"{username}:{password}".encode()).decode()
        headers = {
            'Authorization': f'Basic {credentials}',
            'X-Org-Id': self.org_id
        }
        
        response = requests.post(f"{self.base_url}/auth/login", headers=headers)
        if response.status_code == 200:
            data = response.json()
            self.token = data['token']
            self.token_expiry = datetime.fromisoformat(data['expires'].replace('Z', '+00:00'))
            return True
        return False
    
    def refresh_token_if_needed(self):
        # Refresh if token will expire in the next 5 minutes
        if self.token_expiry and self.token_expiry - timedelta(minutes=5) <= datetime.now(self.token_expiry.tzinfo):
            self.refresh_token()
    
    def refresh_token(self):
        headers = {'Authorization': f'Bearer {self.token}'}
        response = requests.get(f"{self.base_url}/auth/refresh", headers=headers)
        if response.status_code == 200:
            data = response.json()
            self.token = data['token']
            self.token_expiry = datetime.fromisoformat(data['expires'].replace('Z', '+00:00'))
            return True
        return False
    
    def request(self, method, endpoint, data=None):
        self.refresh_token_if_needed()
        
        headers = {'Authorization': f'Bearer {self.token}'}
        url = f"{self.base_url}/{endpoint}"
        
        if method.upper() == 'GET':
            response = requests.get(url, headers=headers)
        elif method.upper() == 'POST':
            response = requests.post(url, headers=headers, json=data)
        elif method.upper() == 'PATCH':
            response = requests.patch(url, headers=headers, json=data)
        elif method.upper() == 'DELETE':
            response = requests.delete(url, headers=headers)
        else:
            raise ValueError(f"Unsupported HTTP method: {method}")
        
        return response