Public Keys + JWT

Public keys with JWT authentication enable secure, client-side access to the Enconvert API from browser environments. Because a public key is visible to end users, it cannot be used to call the API directly. Instead, it is exchanged for a short-lived JWT token that authorizes requests.

How It Works

  1. Exchange your public key (pk_) for a JWT access token by calling POST /v1/auth/token.
  2. Use the JWT token in the Authorization: Bearer <token> header on API requests.
  3. Refresh the token automatically before it expires using POST /v1/auth/refresh.
  4. Domain whitelisting ensures that only requests originating from your approved domains are accepted.

Step 1: Exchange Public Key for JWT

Send your public key and the parent origin to obtain an access token.

Endpoint

POST /v1/auth/token

Request Headers

Header Value Description
X-API-Key pk_your_public_key Your public API key

JavaScript Example

async function getToken() {
  const response = await fetch("https://api.enconvert.com/v1/auth/token", {
    method: "POST",
    headers: {
      "X-API-Key": "pk_your_public_key",
    },
    credentials: "include",
  });

  if (!response.ok) {
    throw new Error(`Token exchange failed: ${response.status}`);
  }

  const data = await response.json();
  return data.token;
}
Important: You must include credentials: "include" in the fetch options. This ensures the refresh token cookie is stored by the browser, which is required for automatic token refresh.

Response

{
  "token": "eyJhbGciOiJIUzI1NiIs..."
}

The response also sets an HttpOnly cookie containing the refresh token. This cookie is managed automatically by the browser and is used when refreshing the access token.

Step 2: Use the JWT Token

Include the JWT token in the Authorization header as a Bearer token on all subsequent API requests.

async function convertUrlToPdf(token, url) {
  const response = await fetch("https://api.enconvert.com/v1/convert/url-to-pdf", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${token}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ url }),
  });

  return await response.json();
}

// Usage
const token = await getToken();
const result = await convertUrlToPdf(token, "https://example.com");
console.log(result.presigned_url);

Step 3: Automatic Token Refresh

Access tokens expire after a short period. Use the refresh endpoint to obtain a new access token without requiring the user to re-authenticate.

Endpoint

POST /v1/auth/refresh

The refresh token is sent automatically via the HttpOnly cookie that was set during the initial token exchange. No request body or additional headers are needed.

JavaScript Auto-Refresh Example

class EnconvertClient {
  constructor(publicKey) {
    this.publicKey = publicKey;
    this.token = null;
    this.tokenExpiry = null;
  }

  async getToken() {
    const response = await fetch("https://api.enconvert.com/v1/auth/token", {
      method: "POST",
      headers: {
        "X-API-Key": this.publicKey,
        "X-Parent-Origin": window.location.origin,
      },
      credentials: "include",
    });

    const data = await response.json();
    this.token = data.token;
    // Set expiry to 55 minutes (refresh before the 1-hour expiry)
    this.tokenExpiry = Date.now() + 55 * 60 * 1000;
    return this.token;
  }

  async refreshToken() {
    const response = await fetch("https://api.enconvert.com/v1/auth/refresh", {
      method: "POST",
      credentials: "include",
    });

    if (!response.ok) {
      // Refresh token expired, re-authenticate
      return await this.getToken();
    }

    const data = await response.json();
    this.token = data.token;
    this.tokenExpiry = Date.now() + 55 * 60 * 1000;
    return this.token;
  }

  async getValidToken() {
    if (!this.token || Date.now() >= this.tokenExpiry) {
      if (this.token) {
        return await this.refreshToken();
      }
      return await this.getToken();
    }
    return this.token;
  }

  async convert(endpoint, body) {
    const token = await this.getValidToken();
    const response = await fetch(`https://api.enconvert.com${endpoint}`, {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${token}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(body),
    });

    return await response.json();
  }
}

// Usage
const client = new EnconvertClient("pk_your_public_key");
const result = await client.convert("/v1/convert/url-to-pdf", {
  url: "https://example.com",
});

Token Lifetimes

Token Lifetime Storage
Access token 1 hour Returned in the JSON response body; store in memory
Refresh token 7 days Set as an HttpOnly cookie; managed by the browser

Domain Whitelisting

Public keys are restricted to specific domains configured in your dashboard.

Matching Rules

  • Exact match: https://example.com matches only https://example.com.
  • Wildcard subdomains: https://*.example.com matches https://app.example.com, https://staging.example.com, etc.
  • Port-specific: http://localhost:3000 matches only that exact origin including the port.

Examples

Whitelisted Domain Matches Does Not Match
https://example.com https://example.com https://www.example.com
https://*.example.com https://app.example.com, https://dev.example.com https://example.com
http://localhost:3000 http://localhost:3000 http://localhost:8080

Security Features

  • Short-lived tokens: Access tokens expire after 1 hour, limiting the window of exposure if a token is compromised.
  • HttpOnly refresh cookies: Refresh tokens are stored in HttpOnly cookies, making them inaccessible to JavaScript and resistant to XSS attacks.
  • Domain restrictions: Tokens are only issued when the request originates from a whitelisted domain.
  • No direct API access: Public keys alone cannot call conversion endpoints. A valid JWT is always required.

Public Key Restrictions

Public key authentication has the following limitations compared to private keys:

  • Synchronous only: Only synchronous conversion endpoints are available. Async operations, job polling, and webhooks are not supported.
  • Single URL per request: Batch processing is not available. Each request may convert only one URL or file.
  • Direct download: Responses provide a presigned_url for immediate download. There is no option for custom storage destinations.
Best Practices:
  • Always store access tokens in memory only. Never persist them to localStorage or sessionStorage.
  • Implement automatic token refresh to avoid interruptions during user sessions.
  • Keep your whitelisted domains list as specific as possible. Avoid broad wildcards.
  • Use credentials: "include" on all fetch requests to ensure cookies are sent and received correctly.
  • Handle token refresh failures gracefully by falling back to a full re-authentication with the public key.