def test_verify_client_client_secret_basic(self): _token = "{}:{}".format(client_id, client_secret) token = as_unicode(base64.b64encode(as_bytes(_token))) authz_token = "Basic {}".format(token) res = verify_client(self.endpoint_context, {}, authorization_info=authz_token, endpoint="token") assert set(res.keys()) == {"method", "client_id"} assert res["method"] == "client_secret_basic"
def _add_passwd(keyjar, conf, kid): if keyjar: _keys = keyjar.get_encrypt_key(key_type="oct", kid=kid) if _keys: pw = as_unicode(_keys[0].k) if "kwargs" in conf: conf["kwargs"]["password"] = pw else: conf["password"] = pw
def test_client_secret_basic(): _token = "{}:{}".format(client_id, client_secret) token = as_unicode(base64.b64encode(as_bytes(_token))) authz_token = "Basic {}".format(token) authn_info = ClientSecretBasic(endpoint_context).verify({}, authz_token) assert authn_info["client_id"] == client_id
def factory(ec, code=None, token=None, refresh=None, jwks_def=None, **kwargs): """ Create a token handler :param code: :param token: :param refresh: :param jwks_def: :return: TokenHandler instance """ TTYPE = {"code": "A", "token": "T", "refresh": "R"} if jwks_def: kj = init_key_jar(**jwks_def) else: kj = None args = {} if code: if kj: _keys = kj.get_encrypt_key(key_type="oct", kid="code") if _keys: code["password"] = as_unicode(_keys[0].k) args["code_handler"] = init_token_handler(ec, code, TTYPE["code"]) if token: if kj: _keys = kj.get_encrypt_key(key_type="oct", kid="token") if _keys: token["password"] = as_unicode(_keys[0].k) args["access_token_handler"] = init_token_handler( ec, token, TTYPE["token"]) if refresh: if kj: _keys = kj.get_encrypt_key(key_type="oct", kid="refresh") if _keys: refresh["password"] = as_unicode(_keys[0].k) args["refresh_token_handler"] = init_token_handler( ec, refresh, TTYPE["refresh"]) return TokenHandler(**args)
def test_csb_wrong_secret(self): _token = "{}:{}".format(client_id, "pillow") token = as_unicode(base64.b64encode(as_bytes(_token))) authz_token = "Basic {}".format(token) assert self.method.is_usable(authorization_token=authz_token) with pytest.raises(AuthnFailure): self.method.verify(authorization_token=authz_token)
def test_client_secret_basic(self): _token = "{}:{}".format(client_id, client_secret) token = as_unicode(base64.b64encode(as_bytes(_token))) authz_token = "Basic {}".format(token) assert self.method.is_usable(authorization_token=authz_token) authn_info = self.method.verify(authorization_token=authz_token) assert authn_info["client_id"] == client_id
def request(self, location, fragment_enc=False): """ Given a URL this method will add a fragment, a query part or extend a query part if it already exists with the information in this instance. :param location: A URL :param fragment_enc: Whether the information should be placed in a fragment (True) or in a query part (False) :return: The extended URL """ _l = as_unicode(location) _qp = as_unicode(self.to_urlencoded()) if fragment_enc: return "%s#%s" % (_l, _qp) else: if "?" in location: return "%s&%s" % (_l, _qp) else: return "%s?%s" % (_l, _qp)
def new_cookie(endpoint_context, cookie_name=None, **kwargs): if endpoint_context.cookie_dealer: _val = as_unicode(b64e(as_bytes(json.dumps(kwargs)))) return endpoint_context.cookie_dealer.create_cookie( _val, typ="sso", cookie_name=cookie_name, ttl=endpoint_context.sso_ttl) else: return None
def verify_cookie_signature(sig, key, *parts): """Constant time verifier for signatures :param sig: The signature hexdigest to check :type sig: str :param key: The HMAC key to use. :type key: bytes :param parts: List of parts to include in the MAC :type parts: list of bytes or strings :raises: `InvalidCookieSign` when the signature is wrong """ return safe_str_cmp(as_unicode(sig), cookie_signature(key, *parts))
def basic_authn(authorization_token): if not authorization_token.startswith("Basic "): raise AuthnFailure("Wrong type of authorization token") _tok = as_bytes(authorization_token[6:]) # Will raise ValueError type exception if not base64 encoded _tok = base64.b64decode(_tok) part = [unquote_plus(p) for p in as_unicode(_tok).split(":")] if len(part) == 2: return dict(zip(["id", "secret"], part)) else: raise ValueError("Illegal token")
def parse_cookie(name, seed, kaka, enc_key=None): """Parses and verifies a cookie value Parses a cookie created by `make_cookie` and verifies it has not been tampered with. You need to provide the same `seed` and `enc_key` used when creating the cookie, otherwise the verification fails. See `make_cookie` for details about the verification. :param seed: A seed key used for the HMAC signature :type seed: bytes :param kaka: The cookie :param enc_key: The encryption key used. :type enc_key: bytes or None :raises InvalidCookieSign: When verification fails. :return: A tuple consisting of (payload, timestamp) or None if parsing fails """ if not kaka: return None seed = as_unicode(seed) parts = cookie_parts(name, kaka) if parts is None: return None elif len(parts) == 3: # verify the cookie signature cleartext, timestamp, sig = parts if not verify_cookie_signature(sig, seed, cleartext, timestamp): raise InvalidCookieSign() return cleartext, timestamp elif len(parts) == 4: # encrypted and signed timestamp = parts[0] iv = base64.b64decode(parts[1]) ciphertext = base64.b64decode(parts[2]) tag = base64.b64decode(parts[3]) ct = ciphertext + tag # Make sure the key is 32-Bytes long key = _make_hashed_key((enc_key, seed)) aesgcm = AESGCM(key) # timestamp does not need to be encrypted, just MAC'ed, # so we add it to 'Associated Data' only. aad = timestamp.encode('utf-8') try: cleartext = aesgcm.decrypt(iv, ct, aad) except JWEException: raise InvalidCookieSign() return cleartext.decode('utf-8'), timestamp return None
def test_encryption_key(): sk = SYMKey(key="df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b") _enc = sk.encryption_key(alg="A128KW") _v = as_unicode(b64e(_enc)) assert _v == "xCo9VhtommCTGMWi-RyWBw" sk = SYMKey(key="df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b") _enc = sk.encryption_key(alg="A192KW") _v = as_unicode(b64e(_enc)) assert _v == "xCo9VhtommCTGMWi-RyWB14GQqHAGC86" sk = SYMKey(key="df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b") _enc = sk.encryption_key(alg="A256KW") _v = as_unicode(b64e(_enc)) assert _v == "xCo9VhtommCTGMWi-RyWB14GQqHAGC86vweU_Pi62X8" ek = sha256_digest( "YzE0MjgzNmRlODI5Yzg2MGYyZTRjNGE0NTZlMzBkZDRiNzJkNDA5MzUzNjM0ODkzM2E2MDk3ZWY" )[:16] assert as_unicode(b64e(ek)) == "yf_UUkAFZ8Pn_prxPPgu9w" sk = SYMKey( key= "YzE0MjgzNmRlODI5Yzg2MGYyZTRjNGE0NTZlMzBkZDRiNzJkNDA5MzUzNjM0ODkzM2E2MDk3ZWY" ) _enc = sk.encryption_key(alg="A128KW") _v = as_unicode(b64e(_enc)) assert _v == as_unicode(b64e(ek))
def test_verify_client_client_secret_basic(self): _token = "{}:{}".format(client_id, client_secret) token = as_unicode(base64.b64encode(as_bytes(_token))) authz_token = "Basic {}".format(token) http_info = {"headers": {"authorization": authz_token}} res = verify_client( self.endpoint_context, {}, http_info=http_info, endpoint=self.server.server_get("endpoint", "token"), ) assert set(res.keys()) == {"method", "client_id"} assert res["method"] == "client_secret_basic"
def cookie_parts(name, kaka): """ Give me the parts of the cookie payload :param name: A name of a cookie object :param kaka: The cookie :return: A list of parts or None if there is no cookie object with the given name """ cookie_obj = SimpleCookie(as_unicode(kaka)) morsel = cookie_obj.get(name) if morsel: return morsel.value.split("|") else: return None
def backchannel_logout(client, request='', request_args=None): """ :param request: URL encoded logout request :return: """ if request: req = BackChannelLogoutRequest().from_urlencoded(as_unicode(request)) else: req = BackChannelLogoutRequest(**request_args) kwargs = { 'aud': client.service_context.get('client_id'), 'iss': client.service_context.get('issuer'), 'keyjar': client.service_context.keyjar, 'allowed_sign_alg': client.service_context.get('registration_response').get( "id_token_signed_response_alg", "RS256") } try: req.verify(**kwargs) except (MessageException, ValueError, NotForMe) as err: raise MessageException('Bogus logout request: {}'.format(err)) # Find the subject through 'sid' or 'sub' try: sub = req[verified_claim_name('logout_token')]['sub'] except KeyError: try: sid = req[verified_claim_name('logout_token')]['sid'] except KeyError: raise MessageException('Neither "sid" nor "sub"') else: _state = client.session_interface.get_state_by_sid(sid) else: _state = client.session_interface.get_state_by_sub(sub) return _state
def modify_json_message(token): part = [as_bytes(p) for p in token.split('.')] _txt = utils.b64d(part[1]) msg = json.loads(as_unicode(_txt)) for k, v in msg.items(): msg_copy = msg.copy() del msg_copy[k] for _k in modify_str(k): msg_copy[_k] = v part[1] = utils.b64e(as_bytes(json.dumps(msg_copy))) yield b'.'.join([as_bytes(p) for p in part]) if isinstance(v, str): for _v in modify_str(v): msg_copy[k] = _v part[1] = utils.b64e(as_bytes(json.dumps(msg_copy))) yield b'.'.join([as_bytes(p) for p in part]) elif isinstance(v, int): _v = v + 1 msg_copy[k] = _v part[1] = utils.b64e(as_bytes(json.dumps(msg_copy))) yield b'.'.join([as_bytes(p) for p in part])
def verify_request_signed_by_signing_keys(smsreq): """ Verify that a JWT is signed with a key that is inside the JWT. :param smsreq: Signed Metadata Statement signing request :return: Dictionary containing 'ms' (the signed request) and 'iss' (the issuer of the JWT). """ _jws = factory(smsreq) _json = _jws.jwt.part[1] _body = json.loads(as_unicode(_json)) iss = _body['iss'] _jwks = _body['signing_keys'] _kj = jwks_to_keyjar(_jwks, iss) try: _kid = _jws.jwt.headers['kid'] except KeyError: _keys = _kj.get_signing_key(owner=iss) else: _keys = _kj.get_signing_key(owner=iss, kid=_kid) _ver = _jws.verify_compact(smsreq, _keys) # remove the JWT specific claims for k in JsonWebToken.c_param.keys(): try: del _ver[k] except KeyError: pass try: del _ver['kid'] except KeyError: pass return {'ms': MetadataStatement(**_ver), 'iss': iss}
def decrypt(self, ciphertext): dec_text = self.core.decrypt(ciphertext) dec_text = dec_text.rstrip(b' ') return as_unicode(dec_text)
def from_jwt(self, txt, keyjar, verify=True, **kwargs): """ Given a signed and/or encrypted JWT, verify its correctness and then create a class instance from the content. :param txt: The JWT :param key: keys that might be used to decrypt and/or verify the signature of the JWT :param verify: Whether the signature should be verified or not :param keyjar: A KeyJar that might contain the necessary key. :param kwargs: Extra key word arguments :return: A class instance """ algarg = {} if 'encalg' in kwargs: algarg['alg'] = kwargs['encalg'] if 'encenc' in kwargs: algarg['enc'] = kwargs['encenc'] _decryptor = jwe_factory(txt, **algarg) if _decryptor: logger.debug("JWE headers: %s", _decryptor.jwt.headers) dkeys = keyjar.get_decrypt_key(owner="") logger.debug('Decrypt class: %s', _decryptor.__class__) _res = _decryptor.decrypt(txt, dkeys) logger.debug('decrypted message: %s', _res) if isinstance(_res, tuple): txt = as_unicode(_res[0]) elif isinstance(_res, list) and len(_res) == 2: txt = as_unicode(_res[0]) else: txt = as_unicode(_res) self.jwe_header = _decryptor.jwt.headers if kwargs.get('sigalg'): _verifier = jws_factory(txt, alg=kwargs['sigalg']) else: _verifier = jws_factory(txt) if _verifier: _jwt = _verifier.jwt jso = _jwt.payload() _header = _jwt.headers key = [] # if "sender" in kwargs: # key.extend(keyjar.get_verify_key(owner=kwargs["sender"])) logger.debug("Raw JSON: %s", jso) logger.debug("JWS header: %s", _header) if _header["alg"] == "none": pass elif verify: if keyjar: key.extend(keyjar.get_jwt_verify_keys(_jwt, **kwargs)) if "alg" in _header and _header["alg"] != "none": if not key: raise MissingSigningKey("alg=%s" % _header["alg"]) logger.debug("Found signing key.") try: _verifier.verify_compact(txt, key) except NoSuitableSigningKeys: if keyjar: keyjar.update() key = keyjar.get_jwt_verify_keys(_jwt, **kwargs) _verifier.verify_compact(txt, key) self.jws_header = _jwt.headers else: jso = json.loads(txt) self.jwt = txt return self.from_dict(jso)
def authz_part2(self, user, authn_event, request, **kwargs): """ After the authentication this is where you should end up :param user: :param request: The Authorization Request :param sid: Session key :param kwargs: possible other parameters :return: A redirect to the redirect_uri of the client """ sid = setup_session(self.endpoint_context, request, user, authn_event=authn_event) try: resp_info = self.post_authentication(user, request, sid, **kwargs) except Exception as err: return self.error_response({}, "server_error", err) if "check_session_iframe" in self.endpoint_context.provider_info: ec = self.endpoint_context salt = rndstr() if not ec.sdb.is_session_revoked(sid): authn_event = ec.sdb.get_authentication_event( sid) # use the last session _state = b64e( as_bytes( json.dumps({"authn_time": authn_event["authn_time"]}))) session_cookie = ec.cookie_dealer.create_cookie( as_unicode(_state), typ="session", cookie_name=ec.cookie_name["session_management"], same_site="None", http_only=False) opbs = session_cookie[ec.cookie_name["session_management"]] logger.debug( "compute_session_state: client_id=%s, origin=%s, opbs=%s, salt=%s", request["client_id"], resp_info["return_uri"], opbs.value, salt, ) _session_state = compute_session_state(opbs.value, salt, request["client_id"], resp_info["return_uri"]) if "cookie" in resp_info: if isinstance(resp_info["cookie"], list): resp_info["cookie"].append(session_cookie) else: append_cookie(resp_info["cookie"], session_cookie) else: resp_info["cookie"] = session_cookie resp_info["response_args"]["session_state"] = _session_state # Mix-Up mitigation resp_info["response_args"]["iss"] = self.endpoint_context.issuer resp_info["response_args"]["client_id"] = request["client_id"] return resp_info
def setup_auth(self, request, redirect_uri, cinfo, cookie, acr=None, **kwargs): """ :param request: The authorization/authentication request :param redirect_uri: :param cinfo: client info :param cookie: :param acr: Default ACR, if nothing else is specified :param kwargs: :return: """ res = self.pick_authn_method(request, redirect_uri, acr, **kwargs) authn = res["method"] authn_class_ref = res["acr"] try: _auth_info = kwargs.get("authn", "") if "upm_answer" in request and request["upm_answer"] == "true": _max_age = 0 else: _max_age = max_age(request) identity, _ts = authn.authenticated_as(cookie, authorization=_auth_info, max_age=_max_age) except (NoSuchAuthentication, TamperAllert): identity = None _ts = 0 except ToOld: logger.info("Too old authentication") identity = None _ts = 0 else: if identity: try: # If identity['uid'] is in fact a base64 encoded JSON string _id = b64d(as_bytes(identity["uid"])) except BadSyntax: pass else: identity = json.loads(as_unicode(_id)) session = self.endpoint_context.sdb[identity.get("sid")] if not session or "revoked" in session: identity = None authn_args = authn_args_gather(request, authn_class_ref, cinfo, **kwargs) # To authenticate or Not if identity is None: # No! logger.info("No active authentication") logger.debug("Known clients: {}".format( list(self.endpoint_context.cdb.keys()))) if "prompt" in request and "none" in request["prompt"]: # Need to authenticate but not allowed return { "error": "login_required", "return_uri": redirect_uri, "return_type": request["response_type"], } else: return {"function": authn, "args": authn_args} else: logger.info("Active authentication") if re_authenticate(request, authn): # demand re-authentication return {"function": authn, "args": authn_args} else: # I get back a dictionary user = identity["uid"] if "req_user" in kwargs: sids = self.endpoint_context.sdb.get_sids_by_sub( kwargs["req_user"]) if (sids and user != self.endpoint_context.sdb.get_authentication_event( sids[-1]).uid): logger.debug("Wanted to be someone else!") if "prompt" in request and "none" in request["prompt"]: # Need to authenticate but not allowed return { "error": "login_required", "return_uri": redirect_uri, } else: return {"function": authn, "args": authn_args} authn_event = create_authn_event( identity["uid"], identity.get("salt", ""), authn_info=authn_class_ref, time_stamp=_ts, ) if "valid_until" in authn_event: vu = time.time() + authn.kwargs.get("expires_in", 0.0) authn_event["valid_until"] = vu return {"authn_event": authn_event, "identity": identity, "user": user}
def cookie_value(b64): return json.loads(as_unicode(b64d(as_bytes(b64))))
def test_basic_auth(): _token = "{}:{}".format(client_id, client_secret) token = as_unicode(base64.b64encode(as_bytes(_token))) res = basic_authn("Basic {}".format(token)) assert res
def test_basic_auth_wrong_label(): _token = "{}:{}".format(client_id, client_secret) token = as_unicode(base64.b64encode(as_bytes(_token))) with pytest.raises(AuthnFailure): basic_authn("Expanded {}".format(token))