Documentation Index
Fetch the complete documentation index at: https://noorle.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
JWT (JSON Web Tokens) are stateless, time-limited tokens for API authentication. Unlike API keys (long-lived), JWTs expire quickly and are refreshed on demand.
JWT vs API Keys
| Aspect | JWT | API Key |
|---|
| Lifespan | Short (1 hour) | Long (months/years) |
| Refresh | Auto via refresh token | Manual rotation |
| Storage | Memory (not on disk) | Secure storage |
| Stateless | Yes (server doesn’t track) | No (server tracks) |
| Use Case | APIs, web apps | Integrations, bots |
When to use JWT:
- Web/mobile apps
- Temporary access
- Cross-service authentication
- External APIs
When to use API Key:
- Server-to-server integrations
- Long-lived services
- Third-party integrations
JWT Structure
JWTs have three parts: header.payload.signature
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header │ Payload │ Signature
────────────────────────────────────
Specifies algorithm and type:
{
"alg": "HS256", // HMAC SHA-256
"typ": "JWT"
}
Payload
Contains claims (data):
{
"sub": "user-123", // subject (who)
"name": "Alice",
"email": "alice@example.com",
"iat": 1516239022, // issued at (when)
"exp": 1516242622, // expiration (when)
"aud": "api.noorle.com", // audience (for who)
"scope": "read execute" // permissions
}
Signature
Ensures token wasn’t tampered with:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret_key
)
Token Lifecycle
Step 1: Obtain Access Token
Typically via OAuth device flow or other auth method:
POST https://api.noorle.com/oauth/token
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600, // seconds until expiration
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}
Step 2: Use in API Calls
Include in Authorization header:
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
https://api.noorle.com/agents
Step 3: Token Expires
Server validates token signature and expiration:
Step 4: Refresh Token
Exchange refresh token for new access token:
POST https://api.noorle.com/oauth/token
Body:
{
"grant_type": "refresh_token",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"client_id": "noorle-cli"
}
Response:
{
"access_token": "new-eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "new-eyJhbGciOiJIUzI1NiIs...",
"expires_in": 3600
}
Token Validation
Server validates tokens before processing requests:
Example: Web App Flow
// 1. User logs in via OAuth
const authCode = await userLogin();
// 2. Get tokens
const response = await fetch("https://api.noorle.com/oauth/token", {
method: "POST",
body: JSON.stringify({
grant_type: "authorization_code",
code: authCode,
client_id: "my-web-app"
})
});
const { access_token, refresh_token, expires_in } = await response.json();
// 3. Store tokens
sessionStorage.setItem("access_token", access_token);
sessionStorage.setItem("refresh_token", refresh_token);
sessionStorage.setItem("expires_at", Date.now() + expires_in * 1000);
// 4. Make API calls
async function callAPI(endpoint) {
let token = sessionStorage.getItem("access_token");
// Check if token expired
if (Date.now() > sessionStorage.getItem("expires_at")) {
token = await refreshToken();
}
return fetch(`https://api.noorle.com${endpoint}`, {
headers: {
"Authorization": `Bearer ${token}`
}
});
}
async function refreshToken() {
const response = await fetch("https://api.noorle.com/oauth/token", {
method: "POST",
body: JSON.stringify({
grant_type: "refresh_token",
refresh_token: sessionStorage.getItem("refresh_token"),
client_id: "my-web-app"
})
});
const { access_token, refresh_token, expires_in } = await response.json();
sessionStorage.setItem("access_token", access_token);
sessionStorage.setItem("refresh_token", refresh_token);
sessionStorage.setItem("expires_at", Date.now() + expires_in * 1000);
return access_token;
}
// 5. Use in requests
const agents = await callAPI("/agents");
// Auto-refreshes token if needed
Token Claims
Standard and custom claims in the payload:
{
"iss": "https://auth.noorle.com", // issuer
"sub": "user-123", // subject (user ID)
"aud": "api.noorle.com", // audience
"iat": 1711179600, // issued at
"exp": 1711183200, // expiration
"nbf": 1711179600, // not before
"jti": "unique-token-id", // JWT ID (for revocation)
// Custom claims
"name": "Alice Johnson",
"email": "alice@example.com",
"account_id": "account-123",
"scope": "read execute",
"roles": ["developer", "user"]
}
Token Revocation
Revoke tokens before expiration if needed:
POST https://api.noorle.com/oauth/revoke
Body:
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"token_type_hint": "access_token"
}
When to revoke:
- User logs out
- Token compromised
- User permissions changed
- Service terminated
Revocation adds token to blocklist (checked on validation).
Security Best Practices
1. Never Store on Disk
// BAD: Token on disk
localStorage.setItem("token", accessToken);
// If browser storage compromised, token leaked
// GOOD: Token in memory only (lost on page reload)
window.token = accessToken;
// BETTER: Token in memory + refresh flow
// Token lost on reload, but refresh token in secure cookie
document.cookie = "refresh_token=...; HttpOnly; Secure; SameSite=Strict";
2. Use Secure HTTP Only Cookies
For refresh tokens (not access tokens):
Set-Cookie: refresh_token=eyJhb...; HttpOnly; Secure; SameSite=Strict
│ │ │
│ Not accessible Secure transport
│ from JavaScript (HTTPS only)
└─ Browser sent auto with requests
Benefits:
- Browser automatically includes with requests
- JavaScript can’t access (protects from XSS)
- Secure flag ensures HTTPS only
3. Short Expiration
Access tokens should expire quickly:
Access token: 1 hour
└─ If leaked, damage limited to 1 hour
Refresh token: 30 days
└─ Longer-lived but more protected
(in HTTP-only cookie, not JavaScript)
4. HTTPS Only
Never send JWTs over unencrypted HTTP:
HTTP → INSECURE (token in plaintext)
HTTPS → SECURE (TLS encryption)
5. Validate Signature
Always validate token signature on the server:
// BAD: Trust token without validation
const payload = JSON.parse(atob(token.split('.')[1]));
// Attacker can forge any payload
// GOOD: Validate signature
const verified = jwt.verify(token, secret);
// Only valid if signed with correct secret
Token Expiration Times
Recommended settings:
Short-lived (sensitive operations):
├─ Access token: 15 minutes
└─ Refresh token: 7 days
Standard (web apps):
├─ Access token: 1 hour
└─ Refresh token: 30 days
Long-lived (trusted apps):
├─ Access token: 8 hours
└─ Refresh token: 90 days
Troubleshooting
| Problem | Solution |
|---|
| ”invalid_token” | Token signature invalid. Token was tampered with. |
| ”expired_token” | Token expired. Use refresh_token to get new access_token. |
| ”invalid_scope” | Token doesn’t have required permission. Request new token with correct scope. |
| ”invalid_audience” | Token is for different service. Verify token and audience match. |
Next: Explore Connected Apps for third-party integrations.