OAuth 2.0 Access Tokens and Refresh Tokens – Practical Tutorial
This tutorial explains OAuth 2.0 access and refresh tokens, shows how to obtain and automatically renew them in Python, and outlines security best practices for protecting user data.
Focus: OAuth 2.0 access token
OAuth 2.0 Access Tokens and Refresh Tokens – Practical Tutorial
What is OAuth 2.0?
OAuth 2.0 is an industry‑standard protocol that lets a third‑party application obtain limited permission to act on behalf of a user without ever seeing the user’s password. The core idea is to issue short‑lived access tokens that grant access to protected resources, and refresh tokens that allow the application to obtain new access tokens when the old ones expire.
Access Token
Definition
- A bearer token (usually a JWT or an opaque string) that proves the holder has the right to access a specific resource.
- Sent in the
Authorization: Bearer <token>HTTP header. - Typically valid for a short period (minutes to a few hours).
How it works
- The user authenticates with the Authorization Server (e.g., Google, Auth0) using credentials or another method.
- The Authorization Server issues an access token to the Client (your app) after a successful token request.
- The Client includes the token on each request to the Resource Server (the API you want to call).
- The Resource Server validates the token (signature, expiry, scope) and returns the requested data.
Example request (Python)
import requests
client_id = "YOUR_CLIENT_ID"
client_secret = "YOUR_CLIENT_SECRET"
redirect_uri = "https://yourapp.com/callback"
token_url = "https://api.example.com/oauth2/token"
# Step 1: Get an authorization code (usually via user login)
# For simplicity we assume we already have the code:
authorization_code = "temp_code"
# Step 2: Exchange the code for tokens
data = {
"grant_type": "authorization_code",
"code": authorization_code,
"redirect_uri": redirect_uri,
"client_id": client_id,
"client_secret": client_secret,
}
response = requests.post(token_url, data=data)
tokens = response.json()
access_token = tokens["access_token"]
refresh_token = tokens["refresh_token"]
print("Access token:", access_token)
print("Refresh token:", refresh_token)Refresh Token
Definition
- Another bearer token, usually long‑lived (days, weeks, or months).
- Allows the Client to request a new access token without user interaction.
- Not sent automatically; the Client must explicitly request a new access token using the refresh token.
How it works
- When the Authorization Server issues the initial tokens, it may also provide a refresh token (if the grant type supports it, e.g.,
authorization_codeorrefresh_token). - When the access token expires, the Client sends a request to the token endpoint:
POST /oauth2/token grant_type=refresh_token refresh_token=<refresh_token> client_id=<client_id> client_secret=<client_secret> - The server validates the refresh token, issues a new access token (and optionally a new refresh token).
- The Client resumes normal API calls using the refreshed access token.
Example request (Python)
def refresh_access_token(refresh_token, client_id, client_secret, token_url):
payload = {
"grant_type": "refresh_token",
"refresh_token": refresh_token,
"client_id": client_id,
"client_secret": client_secret,
}
resp = requests.post(token_url, data=payload)
return resp.json() # contains new access_token (and maybe new refresh_token)
new_tokens = refresh_access_token(refresh_token, client_id, client_secret, token_url)
print("New access token:", new_tokens["access_token"])Token Expiration
- Access tokens are short‑lived to limit the window of misuse if they are leaked.
- Refresh tokens are long‑lived but are stored securely (e.g., encrypted storage, HttpOnly cookies) because they can be used to obtain new access tokens indefinitely.
- Common expiration patterns:
- Access token: 5 minutes to 2 hours.
- Refresh token: 7 days to never (revoked manually or after a security event).
Detecting expiration
Most token libraries embed an exp claim (JWT) or return an expires_in field. Example check for a JWT:
import jwt
import time
def is_access_token_valid(token, secret):
try:
payload = jwt.decode(token, secret, algorithms=["HS256"])
return payload["exp"] > time.time()
except jwt.ExpiredSignatureError:
return False
except jwt.InvalidTokenError:
return FalseWhen to Use Which Token
| Situation | Token to Use | Reason |
|---|---|---|
| Initial API call after user login | Access token | Grants immediate access; short life reduces risk. |
| API call after access token expiry | Refresh token (to get a new access token) | No user interaction required; keeps the session alive. |
| Revoking user’s access | Refresh token revocation (or delete both tokens) | Stops any future token exchanges. |
| Public client (no secret) | Use Authorization Code Flow with PKCE to avoid exposing a secret; still use access and refresh tokens. |
Real‑World Example: Calling a Protected API
def get_user_profile(access_token):
url = "https://api.example.com/user/profile"
headers = {"Authorization": f"Bearer {access_token}"}
resp = requests.get(url, headers=headers)
resp.raise_for_status()
return resp.json()
# Main loop
while True:
profile = get_user_profile(access_token)
print("User profile:", profile)
# Check if token is about to expire (e.g., less than 60 seconds left)
if not is_access_token_valid(access_token, client_secret):
# Refresh
new = refresh_access_token(refresh_token, client_id, client_secret, token_url)
access_token = new["access_token"]
# Optionally update refresh_token if the server returned a new one
refresh_token = new.get("refresh_token", refresh_token)In this loop: - The app repeatedly fetches the user profile. - Before each call it verifies the token’s validity. - If the token is near expiry, it uses the refresh token to obtain a fresh access token, then continues without prompting the user again.
Best Practices
- Store refresh tokens securely – never log them, use secure storage, and treat them like passwords.
- Rotate refresh tokens – many providers issue a new refresh token with each use; store the latest value.
- Set appropriate scopes – request only the permissions you need; it limits damage if a token is compromised.
- Implement token revocation – provide an endpoint for the client or user to invalidate tokens (especially refresh tokens) when a breach is suspected.
- Handle errors gracefully – HTTP 401/403 responses often indicate an expired or invalid token; trigger a refresh flow automatically.
- Use HTTPS everywhere – tokens are bearer credentials; transport security is mandatory.
Summary
- Access tokens are short‑lived credentials that prove a client’s right to access a resource. They are sent in the
Authorizationheader and must be refreshed when they expire. - Refresh tokens are long‑lived credentials that enable the client to obtain new access tokens without user interaction. They are used only when the access token is no longer valid.
- Tokens expire according to the provider’s policy; access tokens typically live minutes to a few hours, while refresh tokens can persist days or weeks.
- A proper flow involves: user authentication → token request (access + refresh) → API calls with the access token → automatic refresh when needed.
- Secure storage, limited scopes, and token revocation are essential to keep the system safe.
By understanding the roles, lifetimes, and exchange mechanics of access and refresh tokens, junior developers can implement robust OAuth 2.0 integrations that protect user data while providing a smooth experience.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.