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
X-Parent-Origin https://yourdomain.com The origin of the parent page embedding the widget or making the request. Must match a whitelisted domain.

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",
      "X-Parent-Origin": window.location.origin,
    },
    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. The X-Parent-Origin header sent during the token exchange must match one of your whitelisted domains.

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.
  • Origin validation: The API validates both the Origin and X-Parent-Origin headers to prevent unauthorized cross-origin requests.
  • 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.

Verify Authentication

Use the verify endpoint to check whether your current authentication (private key or JWT) is valid.

Endpoint

GET /v1/auth/verify

Example

curl https://api.enconvert.com/v1/auth/verify \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Response (Valid)

{
  "authenticated": true,
  "key_type": "public",
  "user_id": "12345"
}

Response (Invalid)

{
  "authenticated": false,
  "error": "Token expired or invalid"
}
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.