Working with Tokens link
Once you have an access token, learn how to inspect, validate, refresh, and use it effectively with APIs.
Quick Reference link
| Task | Command |
|---|
| Inspect token | entratool inspect -t TOKEN |
| Inspect from file | entratool inspect -f token.txt |
| Discover token info | entratool discover -t TOKEN |
| Refresh token | entratool refresh -p PROFILE |
| Check expiration | entratool discover -t TOKEN | jq .exp |
Token Inspection link
Decode JWT Claims link
1
| entratool inspect -t "eyJ0eXAiOiJKV1QiLCJh..."
|
Output:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| {
"header": {
"typ": "JWT",
"alg": "RS256",
"kid": "abc123..."
},
"payload": {
"aud": "https://graph.microsoft.com",
"iss": "https://sts.windows.net/12345678-1234-1234-1234-123456789abc/",
"iat": 1701432000,
"nbf": 1701432000,
"exp": 1701435600,
"appid": "12345678-1234-1234-1234-123456789abc",
"appidacr": "1",
"idp": "https://sts.windows.net/...",
"oid": "87654321-4321-4321-4321-cba987654321",
"rh": "...",
"sub": "...",
"tid": "12345678-1234-1234-1234-123456789abc",
"uti": "...",
"ver": "1.0",
"scp": "User.Read Mail.Read",
"roles": []
},
"signature": "..."
}
|
Inspect from File link
1
2
| entratool get-token -p myprofile --silent > token.txt
entratool inspect -f token.txt
|
Inspect from Pipeline link
1
| entratool get-token -p myprofile --silent | entratool inspect
|
Detailed inspection guide →
Understanding Token Claims link
Essential Claims link
| Claim | Description | Example |
|---|
aud | Audience (API) | https://graph.microsoft.com |
iss | Issuer (Entra ID) | https://sts.windows.net/... |
iat | Issued at (Unix timestamp) | 1701432000 |
exp | Expiration (Unix timestamp) | 1701435600 |
appid | Application ID | 12345678-... |
tid | Tenant ID | 87654321-... |
Permission Claims link
Delegated permissions (user context):
1
2
3
| {
"scp": "User.Read Mail.Read Calendars.Read"
}
|
Application permissions (app context):
1
2
3
| {
"roles": ["User.Read.All", "Mail.Send"]
}
|
User Claims link
1
2
3
4
5
6
| {
"unique_name": "user@contoso.com",
"upn": "user@contoso.com",
"name": "John Doe",
"oid": "87654321-4321-4321-4321-cba987654321"
}
|
Token Discovery link
Quick Token Info link
1
| entratool discover -t "eyJ0eXAiOiJKV1QiLCJh..."
|
Output:
Token Information:
Audience: https://graph.microsoft.com
Issued: 2024-12-01 10:00:00 UTC
Expires: 2024-12-01 11:00:00 UTC
Time remaining: 45 minutes
Scopes: User.Read Mail.Read
Application ID: 12345678-1234-1234-1234-123456789abc
Tenant ID: 87654321-4321-4321-4321-cba987654321
Check if Token is Valid link
1
| entratool discover -t "eyJ0eXAiOiJKV1QiLCJh..." && echo "Valid" || echo "Expired"
|
Exit codes:
0: Token is valid1: Token is expired or invalid
Expiration Check link
1
2
3
4
5
6
| # Get expiration timestamp
EXP=$(entratool inspect -t "$TOKEN" | jq -r .payload.exp)
# Convert to readable date
date -r $EXP # macOS
date -d @$EXP # Linux
|
Detailed discovery guide →
Token Refresh link
When to Refresh link
Refresh token when:
- Token is expired or about to expire
- You need a fresh token with updated permissions
Check expiration:
1
| entratool discover -t "$TOKEN"
|
Refresh Command link
1
| entratool refresh -p myprofile
|
Requirements:
- Original token acquired with
offline_access scope - Refresh token available in cache
Output:
Refreshing token for profile 'myprofile'...
✓ Token refreshed successfully
eyJ0eXAiOiJKV1QiLCJh...
Refresh vs New Token link
| Operation | Refresh | New Token |
|---|
| Speed | Faster | Slower |
| User interaction | None | May require |
| Requirements | Refresh token | Profile + credentials |
| Use case | Token expired | First request or refresh unavailable |
When refresh isn’t available:
- Client Credentials flow (no refresh tokens)
- Refresh token expired (>90 days of inactivity)
Detailed refresh guide →
Using Tokens with APIs link
Microsoft Graph API link
1
2
3
4
| TOKEN=$(entratool get-token -p graph-profile --silent)
curl -H "Authorization: Bearer $TOKEN" \
https://graph.microsoft.com/v1.0/me
|
Example response:
1
2
3
4
5
6
7
| {
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
"id": "87654321-4321-4321-4321-cba987654321",
"displayName": "John Doe",
"mail": "john.doe@contoso.com",
"userPrincipalName": "john.doe@contoso.com"
}
|
Azure Resource Manager link
1
2
3
4
| TOKEN=$(entratool get-token -p azure-profile --silent)
curl -H "Authorization: Bearer $TOKEN" \
"https://management.azure.com/subscriptions?api-version=2020-01-01"
|
Custom APIs link
1
2
3
4
5
6
| TOKEN=$(entratool get-token -p custom-api --silent)
curl -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"key": "value"}' \
https://api.contoso.com/v1/resources
|
Token Validation link
Client-Side Validation link
Check expiration:
1
2
3
4
5
6
7
8
9
10
| # Extract exp claim
EXP=$(entratool inspect -t "$TOKEN" | jq -r .payload.exp)
# Compare with current time
NOW=$(date +%s)
if [ $EXP -lt $NOW ]; then
echo "Token expired"
else
echo "Token valid for $(( ($EXP - $NOW) / 60 )) minutes"
fi
|
Verify audience:
1
2
3
4
5
6
| AUD=$(entratool inspect -t "$TOKEN" | jq -r .payload.aud)
if [ "$AUD" = "https://graph.microsoft.com" ]; then
echo "Valid for Graph API"
else
echo "Wrong audience: $AUD"
fi
|
Server-Side Validation link
APIs validate tokens by:
- Verifying signature using Microsoft’s public keys
- Checking expiration (
exp claim) - Validating audience (
aud claim) - Checking issuer (
iss claim)
Microsoft Graph validates:
- Token signature
- Expiration time
- Audience =
https://graph.microsoft.com - Issuer from trusted Entra ID tenant
Token Expiration link
Typical Lifetimes link
| Token Type | Lifetime |
|---|
| Access token | 1 hour |
| Refresh token | 90 days (or until revoked) |
| ID token | 1 hour |
Handling Expiration link
Pattern 1: Check before use
1
2
3
4
5
6
| if entratool discover -f token.txt &>/dev/null; then
TOKEN=$(cat token.txt)
else
TOKEN=$(entratool get-token -p myprofile --silent)
echo "$TOKEN" > token.txt
fi
|
Pattern 2: Catch API errors
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| TOKEN=$(cat token.txt)
RESPONSE=$(curl -s -w "%{http_code}" \
-H "Authorization: Bearer $TOKEN" \
https://graph.microsoft.com/v1.0/me)
if [[ "$RESPONSE" == *"401"* ]]; then
# Token expired, get new one
TOKEN=$(entratool get-token -p myprofile --silent)
echo "$TOKEN" > token.txt
# Retry request
curl -H "Authorization: Bearer $TOKEN" \
https://graph.microsoft.com/v1.0/me
fi
|
Pattern 3: Preemptive refresh
1
2
3
4
5
6
7
8
| # Refresh if less than 5 minutes remaining
EXP=$(entratool inspect -f token.txt | jq -r .payload.exp)
NOW=$(date +%s)
REMAINING=$(( ($EXP - $NOW) / 60 ))
if [ $REMAINING -lt 5 ]; then
entratool get-token -p myprofile --silent > token.txt
fi
|
Detailed expiration guide →
Token Storage link
Temporary Storage link
1
2
3
4
5
6
| # In-memory (session only)
export TOKEN=$(entratool get-token -p myprofile --silent)
# File (with restricted permissions)
entratool get-token -p myprofile --silent > /tmp/token.txt
chmod 600 /tmp/token.txt
|
Cache Location link
MSAL token cache:
- Windows:
%LOCALAPPDATA%\.msal\cache - macOS/Linux:
~/.msal/cache
Contains:
- Access tokens
- Refresh tokens
- ID tokens
- Account information
Security:
- Encrypted on Windows (DPAPI)
- Encrypted on macOS (Keychain)
- ⚠️ Obfuscated on Linux (not secure)
Clearing Cache link
1
2
3
4
5
| # Delete cache directory
rm -rf ~/.msal/cache
# Next token request will require re-authentication
entratool get-token -p myprofile
|
Token Security link
✅ Best Practices link
Do:
- ✅ Request tokens on-demand
- ✅ Use HTTPS for all API requests
- ✅ Check token expiration before use
- ✅ Store tokens with restrictive permissions (600)
- ✅ Clear tokens after use
- ✅ Use environment variables for temporary storage
- ✅ Rotate credentials regularly
Don’t:
- ❌ Hard-code tokens in source code
- ❌ Commit tokens to git repositories
- ❌ Share tokens via email/chat
- ❌ Log tokens in plaintext
- ❌ Store tokens long-term
- ❌ Use tokens across environments (dev/prod)
Secure Token Handling link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| #!/bin/bash
set -e
# Secure temp file
TOKEN_FILE=$(mktemp)
trap "rm -f $TOKEN_FILE" EXIT
# Get token
entratool get-token -p myprofile --silent > "$TOKEN_FILE"
chmod 600 "$TOKEN_FILE"
# Use token
TOKEN=$(cat "$TOKEN_FILE")
curl -H "Authorization: Bearer $TOKEN" https://api.example.com
# File automatically deleted on exit
|
Common Patterns link
Pattern 1: Token in Script link
1
2
3
4
5
6
7
8
9
10
11
12
| #!/bin/bash
set -euo pipefail
get_token() {
entratool get-token -p "$1" --silent
}
TOKEN=$(get_token "myprofile")
# Use token
curl -H "Authorization: Bearer $TOKEN" \
https://graph.microsoft.com/v1.0/me
|
Pattern 2: Cached Token link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| #!/bin/bash
TOKEN_CACHE="/tmp/my-token-cache.txt"
TOKEN_MAX_AGE=3000 # 50 minutes
get_fresh_token() {
entratool get-token -p myprofile --silent > "$TOKEN_CACHE"
chmod 600 "$TOKEN_CACHE"
}
# Check if cache exists and is fresh
if [ -f "$TOKEN_CACHE" ]; then
FILE_AGE=$(( $(date +%s) - $(stat -f %m "$TOKEN_CACHE") )) # macOS
# FILE_AGE=$(( $(date +%s) - $(stat -c %Y "$TOKEN_CACHE") )) # Linux
if [ $FILE_AGE -gt $TOKEN_MAX_AGE ]; then
get_fresh_token
fi
else
get_fresh_token
fi
TOKEN=$(cat "$TOKEN_CACHE")
|
Pattern 3: Token Rotation link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| #!/bin/bash
# Function to get valid token
get_valid_token() {
local profile=$1
local token_file="/tmp/token-$profile.txt"
# Check if token exists and is valid
if [ -f "$token_file" ] && entratool discover -f "$token_file" &>/dev/null; then
cat "$token_file"
else
# Get fresh token
entratool get-token -p "$profile" --silent | tee "$token_file"
chmod 600 "$token_file"
fi
}
# Use it
TOKEN=$(get_valid_token "myprofile")
curl -H "Authorization: Bearer $TOKEN" https://api.example.com
|
Pattern 4: Multi-Profile Management link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| #!/bin/bash
declare -A TOKENS
get_token_for_profile() {
local profile=$1
if [ -z "${TOKENS[$profile]}" ]; then
TOKENS[$profile]=$(entratool get-token -p "$profile" --silent)
fi
echo "${TOKENS[$profile]}"
}
# Use different profiles
GRAPH_TOKEN=$(get_token_for_profile "graph-profile")
AZURE_TOKEN=$(get_token_for_profile "azure-profile")
curl -H "Authorization: Bearer $GRAPH_TOKEN" https://graph.microsoft.com/v1.0/me
curl -H "Authorization: Bearer $AZURE_TOKEN" https://management.azure.com/subscriptions
|
Troubleshooting link
“Invalid token” link
Cause: Token is malformed or corrupted
Fix:
1
2
3
4
5
| # Validate token format
entratool inspect -t "$TOKEN"
# If invalid, get fresh token
TOKEN=$(entratool get-token -p myprofile --silent)
|
“Token expired” link
Cause: Token lifetime exceeded
Fix:
1
2
| # Get new token
TOKEN=$(entratool get-token -p myprofile --silent)
|
“Insufficient privileges” link
Cause: Token lacks required permissions
Fix:
- Check token scopes:
1
| entratool inspect -t "$TOKEN" | jq .payload.scp
|
- Request token with correct scopes:
1
2
| entratool get-token -p myprofile \
--scope "https://graph.microsoft.com/User.Read Mail.Send"
|
“Invalid audience” link
Cause: Token issued for different API
Fix:
1
2
3
4
5
6
| # Check audience
entratool inspect -t "$TOKEN" | jq .payload.aud
# Get token for correct API
entratool get-token -p myprofile \
--scope "https://correct-api.com/.default"
|
Next Steps link