Commit cce3648b authored by Mihai Patrascoiu's avatar Mihai Patrascoiu
Browse files

FTS-1674: Rework the offline validation decoding. A list of filtered keys is...

FTS-1674: Rework the offline validation decoding. A list of filtered keys is returned which will be tried until the first key that works is found
parent c4600296
Pipeline #2026355 passed with stage
in 2 minutes and 5 seconds
......@@ -271,38 +271,55 @@ class FTS3OAuth2ResourceProvider(ResourceProvider):
:param access_token:
:return: tuple(valid, credential) or tuple(False, None)
def decode(key):
log.debug('Attempt decoding using key={}'.format(key.export()))
if 'wlcg' in issuer:
audience = ''
credential = jwt.decode(access_token,
# We don't check audience for non-WLCG token
credential = jwt.decode(access_token,
options={'verify_aud': False}
return credential
except Exception:
return None
log.debug('entered validate_token_offline')
credential = None
unverified_payload = jwt.decode(access_token, verify=False)
unverified_header = jwt.get_unverified_header(access_token)
issuer = unverified_payload['iss']
key_id = unverified_header['kid']
log.debug('issuer={}, key_id={}'.format(issuer, key_id))
algorithm = unverified_header.get('alg', 'RS256')
pub_key = oidc_manager.get_provider_key(issuer, key_id)
pub_key = JWK.from_json(json.dumps(pub_key.to_dict()))
# Verify & Validate
if 'wlcg' in issuer:
audience = ''
credential = jwt.decode(access_token, pub_key.export_to_pem(), algorithms=[algorithm], audience=audience)
credential = jwt.decode(access_token,
options={'verify_aud': False} # We don't check audience for non-WLCG token)
log.debug('offline_response::: {}'.format(credential))
key_id = unverified_header.get('kid')
algorithm = unverified_header.get('alg')
log.debug('issuer={}, key_id={}, alg={}'.format(issuer, key_id, algorithm))
# Retrieval of keys
keys = oidc_manager.filter_provider_keys(issuer, key_id, algorithm)
jwkeys = [JWK.from_json(json.dumps(key.to_dict())) for key in keys]
# Find the first key which decodes the token
for jwkey in jwkeys:
credential = decode(jwkey)
if credential is not None:
log.debug('offline_response::: {}'.format(credential))
except Exception as ex:
log.debug('return False (exception)')
log.debug('return False, Exception: {}'.format(ex))
return False, None
log.debug('return True, credential')
return True, credential
if credential is None:
log.debug('No key managed to decode the token')
log.debug('return {}, credential'.format(credential is not None))
return (credential is not None), credential
def _validate_token_online(self, access_token):
......@@ -55,20 +55,25 @@ class OIDCmanager:
for keybundle in keybundles:
keybundle.cache_time = cache_time
def get_provider_key(self, issuer, kid):
def filter_provider_keys(self, issuer, kid=None, alg=None):
Get a Provider Key by ID
Return Provider Keys after applying Key ID and Algorithm filter.
If no filters match, return the full set.
:param issuer: provider
:param kid: Key ID
:return: key
:raise ValueError: if key not found
:param alg: Algorithm
:return: keys
:raise ValueError: client could not be retrieved
client = self.clients[issuer]
keys = client.keyjar.get_issuer_keys(issuer) # List of Keys (from pyjwkest)
for key in keys:
if key.kid == kid:
return key
raise ValueError("Key with kid {} not found".format(kid))
client = self.clients.get(issuer)
if client is None:
raise ValueError('Could not retrieve client for issuer={}'.format(issuer))
# List of Keys (from pyjwkest)
keys = client.keyjar.get_issuer_keys(issuer)
filtered_keys = [key for key in keys if key.kid == kid or key.alg == alg]
if len(filtered_keys) is 0:
return keys
return filtered_keys
def introspect(self, issuer, access_token):
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment