sso: implement RS256 JWT signing algo
parent
a57ab99014
commit
576ff1b0ea
|
@ -16,12 +16,22 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- .:/usr/src/app
|
- .:/usr/src/app
|
||||||
environment:
|
environment:
|
||||||
- TESTING=1
|
|
||||||
- TEMPLATES_AUTO_RELOAD=true
|
- TEMPLATES_AUTO_RELOAD=true
|
||||||
- AUTHLIB_INSECURE_TRANSPORT=1
|
- AUTHLIB_INSECURE_TRANSPORT=1
|
||||||
|
|
||||||
|
# Set these to your testing LDAP dn/password
|
||||||
- LDAP_BIND_DN
|
- LDAP_BIND_DN
|
||||||
- LDAP_BIND_PASSWORD
|
- LDAP_BIND_PASSWORD
|
||||||
|
# ...or uncomment this to allow any login with some mocked user info
|
||||||
|
# - TESTING=1
|
||||||
|
|
||||||
- LOGGING_LEVEL=DEBUG
|
- LOGGING_LEVEL=DEBUG
|
||||||
|
|
||||||
|
# Uncomment these to enable proper RSA JWT id_tokens signing
|
||||||
|
# - JWT_PRIVATE_KEY=private.pem
|
||||||
|
# - JWT_PUBLIC_KEYS=public.pem,public.pem
|
||||||
|
# - JWT_ALG=RS256
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
pgdata:
|
pgdata:
|
||||||
|
|
|
@ -25,14 +25,6 @@ import logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
DUMMY_JWT_CONFIG = {
|
|
||||||
"key": "secret-key",
|
|
||||||
"alg": "HS256",
|
|
||||||
"iss": "https://sso.hackerspace.pl",
|
|
||||||
"exp": 3600,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def exists_nonce(nonce, req):
|
def exists_nonce(nonce, req):
|
||||||
exists = AuthorizationCode.query.filter_by(
|
exists = AuthorizationCode.query.filter_by(
|
||||||
client_id=req.client_id, nonce=nonce
|
client_id=req.client_id, nonce=nonce
|
||||||
|
@ -48,7 +40,7 @@ def generate_user_info(user, scope):
|
||||||
preferred_username=user.username,
|
preferred_username=user.username,
|
||||||
nickname=user.username,
|
nickname=user.username,
|
||||||
groups=user.groups,
|
groups=user.groups,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_authorization_code(client, grant_user, request):
|
def create_authorization_code(client, grant_user, request):
|
||||||
|
@ -116,7 +108,7 @@ class HybridGrant(_OpenIDHybridGrant):
|
||||||
return exists_nonce(nonce, request)
|
return exists_nonce(nonce, request)
|
||||||
|
|
||||||
def get_jwt_config(self):
|
def get_jwt_config(self):
|
||||||
return DUMMY_JWT_CONFIG
|
return app.config.get("JWT_CONFIG")
|
||||||
|
|
||||||
def generate_user_info(self, user, scope):
|
def generate_user_info(self, user, scope):
|
||||||
return generate_user_info(user, scope)
|
return generate_user_info(user, scope)
|
||||||
|
|
|
@ -51,9 +51,24 @@ LDAP_BIND_PASSWORD = env.str("LDAP_BIND_PASSWORD", default="insert password here
|
||||||
PROXYFIX_ENABLE = env.bool("PROXYFIX_ENABLE", default=True)
|
PROXYFIX_ENABLE = env.bool("PROXYFIX_ENABLE", default=True)
|
||||||
PROXYFIX_NUM_PROXIES = env.int("PROXYFIX_NUM_PROXIES", default=1)
|
PROXYFIX_NUM_PROXIES = env.int("PROXYFIX_NUM_PROXIES", default=1)
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
from authlib.jose import jwk
|
||||||
|
|
||||||
|
jwt_alg = env.str("JWT_ALG", default="HS256")
|
||||||
|
|
||||||
|
if jwt_alg == "HS256":
|
||||||
|
jwt_privkey = env.str("JWT_SECRET_KEY", default=SECRET_KEY)
|
||||||
|
JWT_PUBLIC_KEYS = []
|
||||||
|
else:
|
||||||
|
jwt_privkey = jwk.dumps(env.path("JWT_PRIVATE_KEY").read_text(), kty="RSA")
|
||||||
|
JWT_PUBLIC_KEYS = [
|
||||||
|
jwk.dumps(pathlib.Path(pub).read_text(), kty="RSA")
|
||||||
|
for pub in env.list("JWT_PUBLIC_KEYS")
|
||||||
|
]
|
||||||
|
|
||||||
JWT_CONFIG = {
|
JWT_CONFIG = {
|
||||||
"key": env.str("JWT_SECRET_KEY", default=SECRET_KEY),
|
"key": jwt_privkey,
|
||||||
"alg": env.str("JWT_ALG", default="HS256"),
|
"alg": jwt_alg,
|
||||||
"iss": env.str("JWT_ISS", default="https://sso.hackerspace.pl"),
|
"iss": env.str("JWT_ISS", default="https://sso.hackerspace.pl"),
|
||||||
"exp": env.int("JWT_EXP", default=3600),
|
"exp": env.int("JWT_EXP", default=3600),
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,7 +173,6 @@ def authorize():
|
||||||
except OAuth2Error as error:
|
except OAuth2Error as error:
|
||||||
return render_template("authorization_error.html", error=dict(error.get_body()))
|
return render_template("authorization_error.html", error=dict(error.get_body()))
|
||||||
|
|
||||||
print(grant)
|
|
||||||
if grant.client.membership_required and not current_user.is_membership_active:
|
if grant.client.membership_required and not current_user.is_membership_active:
|
||||||
return render_template("membership_required.html")
|
return render_template("membership_required.html")
|
||||||
|
|
||||||
|
@ -239,6 +238,11 @@ def api_userinfo():
|
||||||
return jsonify(generate_user_info(user, current_token.scope))
|
return jsonify(generate_user_info(user, current_token.scope))
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/.well-known/jwks.json")
|
||||||
|
def jwks_endpoint():
|
||||||
|
return jsonify({"keys": current_app.config["JWT_PUBLIC_KEYS"]})
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/.well-known/openid-configuration")
|
@bp.route("/.well-known/openid-configuration")
|
||||||
def openid_configuration():
|
def openid_configuration():
|
||||||
return jsonify(
|
return jsonify(
|
||||||
|
@ -247,6 +251,7 @@ def openid_configuration():
|
||||||
"authorization_endpoint": url_for(".authorize", _external=True),
|
"authorization_endpoint": url_for(".authorize", _external=True),
|
||||||
"token_endpoint": url_for(".issue_token", _external=True),
|
"token_endpoint": url_for(".issue_token", _external=True),
|
||||||
"userinfo_endpoint": url_for(".api_userinfo", _external=True),
|
"userinfo_endpoint": url_for(".api_userinfo", _external=True),
|
||||||
|
"jwks_uri": url_for(".jwks_endpoint", _external=True),
|
||||||
"response_types_supported": ["code", "id_token", "token id_token"],
|
"response_types_supported": ["code", "id_token", "token id_token"],
|
||||||
"token_endpoint_auth_methods_supported": [
|
"token_endpoint_auth_methods_supported": [
|
||||||
"client_secret_basic",
|
"client_secret_basic",
|
||||||
|
|
Loading…
Reference in New Issue