ESFA Backend API Documentation Help

Machine-to-Machine Authentication

This endpoint allows you to obtain a Bearer token for machine-to-machine authentication with the ESFA Backend API. The Authentication Service is a separate service from the Backend API.

Endpoint Details

URL: POST /v1/oauth/token/machine
Service: Authentication Service (separate from Backend API)
Content-Type: application/json

Request

Headers

Content-Type: application/json

Request Body

Field

Type

Required

Description

apiKey

string

Yes

Your API key provided by the administrator

secretKey

string

Yes

Your secret key provided by the administrator

Example Request

{ "apiKey": "your-api-key-here", "secretKey": "your-secret-key-here" }

cURL Example

curl -X POST https://auth.your-service.tld/v1/oauth/token/machine \ -H "Content-Type: application/json" \ -d '{ "apiKey": "your-api-key-here", "secretKey": "your-secret-key-here" }'

Response

Success Response (200 OK)

{ "data": { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expires": "2025-09-05T13:31:23.174Z" } }

Response Fields

Field

Type

Description

data.token

string

JWT Bearer token for API authentication

data.expires

string

ISO 8601 timestamp when the token expires (UTC)

Token Usage

Use the returned token in subsequent Backend API calls:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Error Responses

400 Bad Request

Invalid request payload or malformed JSON.

{ "errors": [ { "i18N": "INVALID_REQUEST", "error": "Bad request - invalid input", "type": "BadRequest" } ] }

Common causes:

  • Missing required fields (apiKey or secretKey)

  • Invalid JSON format

  • Empty or null values

401 Unauthorized

Invalid API credentials.

{ "errors": [ { "i18N": "INVALID_CREDENTIALS", "error": "Invalid API key or secret key", "type": "Unauthorized" } ] }

Common causes:

  • Incorrect apiKey

  • Incorrect secretKey

  • Expired or revoked credentials

500 Internal Server Error

Temporary server issue.

{ "errors": [ { "i18N": "SERVER_ERROR", "error": "Internal server error", "type": "ServerError" } ] }

Recommended action: Retry the request after a brief delay with exponential backoff.

Token Management

Token Lifetime

  • Tokens are short-lived for security

  • Typical lifetime: 15 minutes to 1 hour (configured by service)

  • Check the expires field for exact expiration time

Token Refresh

  • Tokens cannot be refreshed - you must request a new token

  • Implement automatic refresh when you receive 401 Unauthorized from Backend API

  • Pre-emptively refresh tokens before expiration for better UX

Best Practices

Secure Storage

// Good: Store in memory class TokenManager { constructor() { this.token = null; this.expiresAt = null; } setToken(tokenData) { this.token = tokenData.token; this.expiresAt = new Date(tokenData.expires); } isTokenExpired() { return !this.token || new Date() >= this.expiresAt; } }

Automatic Refresh

async function getValidToken() { if (tokenManager.isTokenExpired()) { const response = await fetch('/v1/oauth/token/machine', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ apiKey: process.env.API_KEY, secretKey: process.env.SECRET_KEY }) }); if (response.ok) { const data = await response.json(); tokenManager.setToken(data.data); } } return tokenManager.token; }

Security Considerations

Credential Security

  • Never expose API keys or secret keys in client-side code

  • Store credentials in environment variables or secure configuration

  • Use different credentials for different environments

Token Security

  • Never log Bearer tokens

  • Transmit tokens only over HTTPS

  • Avoid storing tokens in persistent storage when possible

Network Security

  • Always use HTTPS in production

  • Implement proper certificate validation

  • Consider certificate pinning for high-security applications

Integration Examples

Node.js Example

const https = require('https'); async function obtainToken(apiKey, secretKey) { const data = JSON.stringify({ apiKey, secretKey }); const options = { hostname: 'auth.your-service.tld', port: 443, path: '/v1/oauth/token/machine', method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': data.length } }; return new Promise((resolve, reject) => { const req = https.request(options, (res) => { let body = ''; res.on('data', (chunk) => body += chunk); res.on('end', () => { if (res.statusCode === 200) { resolve(JSON.parse(body).data); } else { reject(new Error(`HTTP ${res.statusCode}: ${body}`)); } }); }); req.on('error', reject); req.write(data); req.end(); }); }

Python Example

import requests import os from datetime import datetime, timezone def obtain_token(): url = "https://auth.your-service.tld/v1/oauth/token/machine" payload = { "apiKey": os.getenv("API_KEY"), "secretKey": os.getenv("SECRET_KEY") } response = requests.post(url, json=payload) response.raise_for_status() data = response.json()["data"] return { "token": data["token"], "expires_at": datetime.fromisoformat(data["expires"].replace('Z', '+00:00')) } # Usage try: token_data = obtain_token() print(f"Token obtained, expires at: {token_data['expires_at']}") except requests.RequestException as e: print(f"Failed to obtain token: {e}")
05 September 2025