---
title: "JWT Tokens"
description: "Use JSON Web Tokens for stateless authentication with TraceRTM"
keywords: [jwt, json web tokens, stateless, authentication, tokens]
---

# JWT Tokens

JSON Web Tokens (JWT) provide stateless authentication for distributed systems, microservices, and applications requiring token-based access.

## Overview

JWT tokens are:
- **Self-contained**: Include user claims and permissions
- **Stateless**: No server-side session storage required
- **Verifiable**: Cryptographically signed for integrity
- **Portable**: Can be used across services

## JWT Structure

A JWT consists of three parts separated by dots:

```
header.payload.signature
```

### Header
```json
{
  "alg": "RS256",
  "typ": "JWT"
}
```

### Payload (Claims)
```json
{
  "sub": "user_123",
  "role": "admin",
  "scope": "read write",
  "exp": 1704067200,
  "iat": 1704063600
}
```

### Signature
Cryptographic signature ensuring token integrity.

## Getting JWT Tokens

### From OAuth 2.0 Flow

JWT tokens are issued through the OAuth 2.0 token endpoint:

```bash
curl -X POST "https://api.tracertm.dev/oauth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=auth_code_123..." \
  -d "client_id=client_abc123..." \
  -d "client_secret=secret_xyz789..."
```

Response includes JWT access token:
```json
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 3600,
  "refresh_token": "refresh_token_abc123..."
}
```

### Direct Token Generation (Service Accounts)

For service accounts, generate tokens directly:

```bash
curl -X POST "https://api.tracertm.dev/api/auth/token" \
  -H "Content-Type: application/json" \
  -d '{
    "service_account_id": "svc_abc123...",
    "service_account_secret": "secret_xyz789..."
  }'
```

## Using JWT Tokens

Include the JWT in the `Authorization` header:

```bash
curl -X GET "https://api.tracertm.dev/v1/projects" \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
  -H "Content-Type: application/json"
```

## Token Claims

JWT tokens contain standard and custom claims:

### Standard Claims

| Claim | Description |
|-------|-------------|
| `sub` | Subject (user ID) |
| `exp` | Expiration time (Unix timestamp) |
| `iat` | Issued at time |
| `iss` | Issuer (TraceRTM) |
| `aud` | Audience |

### Custom Claims

| Claim | Description |
|-------|-------------|
| `role` | User role (admin, user, guest) |
| `scope` | Permission scopes |
| `project_id` | Associated project (if scoped) |

## Decoding JWT Tokens

### Python

```python
import jwt

# Decode without verification (for inspection only)
decoded = jwt.decode(token, options={"verify_signature": False})
print(decoded)

# Decode with verification
decoded = jwt.decode(
    token,
    public_key,
    algorithms=["RS256"],
    audience="tracertm"
)
```

### JavaScript

```javascript
const jwt = require('jsonwebtoken');

// Decode without verification
const decoded = jwt.decode(token);
console.log(decoded);

// Verify and decode
const decoded = jwt.verify(token, publicKey, {
  algorithms: ['RS256'],
  audience: 'tracertm'
});
```

## Token Refresh

JWT access tokens expire after 1 hour. Use refresh tokens to obtain new access tokens:

```bash
curl -X POST "https://api.tracertm.dev/api/auth/refresh" \
  -H "Content-Type: application/json" \
  -d '{
    "refresh_token": "refresh_token_abc123..."
  }'
```

**Response**:
```json
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 3600
}
```

## Token Validation

The API validates JWT tokens on every request:

1. **Signature verification**: Ensures token wasn't tampered with
2. **Expiration check**: Verifies token hasn't expired
3. **Issuer validation**: Confirms token was issued by TraceRTM
4. **Audience check**: Validates intended recipient

## Implementation Examples

### Python

```python
import jwt
import requests
from datetime import datetime, timedelta

class TraceRTMClient:
    def __init__(self, access_token, refresh_token=None):
        self.access_token = access_token
        self.refresh_token = refresh_token
        self.base_url = "https://api.tracertm.dev"
    
    def _is_token_expired(self):
        try:
            decoded = jwt.decode(
                self.access_token,
                options={"verify_signature": False}
            )
            exp = decoded.get('exp', 0)
            return datetime.now().timestamp() > exp
        except:
            return True
    
    def _refresh_token(self):
        if not self.refresh_token:
            raise ValueError("No refresh token available")
        
        response = requests.post(
            f"{self.base_url}/api/auth/refresh",
            json={"refresh_token": self.refresh_token}
        )
        data = response.json()
        self.access_token = data['access_token']
        return self.access_token
    
    def _get_valid_token(self):
        if self._is_token_expired():
            self._refresh_token()
        return self.access_token
    
    def get_projects(self):
        token = self._get_valid_token()
        headers = {
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json"
        }
        response = requests.get(
            f"{self.base_url}/v1/projects",
            headers=headers
        )
        return response.json()
```

### JavaScript

```javascript
const jwt = require('jsonwebtoken');
const axios = require('axios');

class TraceRTMClient {
  constructor(accessToken, refreshToken) {
    this.accessToken = accessToken;
    this.refreshToken = refreshToken;
    this.baseUrl = 'https://api.tracertm.dev';
  }
  
  isTokenExpired() {
    try {
      const decoded = jwt.decode(this.accessToken);
      const exp = decoded?.exp || 0;
      return Date.now() / 1000 > exp;
    } catch {
      return true;
    }
  }
  
  async refreshToken() {
    if (!this.refreshToken) {
      throw new Error('No refresh token available');
    }
    
    const response = await axios.post(
      `${this.baseUrl}/api/auth/refresh`,
      { refresh_token: this.refreshToken }
    );
    
    this.accessToken = response.data.access_token;
    return this.accessToken;
  }
  
  async getValidToken() {
    if (this.isTokenExpired()) {
      await this.refreshToken();
    }
    return this.accessToken;
  }
  
  async getProjects() {
    const token = await this.getValidToken();
    const response = await axios.get(
      `${this.baseUrl}/v1/projects`,
      {
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json'
        }
      }
    );
    return response.data;
  }
}
```

## Security Best Practices

### 1. Verify Token Signature
Always verify JWT signature before trusting claims:

```python
# ✅ Good: Verify signature
decoded = jwt.decode(token, public_key, algorithms=["RS256"])

# ❌ Bad: Decode without verification
decoded = jwt.decode(token, verify=False)
```

### 2. Check Expiration
Always validate token expiration:

```python
exp = decoded.get('exp', 0)
if datetime.now().timestamp() > exp:
    raise ValueError("Token expired")
```

### 3. Validate Audience
Ensure token is intended for your application:

```python
decoded = jwt.decode(
    token,
    public_key,
    algorithms=["RS256"],
    audience="your-app-id"
)
```

### 4. Store Securely
- Never log tokens
- Use HTTPS for all token exchanges
- Store refresh tokens securely

### 5. Handle Token Refresh
Implement automatic token refresh before expiration:

```python
# Refresh when 5 minutes before expiration
if exp - datetime.now().timestamp() < 300:
    refresh_token()
```

## Error Handling

### Invalid Token

```json
{
  "error": {
    "code": "invalid_token",
    "message": "Invalid or malformed token"
  }
}
```

### Expired Token

```json
{
  "error": {
    "code": "token_expired",
    "message": "Token has expired",
    "refresh_url": "/api/auth/refresh"
  }
}
```

### Insufficient Permissions

```json
{
  "error": {
    "code": "insufficient_permissions",
    "message": "Token does not have required permissions"
  }
}
```

## Token Lifetime

- **Access tokens**: 1 hour
- **Refresh tokens**: 90 days (if unused)
- **Service account tokens**: Configurable (default 24 hours)

## Next Steps

- [Scopes](./05-scopes) - Understand permission scopes
- [Rate Limiting](./06-rate-limiting) - API rate limits
- [Best Practices](./07-best-practices) - Security guidelines
