From f21910e675b6270e5ea43b608f0eac10e0aa3146 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Mon, 21 Apr 2025 11:53:23 +0200 Subject: [PATCH] auth: get OIDC provider URIs, endpoints, and keys from OP config URLs --- demo/config.py | 6 +++--- scopes/tests/config.py | 10 +++++----- scopes/web/auth/oidc.py | 28 ++++++++++++++++++++++++---- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/demo/config.py b/demo/config.py index 599226d..fbb5224 100644 --- a/demo/config.py +++ b/demo/config.py @@ -31,9 +31,9 @@ dbschema = getenv('DBSCHEMA', 'demo') oidc_provider = 'https://a1.cy7.de' oidc_client_id = getenv('OIDC_CLIENT_ID', '311613119816392525') oidc_params = dict( - auth_url=getenv('OIDC_PROVIDER_URL', oidc_provider + '/oauth/v2/authorize'), - token_url=getenv('OIDC_TOKEN_URL', oidc_provider + '/oauth/v2/token'), - userinfo_url=getenv('OIDC_USERINFO_URL', oidc_provider + '/oidc/v1/userinfo'), + op_config_url=oidc_provider + '/.well-known/openid-configuration', + op_uris=None, + op_keys=None, callback_url=getenv('OIDC_CALLBACK_URL', base_url + '/auth/callback'), client_id = oidc_client_id, cookie_name=getenv('OIDC_COOKIE_NAME', 'oidc_' + oidc_client_id), diff --git a/scopes/tests/config.py b/scopes/tests/config.py index 06cd2ca..a4731fe 100644 --- a/scopes/tests/config.py +++ b/scopes/tests/config.py @@ -33,13 +33,13 @@ dbpassword = None dbschema = None # authentication settings -oidc_provider = 'testing:' +oidc_provider = 'test://testing' oidc_client_id = getenv('OIDC_CLIENT_ID', '12345') oidc_params = dict( - auth_url=getenv('OIDC_PROVIDER_URL', oidc_provider + '/oauth/v2/authorize'), - token_url=getenv('OIDC_TOKEN_URL', oidc_provider + '/oauth/v2/token'), - userinfo_url=getenv('OIDC_USERINFO_URL', oidc_provider + '/oidc/v1/userinfo'), - callback_url=getenv('OIDC_CALLBACK_URL', base_url + '/auth_callback'), + op_config_url=oidc_provider + '/.well-known/openid-configuration', + op_uris=None, + op_keys=None, + callback_url=getenv('OIDC_CALLBACK_URL', base_url + '/auth/callback'), client_id=oidc_client_id, principal_prefix=getenv('OIDC_PRINCIPAL_PREFIX', 'loops.'), cookie_name=getenv('OIDC_COOKIE_NAME', 'oidc_' + oidc_client_id), diff --git a/scopes/web/auth/oidc.py b/scopes/web/auth/oidc.py index 3d037aa..fcd65f8 100644 --- a/scopes/web/auth/oidc.py +++ b/scopes/web/auth/oidc.py @@ -79,13 +79,19 @@ class Principal: class Authenticator(DummyFolder): - prefix = 'auth' + prefix = 'auth.oidc' + + oidcProviderUris = ['authorization_endpoint', 'token_endpoint', + 'introspection_endpoint', 'userinfo_endpoint', + 'revocation_endpoint', 'end_session_endpoint', + 'device_authorization_endpoint', 'jwks_uri'] def __init__(self, request): self.request = request self.params = config.oidc_params self.reqUrl = config.base_url self.setCrypt(self.params.get('cookie_crypt')) + self.loadOidcProviderData() def setReqUrl(self, base, path): self.reqUrl = '/'.join((base, path)) @@ -120,7 +126,8 @@ class Authenticator(DummyFolder): request_uri=self.reqUrl, ) self.storeSession(dict(state=state, nonce=nonce, code_verifier=codeVerifier)) - loginUrl = '?'.join((self.params['auth_url'], urlencode(args))) + authUrl = self.params['op_uris']['authorization_endpoint'] + loginUrl = '?'.join((authUrl, urlencode(args))) logger.debug('login: URL %s', loginUrl) req.response.redirect(loginUrl, trusted=True) @@ -138,11 +145,13 @@ class Authenticator(DummyFolder): code_verifier=sdata['code_verifier'] ) # !set header: 'Content-Type: application/x-www-form-urlencoded' - tokenResponse = requests.post(self.params['token_url'], data=args) + tokenUrl = self.params['op_uris']['token_endpoint'] + tokenResponse = requests.post(tokenUrl, data=args) tdata = tokenResponse.json() #print('*** token response', tdata) headers = dict(Authorization='Bearer ' + tdata['access_token']) - userInfo = requests.get(self.params['userinfo_url'], headers=headers) + userInfoUrl = self.params['op_uris']['userinfo_endpoint'] + userInfo = requests.get(userInfoUrl, headers=headers) userData = userInfo.json() #print('*** user data', userData) groupInfo = userData.get('urn:zitadel:iam:org:project:roles', {}) @@ -194,6 +203,17 @@ class Authenticator(DummyFolder): data = json.loads(cookie) return data + def loadOidcProviderData(self, force=False): + if config.oidc_provider.startswith('test'): + return + if force or self.params.get('op_uris') is None: + uris = self.params['op_uris'] = {} + opData = requests.get(self.params['op_config_url']).json() + for key in self.oidcProviderUris: + uris[key] = opData[key] + if force or self.params.get('op_keys') is None: + self.params['op_keys'] = requests.get(uris['jwks_uri']).json() + @register('auth', Root) def authView(context, request):