[3/4] Crude OpenID Connect client using Authlib

Message ID 20200604200020.GA893616@tsubame.mg0.fr
State New
Headers show
Series [1/4] Remove the FastAPI /hello test route | expand

Commit Message

Frédéric Mangano-Tarumi June 4, 2020, 8 p.m. UTC
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

Comments

Lukas Fleischer June 6, 2020, 12:42 p.m. UTC | #1
On Thu, 04 Jun 2020 at 16:00:20, Frédéric Mangano-Tarumi wrote:
> 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/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 = \u6975\u79d8\u3001\u8a33\u3059\u306a\uff01\u3042\u3001\u9045\u904e\u304e\u305f\u3002

Nit: Since this is not really a secret, can we just use a plain text
value (e.g. "secret") here? I know we had a similar discussion before,
but I don't like the idea of having binary data in text configuration
files since it might cause all sorts of issues with different tools (and
even if that means the tools are bad, it's better to avoid it
altogether).
Frédéric Mangano-Tarumi June 6, 2020, 12:49 p.m. UTC | #2
Lukas Fleischer [2020-06-06 08:42:05 -0400]
> > +; Passphrase FastAPI uses to sign client-side sessions.
> > +session_secret = \u6975\u79d8\u3001\u8a33\u3059\u306a\uff01\u3042\u3001\u9045\u904e\u304e\u305f\u3002
> 
> Nit: Since this is not really a secret, can we just use a plain text
> value (e.g. "secret") here? I know we had a similar discussion before,
> but I don't like the idea of having binary data in text configuration
> files since it might cause all sorts of issues with different tools (and
> even if that means the tools are bad, it's better to avoid it
> altogether).

It is plain UTF-8 already. Feel free to replace it by whatever you like.
I don’t think it deserves a new patch submission.

I realize I forgot to mention one important thing: python-authlib and
python-httpx are not yet in Arch’s repos.

Patch

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"),
+)
+
+
+@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")
+
+
+@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 = 極秘、訳すな!あ、遅過ぎた。