OAuth2 Demo — End-to-End Authorization Flow with Multiple Grant Types
In this tutorial, you will learn about OAuth2 Demo. We cover key concepts, practical examples, and best practices to help you master this topic.
This demo implements a complete OAuth2 system with an authorization server, a resource server, and two clients demonstrating different grant types.
What You'll Learn
How all OAuth2 components work together in a single application, from client registration through token validation.
Why It Matters
Seeing the complete flow helps connect the individual concepts. This demo shows how the authorization server, resource server, and clients interact in practice.
Real-World Use
This demo models a simplified version of how Auth0 or Google OAuth works — multiple clients, one authorization server, and protected APIs.
flowchart TD
A["Demo System"] --> B["Authorization Server\nPort 5001"]
A --> C["Resource Server\nPort 5002"]
A --> D["Web App Client\n(Authorization Code)"]
A --> E["Backend Service\n(Client Credentials)"]
D --> B
D --> C
E --> B
E --> C
style A fill:#dbeafe,stroke:#2563eb
style B fill:#fef3c7,stroke:#d97706
style C fill:#dcfce7,stroke:#16a34a
style D fill:#fef3c7,stroke:#d97706
style E fill:#fef3c7,stroke:#d97706
Demo Architecture
| Component | Port | Purpose |
|---|---|---|
| Authorization Server | 5001 | Login, consent, issue tokens |
| Resource Server | 5002 | Validate tokens, return data |
| Web App (Auth Code) | 5003 | Browser-based login flow |
| Backend Service (CC) | - | Machine-to-machine auth |
Authorization Server
# auth_server.py — Simplified auth server
from flask import Flask, request, redirect, jsonify
import jwt, secrets, datetime
app = Flask(__name__)
SECRET = secrets.token_hex(32)
codes = {}
clients = {"web-app": {"secret": "web-secret", "redirect_uris": ["http://localhost:5003/callback"]}}
@app.route("/authorize")
def authorize():
client_id = request.args["client_id"]
redirect_uri = request.args["redirect_uri"]
state = request.args.get("state")
if client_id not in clients or redirect_uri not in clients[client_id]["redirect_uris"]:
return "Invalid", 400
code = secrets.token_urlsafe(32)
codes[code] = {"client_id": client_id, "user": "demo-user",
"expires": datetime.datetime.utcnow() + datetime.timedelta(minutes=2)}
return redirect(f"{redirect_uri}?code={code}&state={state}")
@app.route("/token", methods=["POST"])
def token():
grant = request.form["grant_type"]
if grant == "authorization_code":
c = codes.pop(request.form["code"], None)
if not c or datetime.datetime.utcnow() > c["expires"]:
return jsonify({"error": "invalid_grant"}), 400
token = jwt.encode({"sub": c["user"], "scope": "data:read data:write"}, SECRET, algorithm="HS256")
return jsonify({"access_token": token, "token_type": "Bearer", "expires_in": 3600})
elif grant == "client_credentials":
if request.form.get("client_id") != "backend-service" or request.form.get("client_secret") != "backend-secret":
return jsonify({"error": "invalid_client"}), 401
token = jwt.encode({"sub": "backend-service", "scope": "data:read"}, SECRET, algorithm="HS256")
return jsonify({"access_token": token, "token_type": "Bearer", "expires_in": 3600})
return jsonify({"error": "unsupported_grant_type"}), 400
if __name__ == "__main__":
app.run(port=5001)
Resource Server
# resource_server.py
from flask import Flask, request, jsonify
import jwt
app = Flask(__name__)
SECRET = secrets.token_hex(32) # Same as auth server in this demo
def validate(request):
auth = request.headers.get("Authorization", "")
token = auth[7:] if auth.startswith("Bearer ") else None
if not token:
return None, ("Missing token", 401)
try:
payload = jwt.decode(token, SECRET, algorithms=["HS256"])
return payload, None
except jwt.InvalidTokenError:
return None, ("Invalid token", 401)
@app.route("/api/data")
def get_data():
payload, error = validate(request)
if error:
return jsonify({"error": error[0]}), error[1]
scopes = payload.get("scope", "").split()
if "data:read" not in scopes:
return jsonify({"error": "insufficient_scope"}), 403
return jsonify({"data": "Protected data", "user": payload["sub"]})
if __name__ == "__main__":
app.run(port=5002)
Testing the Demo
# Start servers in separate terminals
python auth_server.py &
python resource_server.py &
# Test Client Credentials
curl -X POST -d "grant_type=client_credentials&client_id=backend-service&client_secret=backend-secret" http://localhost:5001/token
# Use token
TOKEN="<token-from-above>"
curl -H "Authorization: Bearer $TOKEN" http://localhost:5002/api/data
Common Mistakes
1. Demo Security
This demo uses a shared secret. In production, use RS256 and separate keys.
2. No HTTPS
Demo runs on HTTP. Production requires HTTPS everywhere.
3. In-Memory Storage
Tokens and codes are lost on restart. Use Redis in production.
4. No Rate Limiting
Token endpoints must be rate-limited to prevent brute force.
5. No State Parameter
This simplified demo omits state. Real implementations must include it.
Mini Project
Run this demo locally. Then extend it to include: PKCE for the web app client, refresh tokens, scope enforcement on all endpoints, and proper error responses.
What's Next
Now complete the OAuth2 Capstone Project — building a complete OAuth2 system.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro