diff --git a/run.fcgi b/run.fcgi
index 369370a..4235124 100755
--- a/run.fcgi
+++ b/run.fcgi
@@ -2,7 +2,6 @@
from flipflop import WSGIServer
import sys
sys.path.append('/home/edward/src/agenda') # isort:skip
-sys.path.append("/home/edward/src/2024/UniAuth")
from web_view import app # isort:skip
if __name__ == '__main__':
diff --git a/templates/navbar.html b/templates/navbar.html
index d1b8060..fe5b98a 100644
--- a/templates/navbar.html
+++ b/templates/navbar.html
@@ -87,7 +87,7 @@
{% if g.user.is_authenticated %}
- - Logout
+ - Logout
{% else %}
- Login
{% endif %}
diff --git a/web_view.py b/web_view.py
index b9c5de6..084c341 100755
--- a/web_view.py
+++ b/web_view.py
@@ -15,10 +15,11 @@ from collections import defaultdict
from datetime import date, datetime, timedelta, timezone
import flask
-import UniAuth.auth
import werkzeug
import werkzeug.debug.tbtools
import yaml
+from authlib.integrations.flask_client import OAuth
+from werkzeug.middleware.proxy_fix import ProxyFix
import agenda.data
import agenda.error_mail
@@ -36,14 +37,33 @@ from agenda.types import StrDict, Trip
app = flask.Flask(__name__)
app.debug = False
app.config.from_object("config.default")
+app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1)
agenda.error_mail.setup_error_mail(app)
+oauth = OAuth(app)
+authentik_url = app.config["AUTHENTIK_URL"]
+oauth.register(
+ name="authentik",
+ client_id=app.config["AUTHENTIK_CLIENT_ID"],
+ client_secret=app.config["AUTHENTIK_CLIENT_SECRET"],
+ server_metadata_url=f"{authentik_url}/application/o/agenda/.well-known/openid-configuration",
+ client_kwargs={"scope": "openid email profile"},
+)
+
+
+class User:
+ """Simple user object for template compatibility."""
+
+ def __init__(self, is_authenticated: bool) -> None:
+ """Init."""
+ self.is_authenticated = is_authenticated
+
@app.before_request
def handle_auth() -> None:
- """Handle authentication and set global user."""
- flask.g.user = UniAuth.auth.get_current_user()
+ """Set global user from session."""
+ flask.g.user = User(is_authenticated=bool(flask.session.get("user")))
@app.errorhandler(werkzeug.exceptions.InternalServerError)
@@ -831,23 +851,38 @@ def schengen_report() -> str:
return agenda.trip_schengen.flask_route_schengen_report()
-@app.route("/callback")
-def auth_callback() -> tuple[str, int] | werkzeug.Response:
- """Process the authentication callback."""
- return UniAuth.auth.auth_callback()
-
-
@app.route("/login")
def login() -> werkzeug.Response:
- """Login."""
- next_url = flask.request.args["next"]
- return UniAuth.auth.redirect_to_login(next_url)
+ """Redirect to Authentik for OIDC login."""
+ next_url = flask.request.args.get("next", flask.url_for("index"))
+ flask.session["login_next"] = next_url
+ redirect_uri = flask.url_for("auth_callback", _external=True)
+ return oauth.authentik.authorize_redirect(redirect_uri)
+
+
+@app.route("/callback")
+def auth_callback() -> werkzeug.Response:
+ """Handle OIDC callback from Authentik."""
+ try:
+ token = oauth.authentik.authorize_access_token()
+ except Exception:
+ return flask.redirect(flask.url_for("login"))
+ userinfo = token.get("userinfo") or oauth.authentik.userinfo()
+ flask.session["user"] = {
+ "sub": userinfo["sub"],
+ "username": userinfo.get("preferred_username"),
+ "email": userinfo.get("email"),
+ }
+ next_url = flask.session.pop("login_next", flask.url_for("index"))
+ return flask.redirect(next_url)
@app.route("/logout")
def logout() -> werkzeug.Response:
- """Logout."""
- return UniAuth.auth.redirect_to_logout(flask.request.args["next"])
+ """Log out and redirect to Authentik end-session endpoint."""
+ flask.session.pop("user", None)
+ end_session_url = f"{authentik_url}/application/o/agenda/end-session/"
+ return flask.redirect(end_session_url)
if __name__ == "__main__":