From 172d1bee15fadf829dc46ab71d64393eeeff7f86 Mon Sep 17 00:00:00 2001
From: kaiyou <pierre@jaury.eu>
Date: Wed, 10 Jun 2020 10:40:34 +0200
Subject: [PATCH] Switch to RSA signature for id tokens

---
 hiboo/sso/oidc.py | 27 +++++++++++++++++++++++----
 1 file changed, 23 insertions(+), 4 deletions(-)

diff --git a/hiboo/sso/oidc.py b/hiboo/sso/oidc.py
index fb8cb68d..b9aab897 100644
--- a/hiboo/sso/oidc.py
+++ b/hiboo/sso/oidc.py
@@ -8,8 +8,9 @@ from authlib.integrations import flask_oauth2, sqla_oauth2
 from authlib.oauth2 import rfc6749 as oauth2
 from authlib.oidc import core as oidc
 from authlib.common import security as authlib_security
+from authlib.jose import jwk
 
-from hiboo.sso import blueprint, get_service
+from hiboo.sso import blueprint, get_service, generate_rsa_certificate
 from hiboo import models, utils, profile, security
 
 import flask
@@ -17,15 +18,23 @@ import time
 import inspect
 
 
+RSA_KEY_LENGTH = 2048
+
+
 def fill_service(service):
     """ If necessary, prepare the client with cryptographic material.
     """
     if "client_id" not in service.config:
         service.config.update(
             client_id=authlib_security.generate_token(24),
-            client_secret=authlib_security.generate_token(48),
-            jwt_key=authlib_security.generate_token(24),
-            jwt_alg="HS256"
+            client_secret=authlib_security.generate_token(48)
+        )
+    if "jwt_public_key" not in service.config:
+        key, public, _ = generate_rsa_certificate(service.uuid)
+        service.config.update(
+            jwt_key=jwk.dumps(key, kty="RSA"),
+            jwt_public_key=jwk.dumps(public, kty="RSA"),
+            jwt_alg="RS256"
         )
 
 
@@ -173,12 +182,16 @@ class Client(sqla_oauth2.OAuth2ClientMixin):
             "authorization_endpoint": flask.url_for("sso.oidc_authorize", service_uuid=uuid, _external=True),
             "token_endpoint": flask.url_for("sso.oidc_token", service_uuid=uuid, _external=True),
             "userinfo_endpoint": flask.url_for("sso.oidc_userinfo", service_uuid=uuid, _external=True),
+            "jwks_uri": flask.url_for("sso.oidc_jwks", service_uuid=uuid, _external=True),
             "response_types_supported": " ".join(self.service.config["response_types"]),
             "subject_types_supported": ["pairwise"],
             "id_token_signing_alg_values_supported": ["none"],
             "claims_supported": ["openid", "profile", "email"]
         })
 
+    def generate_jwks(self):
+        return flask.jsonify({"keys": [self.service.config["jwt_public_key"]]})
+
 
 @blueprint.route("/oidc/authorize/<service_uuid>", methods=["GET", "POST"])
 @blueprint.route("/oidc/<service_uuid>/authorize", methods=["GET", "POST"])
@@ -205,6 +218,12 @@ def oidc_userinfo(service_uuid):
     return client.generate_user_info(profile, token["scope"])
     
 
+@blueprint.route("/oidc/<service_uuid>/jwks", methods=["GET"])
+def oidc_jwks(service_uuid):
+    client = Client(get_service(service_uuid, __name__))
+    return client.generate_jwks()
+
+
 @blueprint.route("/oidc/<service_uuid>/.well-known/openid-configuration", methods=["GET"])
 def oidc_discovery(service_uuid):
     client = Client(get_service(service_uuid, __name__))
-- 
GitLab