## Authentication To access our APIs, authentication via Bearer tokens is required. These tokens can be generated by creating API keys through the dashboard. Once generated, signing the keys allows access to the APIs with the assigned scopes. This process ensures secure and authorized use of our services. ### Generating a JWT Bearer Token Once an API key is created, a JWT Bearer token can be generated by signing the key. This token is then used to authenticate requests to the APIs. To generate a JWT Bearer token, signing an API key using the `ES256` algorithm is required, along with the `name` and `privateKey` from the downloaded API Key JSON file. The following code snippets demonstrate how to create a JWT Bearer token in various programming languages. Javascript createAuthToken.js const { sign } = require("jsonwebtoken"); const crypto = require("crypto"); export const createAuthToken = () => { const keyName = "org/{org-id}/apiKey/{api-key-id}"; // Replace with `name` const keySecret = "-----BEGIN PRIVATE KEY-----XXXXXXXXXXXX-----END PRIVATE KEY-----"; // Replace with the `privateKey`. const serviceName = "developer-api"; const now = Math.floor(Date.now() / 1000) const payload = { aud: [serviceName], iss: "openfx", sub: keyName, iat: now, nbf: now, exp: now + 120, // Max 2 minutes allowed }; const options = { header: { alg: "ES256", kid: keyName, nonce: crypto.randomBytes(16).toString("hex"), }, }; const token = sign(payload, keySecret, options); return token; }; TypeScript createAuthToken.ts import { sign, type JwtHeader } from "jsonwebtoken"; import crypto from "node:crypto"; export const createAuthToken = (): string => { const keyName = "org/{org-id}/apiKey/{api-key-id}"; // Replace with `name` const keySecret = "-----BEGIN PRIVATE KEY-----XXXXXXXXXXXX-----END PRIVATE KEY-----"; // Replace with the `privateKey`. const serviceName = "developer-api"; const now = Math.floor(Date.now() / 1000); const payload = { aud: [serviceName], iss: "openfx", sub: keyName, iat: now, nbf: now, exp: now + 120, // Max 2 minutes allowed }; const options = { header: { alg: "ES256", kid: keyName, nonce: crypto.randomBytes(16).toString("hex"), } as JwtHeader, }; const token = sign(payload, keySecret, options); return token; }; Python create_auth_token.py import time import secrets import jwt def create_auth_token() -> str: key_name = "org/{org-id}/apiKey/{api-key-id}" # Replace with `name` key_secret = "-----BEGIN PRIVATE KEY-----XXXXXXXXXXXX-----END PRIVATE KEY-----" # Replace with the `privateKey`. service_name = "developer-api" now = int(time.time()) payload = { "aud": [service_name], "iss": "openfx", "sub": key_name, "iat": now, "nbf": now, "exp": now + 120, # 2-minute TTL } headers = { "kid": key_name, "nonce": secrets.token_hex(16), } token = jwt.encode(payload, key_secret, algorithm="ES256", headers=headers) return token Java 24 AuthTokenUtil.java import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; import com.fasterxml.jackson.databind.ObjectMapper; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.SecureRandom; import java.security.spec.PKCS8EncodedKeySpec; import java.time.Instant; import java.util.Arrays; import java.util.Base64; import java.util.Date; import java.util.HashMap; import java.util.Map; public class AuthTokenUtil { private static final String KEY_NAME = "org/{org-id}/apiKey/{api-key-id}"; // Replace with `name` private static final String KEY_SECRET = "-----BEGIN PRIVATE KEY-----XXXXXXXXXXXX-----END PRIVATE KEY-----"; // Replace with the actual `privateKey` private static final String SERVICE_NAME = "developer-api"; public static String createAuthToken() { try { long now = Instant.now().getEpochSecond(); // Generate random nonce SecureRandom random = new SecureRandom(); byte[] nonceBytes = new byte[16]; random.nextBytes(nonceBytes); String nonce = bytesToHex(nonceBytes); // Parse the private key PrivateKey privateKey = parsePrivateKey(KEY_SECRET); // Create JWT with custom header (exactly matching TypeScript structure) Map header = new HashMap<>(); header.put("kid", KEY_NAME); header.put("nonce", nonce); header.put("typ", "JWT"); // Note: alg is set via signWith() method, not in header map String token = Jwts.builder() .setHeader(header) .claim("aud", Arrays.asList(SERVICE_NAME)) .setIssuer("openfx") .setSubject(KEY_NAME) .setNotBefore(new Date(now * 1000)) .setExpiration(new Date((now + 120) * 1000)) // Max 2 minutes allowed .setIssuedAt(new Date(now * 1000)) .signWith(privateKey, SignatureAlgorithm.ES256) .compact(); return token; } catch (Exception e) { throw new RuntimeException("Failed to create auth token", e); } } private static PrivateKey parsePrivateKey(String privateKeyPem) throws Exception { String privateKeyContent = privateKeyPem .replace("-----BEGIN PRIVATE KEY-----", "") .replace("-----END PRIVATE KEY-----", "") .replaceAll("\\s", ""); byte[] keyBytes = Base64.getDecoder().decode(privateKeyContent); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("EC"); return keyFactory.generatePrivate(keySpec); } private static String bytesToHex(byte[] bytes) { StringBuilder result = new StringBuilder(); for (byte b : bytes) { result.append(String.format("%02x", b)); } return result.toString(); } } Go createAuthToken.go package utils import ( "crypto/ecdsa" "crypto/rand" "crypto/x509" "encoding/hex" "encoding/pem" "fmt" "time" "github.com/golang-jwt/jwt/v5" ) // CreateAuthToken creates a JWT token with ES256 algorithm func CreateAuthToken() (string, error) { keyName := "org/{org-id}/apiKey/{api-key-id}" // Replace with `name` keySecret := "-----BEGIN PRIVATE KEY-----XXXXXXXXXXXX-----END PRIVATE KEY-----" // Replace with the `privateKey` serviceName := "developer-api" now := time.Now().Unix() // Parse the private key privateKey, err := parsePrivateKey(keySecret) if err != nil { return "", fmt.Errorf("failed to parse private key: %w", err) } // Generate random nonce nonceBytes := make([]byte, 16) if _, err := rand.Read(nonceBytes); err != nil { return "", fmt.Errorf("failed to generate nonce: %w", err) } nonce := hex.EncodeToString(nonceBytes) // Create claims claims := jwt.MapClaims{ "aud": []string{serviceName}, "iss": "openfx", "sub": keyName, "iat": now, "nbf": now, "exp": now + 120, // 2 minutes } // Create token with custom header token := jwt.NewWithClaims(jwt.SigningMethodES256, claims) token.Header["kid"] = keyName token.Header["nonce"] = nonce // Sign the token tokenString, err := token.SignedString(privateKey) if err != nil { return "", fmt.Errorf("failed to sign token: %w", err) } return tokenString, nil } // parsePrivateKey parses a PEM-encoded ECDSA private key func parsePrivateKey(keyPEM string) (*ecdsa.PrivateKey, error) { block, _ := pem.Decode([]byte(keyPEM)) if block == nil { return nil, fmt.Errorf("failed to parse PEM block containing the key") } privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { // Try parsing as EC private key if PKCS8 fails privateKey, err = x509.ParseECPrivateKey(block.Bytes) if err != nil { return nil, fmt.Errorf("failed to parse private key: %w", err) } } ecdsaKey, ok := privateKey.(*ecdsa.PrivateKey) if !ok { return nil, fmt.Errorf("not an ECDSA private key") } return ecdsaKey, nil } Run the code to generate the JWT Bearer token. Note Retrieve the organization ID from the `orgId` parameter and the private key from the `privateKey` parameter in the JSON file downloaded during API key creation. ### Using the JWT Bearer Token Once the JWT Bearer token is generated, it can be used to authenticate requests to the APIs. To do this, the token must be included in the `Authorization` header of the requests. The following is an example of how to include the token in a request using cURL: ```bash curl -L "https://api.openfx.com/v1/" -H "Authorization: Bearer {JWT_Bearer_Token}" ``` Replace `{JWT_Bearer_Token}` with the generated token. Important - **JWT Bearer tokens** expire in **2 minutes** after generation. A new token must be generated for continued API access. - Setting longer expiry time using `exp` is not allowed. - A unique JWT must be generated for each API request; reuse of tokens is not permitted.