Authentication
FoxNose provides multiple authentication methods to securely access both the Management API and the Flux API. Understanding these methods and choosing the appropriate one for your use case is crucial for maintaining security and efficient access control.
Overview
In FoxNose, authentication is secured through JWT tokens and asymmetric encryption with API keys, each serving distinct purposes.
JWT tokens are primarily for user-based authentication within the Management API. Users authenticate directly using their credentials, receiving short-lived access tokens for routine interactions and refresh tokens for extended access. This method grants user-specific permissions, making it ideal for administrative tasks requiring comprehensive control.
API Key Authentication uses asymmetric encryption and allows external services to interact with both the Management API and Flux API. With each API key, a unique public-private key pair is generated. The private key, used only for signing requests, remains secure and is never shared, ensuring data integrity and authenticity. API keys are linked to specific roles with precise permissions, providing flexible, limited access suitable for service-based interactions.
Key Points
API Type | Authentication Methods |
---|---|
Management API | • JWT Authentication (Bearer Token) • API Key Authentication (Secure/Simple) |
Flux API | • API Key Authentication (Secure/Simple) |
User Authentication (JWT)
Authenticates on behalf of a user using JSON Web Tokens. Suitable for administrative tasks requiring broader permissions. To authenticate as a user, you need to obtain an access token and a refresh token by providing your user credentials.
Token Rotation
For security purposes, JWT tokens are rotated on each authentication. This means that each time you authenticate, you receive new access and refresh tokens. Ensure that you update stored tokens accordingly.
Properties
- Name
access
- Type
- string
- Description
The access token used for authenticating requests to the API. This token is included in the
Authorization
header with the prefixBearer
. The access token grants temporary access to the API based on the user's permissions.Expiration: Access tokens are short-lived and expire after 10 minutes.
- Name
refresh
- Type
- string
- Description
The refresh token allows for obtaining a new access token once the current one expires. It is sent to the Refresh JWT tokens endpoint to generate fresh
access
andrefresh
tokens without requiring re-authentication.Expiration: Refresh tokens are valid for 7 days.
Obtaining JWT Tokens
This endpoint allows users to authenticate by providing their email and password, returning access and refresh tokens for further API usage.
Required attributes
- Name
email
- Type
- string
- Required
- required
- Description
Email address of the user.
- Name
password
- Type
- string
- Required
- required
- Description
Password of the user.
Errors codes
- Name
401
- Description
Invalid credentials.
- Name
400
- Description
One or more required attributes are missing. See
detail
field for more information.
Request
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..."
}
Refresh JWT Tokens
This endpoint allows users to refresh their access token using an existing refresh token. This request generates a new access token and, if the refresh token is still valid, a new refresh token as well.
Required attributes
- Name
refresh
- Type
- string
- Required
- required
- Description
The refresh token that will be used to generate a new access token.
Errors codes
- Name
401
- Description
The refresh token is invalid, expired, or revoked.
- Name
400
- Description
The required
refresh
attribute is missing.
Request
curl https://api.foxnose.net/account/refresh-token/ \
-H "Content-Type: application/json" \
-d '{
"refresh": "your-refresh-token"
}'
Response
{
"access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Code Examples
const axios = require('axios');
// Authenticate and obtain tokens
axios.post('https://api.foxnose.net/account/auth/', {
email: 'your-email@example.com',
password: 'your-password'
})
.then(response => {
const accessToken = response.data.access;
const refreshToken = response.data.refresh;
// Use the access token in your requests
const headers = {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json'
};
// Make an authenticated request
axios.get('https://api.foxnose.net/secure-endpoint/', {headers})
.then(res => {
console.log(res.status);
console.log(res.data);
})
.catch(err => {
console.error(err.response.status);
console.error(err.response.data);
});
})
.catch(error => {
console.error (error.response.status);
console.error(error.response.data);
});
API Key Authentication
FoxNose uses a secure system for accessing both the Management API and the Flux API through API keys, leveraging asymmetric encryption. This ensures a high level of security by keeping your private keys confidential and not storing them on our servers. API keys are generated through the Dashboard of your project.
When you create a new key, you receive:
- Public Key: Used to identify your API key. Can be shared openly and is used to verify signatures.
- Private Key: Used to sign your requests. Must be kept secret and is used to create digital signatures.
Both keys are already provided in Base64 encoding. No need to encode them again. The private key is displayed only once during creation. Ensure you store it securely, as it cannot be recovered later. You can assign a role to the API key. If you do not assign a role during creation, you can add or change it later.
"Secure" Auth Method
When making a request to the Management API or Flux API, you need to:
- Prepare the Data to Sign:
- URI: The path of the request including API version (
/v1/
) and without the hostname, e.g./v1/7c9h4pwu/folders/
. - Body Hash: SHA-256 hash of the request body. If there's no body, use the SHA-256 hash of an empty string ('').
- Timestamp: Current time in ISO 8601 format (e.g., 2024-10-26T20:58:45Z).
Concatenate these elements using a pipe (|) character:
data_to_sign = "<uri>|<body_hash>|<timestamp>"
- Create the Signature:
- Use your private key to sign the
data_to_sign
string using ECDSA with SHA-256.
- Set the Headers:
- Authorization: Include the authentication method (
Secure
), your public key, and the signature in Base64, separated by colons.
Authorization: Secure <public_key>:<signature>
- Date: Include the same timestamp used in
data_to_sign
.
Date: 2024-10-26T20:58:45Z
Code Examples
import base64
import hashlib
import datetime
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
public_key = 'your-public-key'
secret_key = 'your-private-key'
uri = 'your-uri'
body_str = 'your-body'
private_key_der = base64.b64decode(secret_key)
private_key = load_der_private_key(
private_key_der,
password=None,
backend=default_backend()
)
body_hash = hashlib.sha256(body_str.encode('utf-8')).hexdigest()
timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
data_to_sign = f"{uri}|{body_hash}|{timestamp}"
signature = private_key.sign(
data_to_sign.encode('utf-8'),
ec.ECDSA(hashes.SHA256())
)
signature_base64 = base64.b64encode(signature).decode('utf-8')
result = {
'authorization': f'Secure {public_key}:{signature_base64}',
'date': timestamp,
'uri': uri,
'bodyHash': body_hash,
'dataToSign': data_to_sign,
'signature': signature_base64
}
Important Notes
- Time Synchronization: Ensure that your system clock is accurate. If the timestamp in the Date header differs by more than 10 minutes from the server's time, the request will be rejected.
- Timestamp Consistency: The timestamp used in the Date header must be the same as the one used in the signature.
- URI: Use the path of the request without the domain.
- Body Hash: Calculate the SHA-256 hash of the request body. If there is no body, use the SHA-256 hash of an empty string.
body_hash = hashlib.sha256(''.encode('utf-8')).hexdigest()
"Simple" Auth Method
Alternatively, you can use a simplified authentication method, which doesn't require signature and the Date header.
In the Authorization header, include the authentication method (Simple
), your public key, and your private key, separated by colons.
Authorization: Simple <public_key>:<private_key>
const axios = require('axios');
// Your keys (Base64 encoded)
const publicKeyBase64 = 'YourPublicKeyBase64';
const privateKeyBase64 = 'YourPrivateKeyBase64';
// Request details
const uri = '/v1/7c9h4pwu/folders/';
const body = JSON.stringify({name: 'New Folder'});
// Prepare headers
const headers = {
Authorization: `Simple ${publicKeyBase64}:${privateKeyBase64}`,
'Content-Type': 'application/json'
};
// Make request
axios
.post(`https://api.foxnose.net${uri}`, body, {headers})
.then((response) => {
console.log (response.status);
console.log(response.data);
})
.catch((error) => {
console.error (error.response.status);
console.error(error.response.data);
});
Security Considerations
When working with authentication methods in FoxNose CMS, consider the following security best practices:
- Store your private keys securely. Do not share them or expose them in code repositories.
- Keep access and refresh tokens confidential. Do not log them or expose them to unauthorized parties.
- Always use HTTPS to ensure that your requests and credentials are encrypted during transmission.
- Ensure that your system clock is accurate to prevent authentication failures due to timestamp discrepancies.
- Be aware of token expiration times and refresh tokens as needed.
- Regularly rotate your API keys and tokens to minimize the risk of compromised credentials.
- Assign the minimal necessary permissions to API keys and users, following the principle of least privilege.