auth, work in progress: decode id_token, + other improvements
This commit is contained in:
		
							parent
							
								
									87310b9798
								
							
						
					
					
						commit
						01fc7d2874
					
				
					 4 changed files with 30 additions and 10 deletions
				
			
		|  | @ -28,7 +28,7 @@ dbpassword = getenv('DBPASSWORD', 'secret') | ||||||
| dbschema = getenv('DBSCHEMA', 'demo') | dbschema = getenv('DBSCHEMA', 'demo') | ||||||
| 
 | 
 | ||||||
| # authentication settings | # authentication settings | ||||||
| oidc_provider = 'https://a1.cy7.de' | oidc_provider = getenv('OIDC_PROVIDER', 'https://a1.cy7.de') | ||||||
| oidc_client_id = getenv('OIDC_CLIENT_ID', '311613119816392525') | oidc_client_id = getenv('OIDC_CLIENT_ID', '311613119816392525') | ||||||
| oidc_params = dict( | oidc_params = dict( | ||||||
|     op_config_url=oidc_provider + '/.well-known/openid-configuration', |     op_config_url=oidc_provider + '/.well-known/openid-configuration', | ||||||
|  |  | ||||||
|  | @ -5,10 +5,10 @@ from scopes.storage import topic | ||||||
| 
 | 
 | ||||||
| import logging | import logging | ||||||
| import waitress | import waitress | ||||||
| from wsgiref.simple_server import make_server |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def run(app, config): | def run(app, config): | ||||||
|  |     oidc.startup()  # todo: use generic app.startServices() | ||||||
|     port = int(config.server_port) |     port = int(config.server_port) | ||||||
|     print(f'Serving on port {port}.') |     print(f'Serving on port {port}.') | ||||||
|     waitress.serve(app, port=port) |     waitress.serve(app, port=port) | ||||||
|  |  | ||||||
|  | @ -29,5 +29,5 @@ def test_app(self, config): | ||||||
| 
 | 
 | ||||||
| def test_auth(self, config): | def test_auth(self, config): | ||||||
|     from scopes.web.auth import oidc |     from scopes.web.auth import oidc | ||||||
|     oidc.loadOidcProviderData() |     oidc.startup()  # todo: use generic app.startServices() | ||||||
|     self.assertEqual(len(config.oidc_params['op_uris']), 8) |     self.assertEqual(len(config.oidc_params['op_uris']), 8) | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
| from cryptography.fernet import Fernet | from cryptography.fernet import Fernet | ||||||
| from email.utils import formatdate | from email.utils import formatdate | ||||||
| import json | import json | ||||||
|  | import jwt | ||||||
| import logging | import logging | ||||||
| import requests | import requests | ||||||
| from time import time | from time import time | ||||||
|  | @ -103,7 +104,6 @@ class Authenticator(DummyFolder): | ||||||
|         return None |         return None | ||||||
| 
 | 
 | ||||||
|     def login(self): |     def login(self): | ||||||
|         loadOidcProviderData() |  | ||||||
|         req = self.request |         req = self.request | ||||||
|         #print('***', dir(req)) |         #print('***', dir(req)) | ||||||
|         state = util.rndstr() |         state = util.rndstr() | ||||||
|  | @ -142,7 +142,9 @@ class Authenticator(DummyFolder): | ||||||
|         tokenUrl = self.params['op_uris']['token_endpoint'] |         tokenUrl = self.params['op_uris']['token_endpoint'] | ||||||
|         tokenResponse = requests.post(tokenUrl, data=args) |         tokenResponse = requests.post(tokenUrl, data=args) | ||||||
|         tdata =  tokenResponse.json() |         tdata =  tokenResponse.json() | ||||||
|         #print('*** token response', tdata) |         print('*** token response', tdata) | ||||||
|  |         claims = self.getIdTokenData(tdata['id_token']) | ||||||
|  |         print('*** token id claims', claims) | ||||||
|         headers = dict(Authorization='Bearer ' + tdata['access_token']) |         headers = dict(Authorization='Bearer ' + tdata['access_token']) | ||||||
|         userInfoUrl = self.params['op_uris']['userinfo_endpoint'] |         userInfoUrl = self.params['op_uris']['userinfo_endpoint'] | ||||||
|         userInfo = requests.get(userInfoUrl, headers=headers) |         userInfo = requests.get(userInfoUrl, headers=headers) | ||||||
|  | @ -189,7 +191,6 @@ class Authenticator(DummyFolder): | ||||||
|         cookie = self.request.getCookies().get(self.params['cookie_name']) |         cookie = self.request.getCookies().get(self.params['cookie_name']) | ||||||
|         if cookie is None: |         if cookie is None: | ||||||
|             return {} |             return {} | ||||||
|             #raise ValueError('Missing authentication cookie') |  | ||||||
|         if self.cookieCrypt: |         if self.cookieCrypt: | ||||||
|             cookie = self.cookieCrypt.decrypt(cookie) |             cookie = self.cookieCrypt.decrypt(cookie) | ||||||
|         #print('*** loadSession', self.params['cookie_name'], cookie) |         #print('*** loadSession', self.params['cookie_name'], cookie) | ||||||
|  | @ -197,6 +198,23 @@ class Authenticator(DummyFolder): | ||||||
|         data = json.loads(cookie) |         data = json.loads(cookie) | ||||||
|         return data |         return data | ||||||
| 
 | 
 | ||||||
|  |     def getIdTokenData(self, token): | ||||||
|  |         keyUri = self.params['op_uris']['jwks_uri'] | ||||||
|  |         jwksClient = jwt.PyJWKClient(keyUri) | ||||||
|  |         key = jwksClient.get_signing_key_from_jwt(token) | ||||||
|  |         return jwt.decode(token, key, options=dict(verify_aud=False)) | ||||||
|  |         header = jwt.get_unverified_header(token) | ||||||
|  |         kid = header['kid'] | ||||||
|  |         key = self.loadOidcKeys()[kid] | ||||||
|  |         return jwt.decode(token, key, audience=self.params.client_id) | ||||||
|  | 
 | ||||||
|  |     def loadOidcKeys(self): | ||||||
|  |         result = {} | ||||||
|  |         keyUri = self.params['op_uris']['jwks_uri'] | ||||||
|  |         for k in requests.get(keyUri).json()['keys']: | ||||||
|  |             result[k['kid']] = jwt.PyJWK(k) | ||||||
|  |         return result | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| @register('auth', Root) | @register('auth', Root) | ||||||
| def authView(context, request): | def authView(context, request): | ||||||
|  | @ -218,6 +236,11 @@ def logout(context, request): | ||||||
|     return DefaultView(context, request) |     return DefaultView(context, request) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def startup(): | ||||||
|  |     loadOidcProviderData() | ||||||
|  |     #app.Publication.registerBeforeTraversal( | ||||||
|  |     #       lambda req: req.setPrincipal(authentication.authenticate(req)) | ||||||
|  | 
 | ||||||
| oidcProviderUris = ['authorization_endpoint', 'token_endpoint',  | oidcProviderUris = ['authorization_endpoint', 'token_endpoint',  | ||||||
|                     'introspection_endpoint', 'userinfo_endpoint', |                     'introspection_endpoint', 'userinfo_endpoint', | ||||||
|                     'revocation_endpoint', 'end_session_endpoint', |                     'revocation_endpoint', 'end_session_endpoint', | ||||||
|  | @ -230,8 +253,5 @@ def loadOidcProviderData(force=False): | ||||||
|         opData = requests.get(params['op_config_url']).json() |         opData = requests.get(params['op_config_url']).json() | ||||||
|         for key in oidcProviderUris: |         for key in oidcProviderUris: | ||||||
|             uris[key] = opData[key] |             uris[key] = opData[key] | ||||||
|     if force or params.get('op_keys') is None: |     #if force or params.get('op_keys') is None: | ||||||
|         params['op_keys'] = requests.get(uris['jwks_uri']).json()['keys'] |         params['op_keys'] = requests.get(uris['jwks_uri']).json()['keys'] | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue