Authentication

FoxNose exposes two authentication options when calling the Management API: user-based JWT tokens and environment-scoped API keys. Each request must use exactly one of these methods.

Overview

MethodBest forCredentials NeededAuthorization Header
JWT (Bearer)Human operators working in the dashboard or scripts acting as a specific userEmail + password to obtain {access, refresh} tokensAuthorization: Bearer <access_token>
API Key – SecureAutomated services requiring high assurance and replay protectionAPI key pair (public/private) generated in the dashboardAuthorization: Secure <public_key>:<signature> + Date header
API Key – SimpleTrusted internal tooling and staging environments where signing is not availableSame API key pair as Secure modeAuthorization: Simple <public_key>:<private_key>

Need Flux API details? Refer to the dedicated Flux API authentication docs.

API keys are created in the FoxNose dashboard. Each key belongs to one environment and can optionally carry a role, so the key only has access to endpoints allowed by that role.


User Authentication (JWT)

JWT tokens are issued for human users after entering their email and password. Use them for day-to-day management actions where you want all requests to be performed on behalf of a user.

Token Lifetimes

  • Name
    access
    Type
    string
    Description

    Used as Authorization: Bearer <token> in subsequent API calls. Access tokens expire after 24 hours.

  • Name
    refresh
    Type
    string
    Description

    Lets you obtain a new pair of tokens without entering credentials again. Refresh tokens are valid for 7 days and are rotated on every refresh call.


POSTapi.foxnose.net/account/auth/

Obtain JWT Tokens

Submit credentials to receive the { access, refresh } pair.

Required attributes

  • Name
    email
    Type
    string
    Required
    required
    Description

    Registered email address.

  • Name
    password
    Type
    string
    Required
    required
    Description

    Account password.

Error codes

  • Name
    400
    Description

    The payload is missing required fields or has invalid data types.

  • Name
    401
    Description

    Email or password is incorrect, or the user is inactive.

Request

POST
/account/auth/
curl https://api.foxnose.net/account/auth/ \
  -H "Content-Type: application/json" \
  -d '{
    "email": "your-email@example.com",
    "password": "your-password"
  }'

Response

{
  "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

POSTapi.foxnose.net/account/refresh-token/

Refresh JWT Tokens

Exchange a refresh token for a brand-new { access, refresh } pair.

Required attributes

  • Name
    refresh
    Type
    string
    Required
    required
    Description

    Refresh token issued by the authentication endpoint or a previous refresh request.

Error codes

  • Name
    400
    Description

    Missing refresh field or invalid JSON payload.

  • Name
    401
    Description

    Refresh token expired, was already rotated, or belongs to a different user.

Request

POST
/account/refresh-token/
curl https://api.foxnose.net/account/refresh-token/ \
  -H "Content-Type: application/json" \
  -d '{
    "refresh": "your-refresh-token"
  }'

Response

{
  "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Client Examples

import axios from 'axios';

const authClient = axios.create({
  baseURL: 'https://api.foxnose.net',
  headers: { 'Content-Type': 'application/json' },
});

async function login(email, password) {
  const { data: tokens } = await authClient.post('/account/auth/', { email, password });
  const api = axios.create({
    baseURL: 'https://api.foxnose.net',
    headers: { Authorization: `Bearer ${tokens.access}` },
  });
  const folders = await api.get('/v1/7c9h4pwu/folders/');
  console.log(folders.data);

  const { data: refreshed } = await authClient.post('/account/refresh-token/', {
    refresh: tokens.refresh,
  });
  return refreshed;
}

API Key Authentication

API keys are ideal for backend services or integrations that need consistent access to a single environment. You can generate keys in the dashboard, download the Base64-encoded public/private pair, and optionally assign a role to restrict access.

There are two header formats you can choose from.


Secure Auth Method

Secure mode signs every request, preventing tampering and replay attacks. For each call:

  1. Build the signing string data_to_sign = "<path>|<sha256_hex_of_body>|<timestamp>"
    • path – exact request path, e.g., /v1/7c9h4pwu/folders/
    • body hash – SHA‑256 hex digest of the raw body (hash an empty string when there is no body)
    • timestamp – ISO 8601 UTC string, e.g., 2024-10-26T20:58:45Z
  2. Sign with your private key using ECDSA (curve SECP256R1). Encode the signature in Base64.
  3. Send headers
Authorization: Secure <public_key_base64>:<signature_base64>
Date: 2024-10-26T20:58:45Z
import base64
import hashlib
import json
from datetime import datetime, timezone
import requests
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec

PUBLIC_KEY = "your-public-key"
PRIVATE_KEY = "your-private-key"
URI = "/v1/7c9h4pwu/folders/"
body = json.dumps({"name": "Docs Folder"})

timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
body_hash = hashlib.sha256(body.encode("utf-8")).hexdigest()
data_to_sign = f"{URI}|{body_hash}|{timestamp}"

private_key = serialization.load_der_private_key(
    base64.b64decode(PRIVATE_KEY),
    password=None,
)
signature = private_key.sign(
    data_to_sign.encode("utf-8"),
    ec.ECDSA(hashes.SHA256()),
)
signature_b64 = base64.b64encode(signature).decode("utf-8")

headers = {
    "Authorization": f"Secure {PUBLIC_KEY}:{signature_b64}",
    "Date": timestamp,
    "Content-Type": "application/json",
}

response = requests.post("https://api.foxnose.net" + URI, headers=headers, data=body)
response.raise_for_status()
print(response.json())

Notes

  • The server compares the Date header to current time and rejects requests that differ by more than 15 minutes.
  • Query parameters do not appear in the signing string, but they still affect the final API response.
  • If the signature or timestamp check fails, the API responds with a standard 401 Unauthorized error.

Simple Auth Method

Simple mode is useful for secure staging environments where signing infrastructure is unavailable. It sends both keys directly in the header:

Authorization: Simple <public_key_base64>:<private_key_base64>

No Date header or signature is required. Because the private key is transmitted on every request, restrict Simple mode to trusted networks and rotate keys after use.

import axios from 'axios';

const publicKey = 'YourPublicKeyBase64';
const privateKey = 'YourPrivateKeyBase64';
const uri = '/v1/7c9h4pwu/folders/';

await axios.post(
  `https://api.foxnose.net${uri}`,
  { name: 'Docs Folder' },
  {
    headers: {
      Authorization: `Simple ${publicKey}:${privateKey}`,
      'Content-Type': 'application/json',
    },
  },
);

Security Considerations

  • Treat private keys and refresh tokens as secrets. Rotate them regularly and revoke compromised credentials immediately.
  • Keep client clocks in sync when using Secure mode. A drift larger than 15 minutes causes authentication failures.
  • Always send requests over HTTPS, especially when using Simple mode.
  • Assign the narrowest possible role when creating API keys so each key only has access to the required endpoints.
  • Store refresh tokens securely server-side (for backend services) or in encrypted storage (for native apps); never embed them in public code.

Was this page helpful?