[PATCH 3/4] Crude OpenID Connect client using Authlib

Frédéric Mangano-Tarumi fmang at mg0.fr
Thu Jun 4 20:00:20 UTC 2020


Developers can go to /sso/login to get redirected to the SSO. On
successful login, the ID token is displayed.
---
 .gitlab-ci.yml             |  3 ++-
 TESTING                    |  3 ++-
 aurweb/asgi.py             | 13 +++++++++++++
 aurweb/routers/__init__.py |  5 +++++
 aurweb/routers/sso.py      | 30 ++++++++++++++++++++++++++++++
 aurweb/spawn.py            |  3 +++
 conf/config.defaults       |  8 ++++++++
 conf/config.dev            |  9 +++++++++
 8 files changed, 72 insertions(+), 2 deletions(-)
 create mode 100644 aurweb/routers/__init__.py
 create mode 100644 aurweb/routers/sso.py

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f6260ebb..9dc951aa 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -11,7 +11,8 @@ before_script:
            base-devel git gpgme protobuf pyalpm python-mysql-connector
            python-pygit2 python-srcinfo python-bleach python-markdown
            python-sqlalchemy python-alembic python-pytest python-werkzeug
-           python-pytest-tap python-fastapi uvicorn nginx
+           python-pytest-tap python-fastapi uvicorn nginx python-authlib
+           python-itsdangerous python-httpx
 
 test:
   script:
diff --git a/TESTING b/TESTING
index 7261df92..d7df3672 100644
--- a/TESTING
+++ b/TESTING
@@ -13,7 +13,8 @@ INSTALL.
 
     # pacman -S --needed php php-sqlite sqlite words fortune-mod \
                          python python-sqlalchemy python-alembic \
-                         python-fastapi uvicorn nginx
+                         python-fastapi uvicorn nginx \
+                         python-authlib python-itsdangerous python-httpx
 
    Ensure to enable the pdo_sqlite extension in php.ini.
 
diff --git a/aurweb/asgi.py b/aurweb/asgi.py
index 9bb71ecc..60c7ade7 100644
--- a/aurweb/asgi.py
+++ b/aurweb/asgi.py
@@ -1,3 +1,16 @@
 from fastapi import FastAPI
+from starlette.middleware.sessions import SessionMiddleware
+
+import aurweb.config
+
+from aurweb.routers import sso
 
 app = FastAPI()
+
+session_secret = aurweb.config.get("fastapi", "session_secret")
+if not session_secret:
+    raise Exception("[fastapi] session_secret must not be empty")
+
+app.add_middleware(SessionMiddleware, secret_key=session_secret)
+
+app.include_router(sso.router)
diff --git a/aurweb/routers/__init__.py b/aurweb/routers/__init__.py
new file mode 100644
index 00000000..35d43c03
--- /dev/null
+++ b/aurweb/routers/__init__.py
@@ -0,0 +1,5 @@
+"""
+API routers for FastAPI.
+
+See https://fastapi.tiangolo.com/tutorial/bigger-applications/
+"""
diff --git a/aurweb/routers/sso.py b/aurweb/routers/sso.py
new file mode 100644
index 00000000..b16edffb
--- /dev/null
+++ b/aurweb/routers/sso.py
@@ -0,0 +1,30 @@
+import fastapi
+
+from authlib.integrations.starlette_client import OAuth
+from starlette.requests import Request
+
+import aurweb.config
+
+router = fastapi.APIRouter()
+
+oauth = OAuth()
+oauth.register(
+    name="sso",
+    server_metadata_url=aurweb.config.get("sso", "openid_configuration"),
+    client_kwargs={"scope": "openid"},
+    client_id=aurweb.config.get("sso", "client_id"),
+    client_secret=aurweb.config.get("sso", "client_secret"),
+)
+
+
+ at router.get("/sso/login")
+async def login(request: Request):
+    redirect_uri = aurweb.config.get("options", "aur_location") + "/sso/authenticate"
+    return await oauth.sso.authorize_redirect(request, redirect_uri, prompt="login")
+
+
+ at router.get("/sso/authenticate")
+async def authenticate(request: Request):
+    token = await oauth.sso.authorize_access_token(request)
+    user = await oauth.sso.parse_id_token(request, token)
+    return dict(user)
diff --git a/aurweb/spawn.py b/aurweb/spawn.py
index e86f29fe..5da8587e 100644
--- a/aurweb/spawn.py
+++ b/aurweb/spawn.py
@@ -60,6 +60,9 @@ def generate_nginx_config():
                 location / {{
                     proxy_pass http://{aurweb.config.get("php", "bind_address")};
                 }}
+                location /sso {{
+                    proxy_pass http://{aurweb.config.get("fastapi", "bind_address")};
+                }}
             }}
         }}
     """)
diff --git a/conf/config.defaults b/conf/config.defaults
index 447dacac..49259754 100644
--- a/conf/config.defaults
+++ b/conf/config.defaults
@@ -68,6 +68,14 @@ username-regex = [a-zA-Z0-9]+[.\-_]?[a-zA-Z0-9]+$
 git-serve-cmd = /usr/local/bin/aurweb-git-serve
 ssh-options = restrict
 
+[sso]
+openid_configuration =
+client_id =
+client_secret =
+
+[fastapi]
+session_secret =
+
 [serve]
 repo-path = /srv/http/aurweb/aur.git/
 repo-regex = [a-z0-9][a-z0-9.+_-]*$
diff --git a/conf/config.dev b/conf/config.dev
index d752f61f..27e981f8 100644
--- a/conf/config.dev
+++ b/conf/config.dev
@@ -20,6 +20,12 @@ aur_location = http://127.0.0.1:8080
 disable_http_login = 0
 enable-maintenance = 0
 
+; Single sign-on
+[sso]
+openid_configuration = http://127.0.0.1:8083/auth/realms/aurweb/.well-known/openid-configuration
+client_id = aurweb
+client_secret =
+
 [php]
 ; Address PHP should bind when spawned in development mode by aurweb.spawn.
 bind_address = 127.0.0.1:8081
@@ -30,3 +36,6 @@ htmldir = YOUR_AUR_ROOT/web/html
 [fastapi]
 ; Address uvicorn should bind when spawned in development mode by aurweb.spawn.
 bind_address = 127.0.0.1:8082
+
+; Passphrase FastAPI uses to sign client-side sessions.
+session_secret = 極秘、訳すな!あ、遅過ぎた。
-- 
2.27.0


More information about the aur-dev mailing list