OAuth2 Access Tokens — Short-Lived Credentials for API Authorization
In this tutorial, you will learn about OAuth2 Access Tokens. We cover key concepts, practical examples, and best practices to help you master this topic.
OAuth2 access tokens are short-lived credentials issued by the authorization server, used by clients to access protected resources on the resource server.
What You'll Learn
Access token formats, lifetimes, how resource servers validate them, and the difference between self-contained (JWT) and opaque (reference) tokens.
Why It Matters
Access tokens are the primary credential for API requests. Getting their format, validation, and security right is critical for any OAuth2 implementation.
Real-World Use
Google uses opaque access tokens. Auth0 uses JWTs for access tokens. GitHub uses JWTs for some tokens and opaque for others. The format choice affects verification Strategy.
flowchart LR
A["Authorization Server"] -->|"Issues Access Token"| B["Client"]
B -->|"API Call + Bearer Token"| C["Resource Server"]
C -->{"Token Format?"}
C -->|"JWT"| D["Validate signature locally"]
C -->|"Opaque"| E["Introspection endpoint"]
D -->|"Valid"| F["Process Request"]
E -->|"Active"| F
style A fill:#dbeafe,stroke:#2563eb
style B fill:#fef3c7,stroke:#d97706
style C fill:#fef3c7,stroke:#d97706
style F fill:#dcfce7,stroke:#16a34a
Token Format Comparison
| Feature | JWT Access Token | Opaque Access Token |
|---|---|---|
| Self-contained | Yes (all data in token) | No (reference only) |
| Validation | Signature + claims | Server call (introspection) |
| Revocation | Hard (needs blocklist) | Easy (delete from store) |
| Size | Larger | Small |
| Debuggable | Decode to see claims | Must call introspection |
| Performance | Fast (local) | Slower (network call) |
Code Example: JWT Access Token Validation
import jwt
from flask import Flask, request, jsonify
app = Flask(__name__)
JWKS_URL = "https://auth.example.com/.well-known/jwks.json"
jwks_client = PyJWKClient(JWKS_URL)
def validate_access_token(token):
try:
signing_key = jwks_client.get_signing_key_from_jwt(token)
payload = jwt.decode(
token,
signing_key.key,
algorithms=["RS256"],
options={"require": ["scope", "exp"]},
audience="https://api.example.com"
)
return payload
except jwt.ExpiredSignatureError:
return None
except jwt.InvalidTokenError:
return None
@app.route("/api/resource")
def get_resource():
auth = request.headers.get("Authorization", "")
token = auth[7:] if auth.startswith("Bearer ") else None
if not token:
return jsonify({"error": "Missing token"}), 401
payload = validate_access_token(token)
if not payload:
return jsonify({"error": "Invalid token"}), 401
return jsonify({
"user": payload["sub"],
"scopes": payload.get("scope")
})
Common Mistakes
1. Making Access Tokens Too Long
24-hour access tokens defeat the purpose of short-lived tokens. Use 15-60 minutes.
2. Not Including Token Type
Without token_type: Bearer, other token types might be accepted as access tokens.
3. Using Opaque Tokens When JWT Is Better
If the resource server cannot call the introspection endpoint on every request, use JWT for local validation.
4. Not Scoping Access Tokens
Every access token should have specific scopes. Full-access tokens violate Least Privilege.
5. Sending Access Tokens in URLs
Always use the Authorization header. Never pass tokens in URL query parameters.
Practice Questions
- What is the difference between JWT and opaque access tokens?
- How long should access tokens live?
- How does a resource server validate an opaque token?
- What claims should access tokens include?
- Why should access tokens have scopes?
Answers:
- JWT tokens are self-contained (validated locally). Opaque tokens need server-side introspection.
- 15-60 minutes. Shorter for high security, longer for mobile apps (battery).
- Call the authorization server's introspection endpoint with the token. The endpoint returns active/inactive and token metadata.
- sub (user), scope (permissions), exp (expiry), iss (issuer), aud (audience), token_type.
- Scopes enforce the principle of least privilege — a token can only do what its scopes allow.
Challenge: Build a resource server that supports both JWT (local validation) and opaque (introspection) access tokens. The server detects the token format and validates accordingly.
FAQ
Mini Project
Create a Flask resource server that validates access tokens using JWKS (for JWT tokens) and introspection (for opaque tokens). Implement scope-based access control on multiple endpoints.
What's Next
Now learn about OAuth2 Refresh Tokens — the counterpart to access tokens for persistent sessions.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro