Manage Access Tokens

Learn how to implement automatic token management for production applications that make frequent API calls.

Why Tokens Expire

Access tokens expire after a set period (1 hour for ICEYE) as a security best practice. Short-lived tokens limit the damage if a token is compromised. If someone intercepts your token, they can only use it for a limited time before it becomes invalid.

This is standard across the API industry. Most APIs use expiring tokens to balance security with developer convenience.

Overview

For applications that run for extended periods or make frequent API calls, you need to:

  1. Track when your token expires

  2. Request a new token before the current one expires

  3. Handle token requests gracefully

Implementation

Implement a token manager that:

  1. Stores the current access token in memory

  2. Tracks the token expiration time

  3. Checks expiration before each API call

  4. Automatically requests a new token when needed (with a 5-minute buffer)

Implementation Examples

The examples below show production-ready token management in each language using standard libraries.

  • Python

  • JavaScript/Node.js

  • Java

  • PowerShell

  • Bash/cURL

import base64
import requests
from datetime import datetime, timedelta

class IceyeAuthManager:
    def __init__(self, token_url, client_id, client_secret):
        self.token_url = token_url
        self.client_id = client_id
        self.client_secret = client_secret
        # Generate BASE64_KEY
        credentials = f"{client_id}:{client_secret}"
        self.base64_key = base64.b64encode(credentials.encode()).decode()
        self.access_token = None
        self.token_expiry = None
    
    def get_token(self):
        # Request new token if it doesn't exist or expires in less than 5 minutes
        if not self.access_token or datetime.now() >= self.token_expiry - timedelta(minutes=5):
            self._request_new_token()
        return self.access_token
    
    def _request_new_token(self):
        response = requests.post(
            self.token_url,
            headers={
                'Authorization': f'Basic {self.base64_key}',
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            data={'grant_type': 'client_credentials'}
        )
        data = response.json()
        self.access_token = data['access_token']
        # Set expiry time (subtract 60 seconds as buffer)
        self.token_expiry = datetime.now() + timedelta(seconds=data['expires_in'] - 60)

# Usage
token_url = "your_token_url"
client_id = "your_client_id"
client_secret = "your_client_secret"

auth_manager = IceyeAuthManager(token_url, client_id, client_secret)

# Always get fresh token
headers = {'Authorization': f'Bearer {auth_manager.get_token()}'}

Usage:

# Initialize once
auth_manager = IceyeAuthManager(
    "your_token_url",
    "your_client_id",
    "your_client_secret"
)

# Use throughout your application
response = requests.get(
    "https://platform.iceye.com/api/company/v1/contracts",
    headers={'Authorization': f'Bearer {auth_manager.get_token()}'}
)
const axios = require('axios');

class IceyeAuthManager {
  constructor(tokenUrl, clientId, clientSecret) {
    this.tokenUrl = tokenUrl;
    this.clientId = clientId;
    this.clientSecret = clientSecret;
    // Generate BASE64_KEY
    this.base64Key = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
    this.accessToken = null;
    this.tokenExpiry = null;
  }
  
  async getToken() {
    // Request new token if it doesn't exist or expires in less than 5 minutes
    if (!this.accessToken || Date.now() >= this.tokenExpiry - 5 * 60 * 1000) {
      await this.requestNewToken();
    }
    return this.accessToken;
  }
  
  async requestNewToken() {
    const response = await axios.post(
      this.tokenUrl,
      'grant_type=client_credentials',
      {
        headers: {
          'Authorization': `Basic ${this.base64Key}`,
          'Content-Type': 'application/x-www-form-urlencoded'
        }
      }
    );
    
    this.accessToken = response.data.access_token;
    // Set expiry time (subtract 60 seconds as buffer)
    this.tokenExpiry = Date.now() + (response.data.expires_in - 60) * 1000;
  }
}

// Usage
const tokenUrl = "your_token_url";
const clientId = "your_client_id";
const clientSecret = "your_client_secret";

const authManager = new IceyeAuthManager(tokenUrl, clientId, clientSecret);

// Always get fresh token
const token = await authManager.getToken();
const headers = { 'Authorization': `Bearer ${token}` };

Usage:

// Initialize once
const authManager = new IceyeAuthManager(
    "your_token_url",
    "your_client_id",
    "your_client_secret"
);

// Use throughout your application
const token = await authManager.getToken();
const response = await axios.get(
    "https://platform.iceye.com/api/company/v1/contracts",
    { headers: { 'Authorization': `Bearer ${token}` } }
);
import java.net.http.*;
import java.net.URI;
import java.time.Instant;
import java.util.Base64;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

public class IceyeAuthManager {
    private final String tokenUrl;
    private final String base64Key;
    private String accessToken;
    private Instant tokenExpiry;
    private final HttpClient client;
    
    public IceyeAuthManager(String tokenUrl, String clientId, String clientSecret) {
        this.tokenUrl = tokenUrl;
        // Generate BASE64_KEY
        String credentials = clientId + ":" + clientSecret;
        this.base64Key = Base64.getEncoder().encodeToString(credentials.getBytes());
        this.client = HttpClient.newHttpClient();
    }
    
    public String getToken() throws Exception {
        // Request new token if it doesn't exist or expires in less than 5 minutes
        if (needsNewToken()) {
            requestNewToken();
        }
        return accessToken;
    }
    
    private boolean needsNewToken() {
        if (accessToken == null || tokenExpiry == null) {
            return true;
        }
        // Request new token if expiring within 5 minutes
        return Instant.now().plusSeconds(300).isAfter(tokenExpiry);
    }
    
    private void requestNewToken() throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(tokenUrl))
            .header("Authorization", "Basic " + base64Key)
            .header("Content-Type", "application/x-www-form-urlencoded")
            .POST(HttpRequest.BodyPublishers.ofString("grant_type=client_credentials"))
            .build();
        
        HttpResponse<String> response = client.send(request, 
            HttpResponse.BodyHandlers.ofString());
        
        JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject();
        this.accessToken = json.get("access_token").getAsString();
        int expiresIn = json.get("expires_in").getAsInt();
        // Set expiry time (subtract 60 seconds as buffer)
        this.tokenExpiry = Instant.now().plusSeconds(expiresIn - 60);
    }
    
    // Usage
    public static void main(String[] args) throws Exception {
        String tokenUrl = "your_token_url";
        String clientId = "your_client_id";
        String clientSecret = "your_client_secret";
        
        IceyeAuthManager authManager = new IceyeAuthManager(tokenUrl, clientId, clientSecret);
        
        // Always get fresh token
        String token = authManager.getToken();
        System.out.println("Access token: " + token);
    }
}

Usage:

// Initialize once
IceyeAuthManager authManager = new IceyeAuthManager(
    "your_token_url",
    "your_client_id",
    "your_client_secret"
);

// Use throughout your application
String token = authManager.getToken();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://platform.iceye.com/api/company/v1/contracts"))
    .header("Authorization", "Bearer " + token)
    .GET()
    .build();
class IceyeAuthManager {
    [string]$TokenUrl
    [string]$ClientId
    [string]$ClientSecret
    [string]$Base64Key
    [string]$AccessToken
    [datetime]$TokenExpiry

    IceyeAuthManager([string]$tokenUrl, [string]$clientId, [string]$clientSecret) {
        $this.TokenUrl = $tokenUrl
        $this.ClientId = $clientId
        $this.ClientSecret = $clientSecret
        # Generate BASE64_KEY
        $this.Base64Key = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("${clientId}:${clientSecret}"))
        $this.AccessToken = $null
        $this.TokenExpiry = [datetime]::MinValue
    }

    [string] GetToken() {
        # Request new token if it doesn't exist or expires in less than 5 minutes
        $now = Get-Date
        $bufferTime = $this.TokenExpiry.AddMinutes(-5)
        
        if (-not $this.AccessToken -or $now -ge $bufferTime) {
            $this.RequestNewToken()
        }
        return $this.AccessToken
    }

    [void] RequestNewToken() {
        $response = Invoke-RestMethod -Uri $this.TokenUrl `
            -Method Post `
            -Headers @{
                "Authorization" = "Basic $($this.Base64Key)"
                "Content-Type" = "application/x-www-form-urlencoded"
            } `
            -Body "grant_type=client_credentials"
        
        $this.AccessToken = $response.access_token
        # Set expiry time (subtract 60 seconds as buffer)
        $expiresIn = $response.expires_in - 60
        $this.TokenExpiry = (Get-Date).AddSeconds($expiresIn)
    }
}

# Usage
$tokenUrl = "your_token_url"
$clientId = "your_client_id"
$clientSecret = "your_client_secret"

$authManager = [IceyeAuthManager]::new($tokenUrl, $clientId, $clientSecret)

# Always get fresh token
$headers = @{
    "Authorization" = "Bearer $($authManager.GetToken())"
}

Usage:

# Initialize once
$authManager = [IceyeAuthManager]::new(
    "your_token_url",
    "your_client_id",
    "your_client_secret"
)

# Use throughout your script
$response = Invoke-RestMethod -Uri "https://platform.iceye.com/api/company/v1/contracts" `
    -Headers @{ "Authorization" = "Bearer $($authManager.GetToken())" }
#!/bin/bash
# Simple token management with expiry checking

TOKEN_FILE="/tmp/iceye_token.json"
TOKEN_EXPIRY_FILE="/tmp/iceye_token_expiry"

get_token() {
    # Check if token exists and is still valid
    if [ -f "$TOKEN_EXPIRY_FILE" ]; then
        EXPIRY=$(cat "$TOKEN_EXPIRY_FILE")
        NOW=$(date +%s)
        # Refresh if expiring in less than 5 minutes (300 seconds)
        if [ $((EXPIRY - NOW)) -gt 300 ]; then
            grep -o '"access_token":"[^"]*"' "$TOKEN_FILE" | cut -d'"' -f4
            return
        fi
    fi
    
    # Request new token
    RESPONSE=$(curl --silent --request POST \
      --url "${TOKEN_URL}" \
      --header "Authorization: Basic ${BASE64_KEY}" \
      --header "Content-Type: application/x-www-form-urlencoded" \
      --data "grant_type=client_credentials")
    
    # Save token and calculate expiry time
    echo "$RESPONSE" > "$TOKEN_FILE"
    EXPIRES_IN=$(echo "$RESPONSE" | grep -o '"expires_in":[0-9]*' | cut -d':' -f2)
    EXPIRY_TIME=$(($(date +%s) + EXPIRES_IN - 60))
    echo "$EXPIRY_TIME" > "$TOKEN_EXPIRY_FILE"
    
    echo "$RESPONSE" | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4
}

# Usage
ACCESS_TOKEN=$(get_token)
echo "Access token: $ACCESS_TOKEN"

Usage:

# Source the functions
source token-manager-curl.sh

# Use throughout your script
TOKEN=$(get_valid_token)
curl --request GET \
  --url "https://platform.iceye.com/api/company/v1/contracts" \
  --header "Authorization: Bearer ${TOKEN}"

Key Concepts

5-Minute Buffer

Request a new token when the current one expires in less than 5 minutes. This prevents race conditions where the token expires between checking and using it.

In-Memory Storage

Store access tokens in memory or a shared cache for distributed systems. Persistent storage (files, databases) is unnecessary since tokens expire after 1 hour.

No Refresh Tokens

The Client Credentials flow doesn’t use refresh tokens. Simply request a new access token when needed using your CLIENT_ID and CLIENT_SECRET.

Error Handling

If you receive a 401 Unauthorized error, your token has expired. Request a new token and retry the request.

Best Practices

Initialize Once

Create your token manager once when your application starts, not for every API call.

Reuse Tokens

Don’t request a new token for every API call. Check expiration and reuse valid tokens.

Handle Failures

Implement retry logic for token requests in case of network issues.

Thread Safety

If your application is multi-threaded, ensure token requests are synchronized to avoid duplicate requests.