Search

 

JWT Authentication Best Practices

In our IoT Accelerator REST API, enterprise customers authenticate by requesting a JSON Web Token (JWT) from the /iot/api/auth endpoint. This token grants them access to subsequent API calls. However, we have observed that some clients call the authentication endpoint before every request, which is unnecessary and can create performance bottlenecks and may contribute towards unnecessary consumption of rate-limited requests.

This guide explores how to use the token's expiration information to determine when a token is still valid, and when you actually need to request a new one — improving efficiency and reducing authentication load.

Tip: If you are also looking to optimize how you stay within API usage limits, see our Best Practices for Avoiding Rate Limiting in API Requests article.

How JWT Authentication Works

  1. Your client authenticates once by calling POST /iot/api/auth with valid credentials.
  2. The API responds with a JWT string, typically in the access_token field of the JSON response.
  3. The token contains claims (metadata), including an exp field indicating the expiry time in epoch seconds.
  4. You include this token in the Authorization: Bearer <token> header of subsequent API requests until it expires.

Understanding the exp Claim

JWTs are composed of three base64-encoded parts: header, payload, and signature. The payload contains standard claims, including exp — the UNIX timestamp (in seconds) when the token expires.

Below is an anonymized example showing the structure you can expect in a token payload:

{
  "sub": "user@example.com",
  "iss": "https://auth.example.com",
  "last_name": "Doe",
  "groups": [
    "api_read",
    "api_write",
    "subscription_admin"
  ],
  "organization_ids": {
    "default": {
      "include": [
        "123",
        "123.*"
      ]
    }
  },
  "aud": "api-client",
  "upn": "user@example.com",
  "azp": "user@example.com",
  "enterprise_group_ids": {
    "default": {
      "include": [
        "123",
        "123.*"
      ]
    }
  },
  "exp": 1758908748,
  "billing_organization_id": "123",
  "iat": 1758906948,
  "first_name": "John",
  "jti": "uuid-here-1234",
  "email": "user@example.com"
}

By checking the current time against exp, you can determine whether the token is still valid and avoid unnecessary authentication requests.

Best Practices for JWT Token Management

  1. Authenticate Once and Reuse the Token: Store the token securely in memory or a secure cache, and reuse it until it expires.
  2. Check Expiry Before Each API Call: Compare the current timestamp to the token's exp value.
  3. Refresh Proactively: Renew the token shortly before expiry (e.g., 1-2 minutes earlier) to avoid race conditions and failed requests.
  4. Handle Expired Tokens Gracefully: If an API request fails with HTTP 401 Unauthorized due to token expiry, immediately request a new token and retry the call.

Example: Decoding and Using exp in Python

This example uses the pyjwt package to decode the JWT without verifying the signature (since you only need the claim values).

import time
import requests
import jwt  # pip install pyjwt

AUTH_URL = 'https://iot-api.aeris.com/iot/api/auth'
API_URL = 'https://iot-api.aeris.com/iot/api/data'
USERNAME = 'your_username'
PASSWORD = 'your_password'

token_data = None

def get_token():
    global token_data
    resp = requests.post(AUTH_URL, json={'username': USERNAME, 'password': PASSWORD})
    resp.raise_for_status()
    token = resp.json()['access_token']
    payload = jwt.decode(token, options={"verify_signature": False})
    token_data = {
        'token': token,
        'exp': payload['exp']
    }
    print(f"New token acquired, expires at {time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(token_data['exp']))}")

def ensure_token():
    global token_data
    now = int(time.time())
    if not token_data or token_data['exp'] - now <120: 2="2" no="No" token="ensure_token()" or="or" expires="expires" in="in" under="under" minutes="minutes" return="return" def="def" headers="headers)" authorization="Authorization" f="f" bearer="Bearer" resp="requests.get(API_URL," if="if" __name__="=" __main__="__main__">

Example: Decoding and Using exp in JavaScript

This example uses the jwt-decode library to inspect the token and check expiry.

const axios = require('axios');
const jwt_decode = require('jwt-decode'); // npm install jwt-decode

const AUTH_URL = 'https://iot-api.aeris.com/iot/api/auth';
const API_URL = 'https://iot-api.aeris.com/iot/api/data';
const USERNAME = 'your_username';
const PASSWORD = 'your_password';

let tokenData = null;

async function getToken() {
    const resp = await axios.post(AUTH_URL, { username: USERNAME, password: PASSWORD });
    const token = resp.data.access_token;
    const payload = jwt_decode(token);
    tokenData = {
        token,
        exp: payload.exp
    };
    console.log(`New token acquired, expires at ${new Date(tokenData.exp * 1000).toISOString()}`);
}

async function ensureToken() {
    const now = Math.floor(Date.now() / 1000);
    if (!tokenData || tokenData.exp - now <120) await="await" return="return" async="async" function="function" const="const" token="await" resp="await">

Conclusion

Instead of calling /iot/api/auth before every request, read the exp claim of your JWT token to determine if it's still valid. By checking and reusing your token until it is near expiry, you reduce unnecessary network calls, improve performance, and minimize authentication load on the server.

Implementing token caching and proactive renewal is a best practice that will make your integration with the IoT Accelerator REST API more efficient and responsive.

This approach also complements our guidance on avoiding rate limiting. See our Best Practices for Avoiding Rate Limiting in API Requests for strategies that work alongside token optimization.

Was this article helpful?
0 out of 0 found this helpful
Have more questions? Submit a request

0 Comments

Please sign in to leave a comment.