API Authentication
Authentication methods for API access — from Basic auth to OAuth2 PKCE.
Basic Authentication
Basic auth — base64-encoded user:pass, requires TLS
# curl shorthand — curl handles base64 encoding
curl -u admin:password https://api.example.com/v1/resource
# Manual header — equivalent, explicit
curl -H "Authorization: Basic $(echo -n 'admin:password' | base64)" \
https://api.example.com/v1/resource
| Base64 is encoding, not encryption. Without TLS, credentials are plaintext on the wire. |
Bearer Token (JWT / OAuth2)
Bearer auth — carry JWT or opaque token in Authorization header
curl -H "Authorization: Bearer $TOKEN" \
https://api.example.com/v1/data
| Never hardcode tokens in scripts. Source from environment variables or a secrets manager like gopass/Vault. |
Decode a JWT payload — inspect claims without verification
echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq .
Example JWT payload
{
"sub": "admin",
"iss": "vault-01.inside.domusdigitalis.dev",
"aud": "domus-api",
"iat": 1712345678,
"exp": 1712349278,
"nbf": 1712345678
}
Check JWT expiration — compare exp claim against current epoch
exp=$(echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq -r '.exp')
now=$(date +%s)
if (( exp < now )); then
echo "Token expired $(( now - exp )) seconds ago"
else
echo "Token valid for $(( exp - now )) more seconds"
fi
API Key Authentication
API key in header — preferred, does not leak in server logs
curl -H "X-API-Key: $API_KEY" https://api.example.com/v1/data
API key in query parameter — visible in logs and browser history
# Avoid this pattern when possible
curl "https://api.example.com/v1/data?api_key=$API_KEY"
Key rotation pattern — overlap period prevents downtime
# 1. Generate new key
# 2. Deploy new key to all clients
# 3. Verify all clients using new key (check logs)
# 4. Revoke old key
# Never instant-swap — always overlap
OAuth2 Flows
Client Credentials — machine-to-machine, no user context
curl -X POST https://auth.example.com/oauth/token \
-d "grant_type=client_credentials" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" \
-d "scope=read:devices write:devices"
Example token response
{
"token_type": "Bearer",
"access_token": "eyJhbGciOi...",
"expires_in": 3600,
"scope": "read:devices write:devices"
}
Authorization Code — user-facing apps, browser redirect
# Step 1: Redirect user to authorization endpoint
# GET https://auth.example.com/authorize?
# response_type=code&
# client_id=$CLIENT_ID&
# redirect_uri=http://localhost:8080/callback&
# scope=read:profile
# Step 2: Exchange code for token
curl -X POST https://auth.example.com/oauth/token \
-d "grant_type=authorization_code" \
-d "code=$AUTH_CODE" \
-d "redirect_uri=http://localhost:8080/callback" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET"
PKCE flow — Authorization Code for public clients (SPAs, mobile)
# Generate code verifier (43-128 random chars)
code_verifier=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9-._~' | head -c 64)
# Derive code challenge
code_challenge=$(echo -n "$code_verifier" | openssl dgst -sha256 -binary | base64 | tr '+/' '-_' | tr -d '=')
# Step 1: Auth request includes challenge
# GET /authorize?...&code_challenge=$code_challenge&code_challenge_method=S256
# Step 2: Token exchange includes verifier
curl -X POST https://auth.example.com/oauth/token \
-d "grant_type=authorization_code" \
-d "code=$AUTH_CODE" \
-d "code_verifier=$code_verifier" \
-d "client_id=$CLIENT_ID"
Refresh token — exchange long-lived token for new access token
curl -X POST https://auth.example.com/oauth/token \
-d "grant_type=refresh_token" \
-d "refresh_token=$REFRESH_TOKEN" \
-d "client_id=$CLIENT_ID"
Mutual TLS (mTLS)
Client certificate authentication — strongest auth method
curl --cert client.pem --key client.key \
--cacert ca-chain.pem \
https://mtls.example.com/v1/secure
Test mTLS handshake — verify certificate acceptance
openssl s_client -connect host:443 \
-cert client.pem -key client.key \
-CAfile ca-chain.pem \
-brief
HMAC Request Signing
HMAC-SHA256 signature — prove request integrity and authenticity
timestamp=$(date +%s)
payload="GET/v1/data${timestamp}"
signature=$(echo -n "$payload" | openssl dgst -sha256 -hmac "$SECRET" -binary | base64)
curl -H "X-Timestamp: $timestamp" \
-H "X-Signature: $signature" \
https://api.example.com/v1/data
Session & Cookie Authentication
Session cookie — HttpOnly blocks XSS, Secure requires TLS, SameSite blocks CSRF
# Login and capture session cookie
curl -c cookies.txt -X POST https://app.example.com/login \
-d "username=admin&password=secret"
# Use session cookie for subsequent requests
curl -b cookies.txt https://app.example.com/api/data
Challenge-Response Headers
401 + WWW-Authenticate — server tells client how to authenticate
# Inspect the challenge header to understand auth requirements
curl -sI https://api.example.com/v1/protected | grep -i www-authenticate
# WWW-Authenticate: Bearer realm="api", error="invalid_token"