Prefer AI-assisted setup? Use our AI prompts for Flask to automatically integrate Civic Auth using Claude, ChatGPT, or other AI assistants. Includes a step-by-step video tutorial!
Important: The SDK Handles EverythingThe Civic Auth Python SDK abstracts away all token validation complexity. You do NOT need to:
- Implement custom middleware for token validation
- Parse or validate JWT tokens manually
- Handle token refresh logic yourself
Simply use the provided decorators and user access functions - the SDK handles all authentication logic for you.
1. Install dependencies
pip install "civic-auth[flask]"
Or, if you’re using the uv package manager:
uv add "civic-auth[flask]"
Your app will need the following configuration:
config = {
"client_id": "...", # Client ID from auth.civic.com
"redirect_url": "http://localhost:5000/auth/callback", # change to your domain when deploying
"post_logout_redirect_url": "http://localhost:5000/" # The postLogoutRedirectUrl is the URL where the user will be redirected after successfully logging out from Civic's auth server.
}
Note: redirect_url and post_logout_redirect_url must be absolute URLs.
3. Initialize Civic Auth
Set up Civic Auth with your Flask app:
from flask import Flask
from civic_auth.integrations.flask import init_civic_auth, create_auth_blueprint
app = Flask(__name__)
app.secret_key = "your-secret-key" # Required for session handling
# Initialize Civic Auth
init_civic_auth(app, config)
# Register the auth blueprint (provides /auth/* routes)
app.register_blueprint(create_auth_blueprint(config))
4. Login and Logout Routes
The auth blueprint automatically creates these routes:
/auth/login - Initiates the login flow
/auth/callback - Handles the OAuth callback
/auth/logout - Logs the user out
You can redirect users to start the login process:
from flask import redirect, url_for
@app.route("/")
def index():
return redirect(url_for("civic_auth.login"))
5. Protect Routes
Use the civic_auth_required decorator to protect routes:
from civic_auth.integrations.flask import civic_auth_required
@app.route("/admin/hello")
@civic_auth_required
async def hello():
return "Hello, authenticated user!"
Use get_civic_user() to access the logged-in user as a dictionary:
from civic_auth.integrations.flask import get_civic_user
@app.route("/admin/profile")
@civic_auth_required
async def profile():
user = await get_civic_user()
return {
"message": f"Hello, {user.get('name', 'User')}!",
"user_info": {
"id": user.get("id"),
"email": user.get("email"),
"name": user.get("name"),
"picture": user.get("picture")
}
}
@app.route("/admin/dashboard")
@civic_auth_required
async def dashboard():
user = await get_civic_user()
return f"""
<h1>Dashboard</h1>
<p>Welcome, {user.get('name', 'User')}!</p>
<img src="{user.get('picture', '')}" alt="Profile picture" style="width: 50px; height: 50px; border-radius: 50%;">
<p>Email: {user.get('email', 'No email')}</p>
<p><a href="/auth/logout">Logout</a></p>
"""
Working with User Data
The get_civic_user() function returns a dictionary with user information. Always use .get() for safe access:
@app.route("/user-info")
@civic_auth_required
async def user_info():
user = await get_civic_user()
return {
"id": user.get("id"),
"email": user.get("email", "No email"),
"name": user.get("name", "Anonymous"),
"picture": user.get("picture")
}
Complete Example
Here’s a complete working Flask application:
from flask import Flask, redirect, url_for
from civic_auth.integrations.flask import (
init_civic_auth,
create_auth_blueprint,
civic_auth_required,
get_civic_user
)
app = Flask(__name__)
app.secret_key = "your-secret-key" # Required for session handling
# Configuration
config = {
"client_id": "YOUR_CLIENT_ID", # Replace with your actual client ID
"redirect_url": "http://localhost:5000/auth/callback",
"post_logout_redirect_url": "http://localhost:5000/"
}
# Initialize Civic Auth
init_civic_auth(app, config)
app.register_blueprint(create_auth_blueprint(config))
@app.route("/")
def home():
return """
<h1>Welcome to My Flask App!</h1>
<p><a href="/auth/login">Login with Civic Auth</a></p>
<p><a href="/profile">View Profile</a> (requires authentication)</p>
<p><a href="/dashboard">Dashboard</a> (requires authentication)</p>
<p><a href="/public">Public Page</a> (no authentication required)</p>
"""
@app.route("/profile")
@civic_auth_required
async def profile():
user = await get_civic_user()
return {
"authenticated": True,
"user": {
"id": user.get("id"),
"name": user.get("name"),
"email": user.get("email"),
"picture": user.get("picture")
},
"logout_url": "/auth/logout"
}
@app.route("/dashboard")
@civic_auth_required
async def dashboard():
user = await get_civic_user()
return f"""
<h1>Dashboard</h1>
<p>Welcome, {user.get('name', 'User')}!</p>
<p>Email: {user.get('email', 'No email')}</p>
<p><a href="/auth/logout">Logout</a></p>
"""
@app.route("/public")
def public():
return "<h1>Public Page</h1><p>No authentication required</p>"
if __name__ == "__main__":
app.run(debug=True)
Configuration Options
| Field | Required | Description |
|---|
client_id | Yes | Your Civic Auth Client ID from auth.civic.com |
redirect_url | Yes | Where Civic redirects after authentication (must be absolute URL) |
post_logout_redirect_url | Yes | Where users go after logout (must be absolute URL) |
Note: redirect_url and post_logout_redirect_url must be absolute URLs.
Next Steps
- Get your Client ID: Sign up at auth.civic.com
- Replace
YOUR_CLIENT_ID with your actual client ID
- Update URLs when deploying to production
- Add more protected routes as needed
- Handle user data safely using
.get() method for dictionary access
Authentication Flows
Civic Auth supports multiple OAuth 2.0 authentication methods to provide maximum security for different application architectures.
Need client secret authentication? Civic Auth supports PKCE-only, client secrets, and hybrid PKCE + client secret approaches. See our Authentication Flows guide for detailed comparison.
The examples above use PKCE authentication, which is handled entirely by the Civic Auth SDK and suitable for most applications.
Access Tokens
Retrieve tokens from session (or via a CivicAuth instance if exposed):
from flask import session
from civic_auth.core import CivicAuth # for key names
@app.route("/tokens")
@civic_auth_required
async def tokens():
return {
"access_token": session.get(CivicAuth.ACCESS_TOKEN_KEY),
"id_token": session.get(CivicAuth.ID_TOKEN_KEY),
"refresh_token": session.get(CivicAuth.REFRESH_TOKEN_KEY),
"token_type": "Bearer",
}