def load_key(header, payload): jwk_set = JsonWebKey.import_key_set(self.fetch_jwk_set()) try: return jwk_set.find_by_kid(header.get('kid')) except ValueError: # re-try with new jwk set jwk_set = JsonWebKey.import_key_set( self.fetch_jwk_set(force=True)) return jwk_set.find_by_kid(header.get('kid'))
def test_import_key_set(self): jwks_public = read_file_path('jwks_public.json') key_set1 = JsonWebKey.import_key_set(jwks_public) key1 = key_set1.find_by_kid('abc') self.assertEqual(key1['e'], 'AQAB') key_set2 = JsonWebKey.import_key_set(jwks_public['keys']) key2 = key_set2.find_by_kid('abc') self.assertEqual(key2['e'], 'AQAB') key_set3 = JsonWebKey.import_key_set(json_dumps(jwks_public)) key3 = key_set3.find_by_kid('abc') self.assertEqual(key3['e'], 'AQAB') self.assertRaises(ValueError, JsonWebKey.import_key_set, 'invalid')
def load_key(header, _): alg = header.get("alg") if alg in ["HS256", "HS384", "HS512"]: # For HS256: client secret is used for id_token signing return self.client_secret elif alg in ["RS256", "RS384", "RS512"]: jwk_set = JsonWebKey.import_key_set(self.fetch_jwk_set()) try: return jwk_set.find_by_kid(header.get("kid")) except ValueError: # re-try with new jwk set jwk_set = JsonWebKey.import_key_set( self.fetch_jwk_set(force=True)) return jwk_set.find_by_kid(header.get("kid")) else: raise RuntimeError(f"Unsupported id_token algorithm: '{alg}'")
def fetch_keys(self): if not self.key_url: return response = requests.get(self.key_url) if response.ok: data = response.json() self._keyset = JsonWebKey.import_key_set(data)
def verifyToken(self, accessToken=None, jwks=None): """Verify access token :param str accessToken: access token :param dict jwks: JWKs :return: dict """ # Define an access token if not accessToken: accessToken = self.token["access_token"] # Renew a JWKs of an identity provider if needed if not jwks: result = self.updateJWKs() if not result["OK"]: return result jwks = self.jwks if not jwks: return S_ERROR("JWKs not found.") # Try to decode and verify an access token self.log.debug("Try to decode token %s with JWKs:\n" % accessToken, pprint.pformat(jwks)) try: return S_OK(jwt.decode(accessToken, JsonWebKey.import_key_set(jwks))) except Exception as e: self.log.exception(e) return S_ERROR(repr(e))
async def parse_id_token(self, token, nonce, claims_options=None): """Return an instance of UserInfo from token's ``id_token``.""" claims_params = dict( nonce=nonce, client_id=self.client_id, ) if 'access_token' in token: claims_params['access_token'] = token['access_token'] claims_cls = CodeIDToken else: claims_cls = ImplicitIDToken metadata = await self.load_server_metadata() if claims_options is None and 'issuer' in metadata: claims_options = {'iss': {'values': [metadata['issuer']]}} alg_values = metadata.get('id_token_signing_alg_values_supported') if not alg_values: alg_values = ['RS256'] jwt = JsonWebToken(alg_values) jwk_set = await self.fetch_jwk_set() try: claims = jwt.decode( token['id_token'], key=JsonWebKey.import_key_set(jwk_set), claims_cls=claims_cls, claims_options=claims_options, claims_params=claims_params, ) except ValueError: jwk_set = await self.fetch_jwk_set(force=True) claims = jwt.decode( token['id_token'], key=JsonWebKey.import_key_set(jwk_set), claims_cls=claims_cls, claims_options=claims_options, claims_params=claims_params, ) # https://github.com/lepture/authlib/issues/259 if claims.get('nonce_supported') is False: claims.params['nonce'] = None claims.validate(leeway=120) return UserInfo(claims)
def readToken(self, token): """Decode self token :param str token: token to decode :return: S_OK(dict)/S_ERROR() """ result = self.db.getKeySet() if not result["OK"]: return result try: return S_OK(jwt.decode(token, JsonWebKey.import_key_set(result["Value"].as_dict()))) except Exception as e: sLog.exception(e) return S_ERROR(repr(e))
def load_config(): load_dotenv() config = { **os.environ, } # symmetric key, therefore no need to update or anything # we use https anyways :) with open('static/keys.json', 'r') as keys: config["KEY_SET"] = JsonWebKey.import_key_set(json.loads(keys.read())) config["OAUTH2_JWT_KEY"] = config["KEY_SET"].keys[0] config["ADMINS"] = str(config["ADMINS"]).split(",") return config
def validate_jwks(self): """Client's JSON Web Key Set [RFC7517] document value, which contains the client's public keys. The value of this field MUST be a JSON object containing a valid JWK Set. These keys can be used by higher-level protocols that use signing or encryption. This parameter is intended to be used by clients that cannot use the "jwks_uri" parameter, such as native clients that cannot host public URLs. The "jwks_uri" and "jwks" parameters MUST NOT both be present in the same request or response. """ if 'jwks' in self: if 'jwks_uri' in self: # The "jwks_uri" and "jwks" parameters MUST NOT both be present raise InvalidClaimError('jwks') jwks = self['jwks'] try: key_set = JsonWebKey.import_key_set(jwks) if not key_set: raise InvalidClaimError('jwks') except ValueError: raise InvalidClaimError('jwks')
def test_keys(): """Try to store/get/remove keys""" # JWS jws = JsonWebSignature(algorithms=["RS256"]) code_payload = { "user_id": "user", "scope": "scope", "client_id": "client", "redirect_uri": "redirect_uri", "code_challenge": "code_challenge", } # Token metadata header = {"alg": "RS256"} payload = { "sub": "user", "iss": "issuer", "scope": "scope", "setup": "setup", "group": "my_group" } # Remove all keys result = db.removeKeys() assert result["OK"], result["Message"] # Check active keys result = db.getActiveKeys() assert result["OK"], result["Message"] assert result["Value"] == [] # Create new one result = db.getPrivateKey() assert result["OK"], result["Message"] private_key = result["Value"] assert isinstance(private_key, RSAKey) # Sign token header["kid"] = private_key.thumbprint() # Find key by KID result = db.getPrivateKey(header["kid"]) assert result["OK"], result["Message"] # as_dict has no arguments for authlib < 1.0.0 # for authlib >= 1.0.0: assert result["Value"].as_dict(True) == private_key.as_dict(True) # Sign token token = jwt.encode(header, payload, private_key) # Sign auth code code = jws.serialize_compact(header, json_b64encode(code_payload), private_key) # Get public key set result = db.getKeySet() keyset = result["Value"] assert result["OK"], result["Message"] # as_dict has no arguments for authlib < 1.0.0 # for authlib >= 1.0.0: assert bool([ key for key in keyset.as_dict(True)["keys"] if key["kid"] == header["kid"] ]) # Read token _payload = jwt.decode(token, JsonWebKey.import_key_set(keyset.as_dict())) assert _payload == payload # Read auth code data = jws.deserialize_compact(code, keyset.keys[0]) _code_payload = json_loads(urlsafe_b64decode(data["payload"])) assert _code_payload == code_payload
async def get_jwks(url: str) -> KeySet: async with httpx.AsyncClient() as client: resp = await client.get(url) resp.raise_for_status() return JsonWebKey.import_key_set(resp.json())
def load_key(header, payload): jwk_set = JsonWebKey.import_key_set(provider.fetch_jwk_set(force=True)) return jwk_set.find_by_kid(header.get("kid"))
def get_validate_key(self): with open(get_file_path('jwks_public.json'), 'r') as f: return JsonWebKey.import_key_set(json.load(f))
def jwks(self): return JsonWebKey.import_key_set(self.jwks_data())