Flask-OIDC
Flask-OIDC is an extension to Flask that allows you to add OpenID Connect based authentication to your website. It is currently at version 2.4.0 and sees regular releases, with several updates in the past year, indicating active maintenance and development.
Warnings
- breaking Version 2.0.0 represents a major rebase of Flask-OIDC's API on the Authlib library. This introduced significant breaking changes in how the library is configured and used compared to 1.x versions.
- deprecated The `OpenIDConnect.user_getinfo()` and `OpenIDConnect.user_getfield()` methods are deprecated. User information should now be accessed via `session["oidc_auth_profile"]` or `g.oidc_user.userinfo`.
- breaking The `redirect_uri` sent to the ID provider in earlier 2.x versions (e.g., 2.0.3) was forced to HTTPS. In 2.1.0, this was changed to no longer force HTTPS based on OIDC spec recommendations. If you explicitly need to force HTTPS (or any specific URL), use `OIDC_OVERWRITE_REDIRECT_URI`.
- gotcha The `oidc.redirect_to_auth_server()` method was initially removed in 2.x and then re-added in version 2.2.2 for compatibility with v1.x usage patterns.
- gotcha A `SECRET_KEY` for the Flask application is absolutely critical for session management and overall security. Failing to set a strong, unique secret key will lead to security vulnerabilities.
- gotcha The `client_secrets.json` file is mandatory for OIDC configuration unless the `OIDC_ENABLED` setting is explicitly set to `False`. Before version 2.3.1, not having this file would cause issues even if `OIDC_ENABLED` was set to `False`.
- breaking Version 2.4.0 includes a fix for an open redirect vulnerability in login and logout URLs. While this is a fix, applications relying on or inadvertently enabling such redirects could experience changes in behavior or breakages.
Install
-
pip install flask-oidc
Imports
- OpenIDConnect
from flask_oidc import OpenIDConnect
- g
from flask import g
- session
from flask import session
Quickstart
import os
from flask import Flask, redirect, url_for, render_template_string, g, session
from flask_oidc import OpenIDConnect
app = Flask(__name__)
app.config.update({
'SECRET_KEY': os.environ.get('FLASK_SECRET_KEY', 'a_very_secret_key_that_should_be_random'),
'OIDC_CLIENT_SECRETS': os.environ.get('OIDC_CLIENT_SECRETS_FILE', './client_secrets.json'),
'OIDC_REDIRECT_URI': os.environ.get('OIDC_REDIRECT_URI', 'http://localhost:5000/oidc_callback'),
'OIDC_SCOPES': ['openid', 'email', 'profile'],
'OIDC_COOKIE_SECURE': False, # Use True in production with HTTPS
'OIDC_CALLBACK_ROUTE': '/oidc_callback', # The default callback route.
'OIDC_REQUIRE_VERIFIED_EMAIL': False # Set to True for stricter validation
})
oidc = OpenIDConnect(app)
HTML_TEMPLATE = '''
<!doctype html>
<html lang="en">
<head><meta charset="utf-8"></head>
<body>
{% if g.oidc_user.is_authenticated() %}
Hello, {{ g.oidc_user.userinfo.get('preferred_username', 'User') }}!
<a href="{{ url_for('private') }}">Access Protected Area</a>
<a href="{{ url_for('oidc_logout') }}">Log Out</a>
{% else %}
Welcome, anonymous user!
<a href="{{ url_for('private') }}">Log In</a>
{% endif %}
</body>
</html>
'''
@app.route('/')
def index():
return render_template_string(HTML_TEMPLATE)
@app.route('/private')
@oidc.require_login # Protect this route.
def private():
return f"Hello, {g.oidc_user.userinfo.get('email')}! This is a protected area."
@app.route('/logout')
def oidc_logout():
oidc.logout()
return redirect(url_for('index'))
# Example client_secrets.json content (create this file next to your app.py):
# {
# "web": {
# "client_id": "YOUR_CLIENT_ID",
# "client_secret": "YOUR_CLIENT_SECRET",
# "auth_uri": "YOUR_PROVIDER_AUTH_URI",
# "token_uri": "YOUR_PROVIDER_TOKEN_URI",
# "userinfo_uri": "YOUR_PROVIDER_USERINFO_URI",
# "issuer": "YOUR_PROVIDER_ISSUER",
# "redirect_uris": ["http://localhost:5000/oidc_callback"],
# "token_introspection_uri": "YOUR_PROVIDER_TOKEN_INTROSPECTION_URI" (optional)
# }
# }
if __name__ == '__main__':
# For local development, ensure OIDC_CLIENT_SECRETS_FILE points to a valid file.
# Replace with your actual OIDC provider details in client_secrets.json
# And set FLASK_SECRET_KEY in your environment.
# export FLASK_SECRET_KEY="a_strong_random_secret_key"
# If using HTTP (not recommended for production), set OIDC_COOKIE_SECURE to False.
# Otherwise, ensure your application runs over HTTPS.
app.run(debug=True, port=5000)