def _func(self, environ): done = False _vkeys = environ["client"].keystore.get_keys("ver", owner=None) for item in environ["item"]: if self._status == self.status or done: break try: _idt = item["id_token"] if _idt is None: continue except KeyError: continue idtoken = _idt for key, val in self._kwargs["claims"].items(): if key == "max_age": if idtoken["exp"] > (time_util.utc_time_sans_frac() + val): self._status = self.status diff = idtoken["exp"] - time_util.utc_time_sans_frac() self._message = "exp to far in the future [%d]" % diff break else: continue if val is None: if key not in idtoken: self._status = self.status self._message = "'%s' was supposed to be there" % key break elif val == {"essential":True}: pass elif "values" in val: if key not in idtoken: self._status = self.status self._message = "Missing value on '%s'" % key break else: _val = idtoken[key] if isinstance(_val, basestring): if _val not in val["values"]: self._status = self.status self._message = "Wrong value on '%s'" % key break elif isinstance(_val, int): if _val not in val["values"]: self._status = self.status self._message = "Wrong value on '%s'" % key break else: for sval in _val: if sval in val["values"]: continue self._status = self.status self._message = "Wrong value on '%s'" % key break done = True return {}
def create_new_client(self, request): """ :param request: The Client registration request :return: The client_id """ _cinfo = request.to_dict() # create new id and secret _id = rndstr(12) while _id in self.cdb: _id = rndstr(12) _cinfo["client_id"] = _id _cinfo["client_secret"] = secret(self.seed, _id) _cinfo["client_id_issued_at"] = utc_time_sans_frac() _cinfo["client_secret_expires_at"] = utc_time_sans_frac() + \ self.secret_lifetime # If I support client info endpoint if ClientInfoEndpoint in self.endp: _cinfo["registration_access_token"] = rndstr(32) _cinfo["registration_client_uri"] = "%s%s?client_id=%s" % ( self.client_info_url, ClientInfoEndpoint.etype, _id) if "redirect_uris" in request: _cinfo["redirect_uris"] = self._uris_to_tuples( request["redirect_uris"]) self.cdb[_id] = _cinfo return _id
def __call__(self, sid, sinfo=None, kid='', **kwargs): keys = self.keyjar.get_signing_key(alg2keytype(self.sign_alg), owner='', kid=kid) if not keys: raise NoSuitableSigningKeys('kid={}'.format(kid)) key = keys[0] # Might be more then one if kid == '' rt = ' '.join(sinfo['response_type']) try: exp = utc_time_sans_frac() + self.lifetime[rt] except KeyError: exp = utc_time_sans_frac() + self.lifetime[''] _jti = '{}-{}'.format(self.type, uuid.uuid4().hex) _tok = TokenAssertion( iss=self.iss, azp=sinfo['client_id'], sub=sinfo['sub'], kid=key.kid, exp=exp, jti=_jti ) self.db[_jti] = sid try: _tok['aud'] = kwargs['aud'] except KeyError: pass return _tok.to_jwt([key], self.sign_alg)
def update_registered_data(self, sws_id, sws_message, unpacked_request): client_id = unpacked_request["client_id"] if client_id not in self.cdb: client_secret = secret(self.seed, client_id) _rat = rndstr(32) reg_enp = "" for endp in self.endp: if endp == RegistrationEndpoint: reg_enp = "%s%s" % (self.baseurl, endp.etype) break self.cdb[client_id] = { "client_id": client_id, "client_secret": client_secret, "registration_access_token": _rat, "registration_client_uri": "%s?client_id=%s" % (reg_enp, client_id), "client_secret_expires_at": utc_time_sans_frac() + 86400, "client_id_issued_at": utc_time_sans_frac()} self.cdb[_rat] = client_id #TODO Why should this be ignored "redirect_uris", "policy_uri", "logo_uri", "tos_uri",? _cinfo = self.do_client_registration(sws_message.to_dict(), client_id, ignore=["redirect_uris", "policy_uri", "logo_uri", "tos_uri", "client_id", "client_secret", "registration_access_token", "registration_client_uri", "client_secret_expires_at", "client_id_issued_at", "software_statement"]) if isinstance(_cinfo, Response): return _cinfo cinfo = self.cdb[client_id] args = dict([(k, v) for k, v in _cinfo.items() if k in RegistrationRequest.c_param]) for key, value in iteritems(args): cinfo[key] = value # Update cache cinfo[SWS_CACHE_KEY] = sws_id self.cdb[client_id] = cinfo try: self.cdb.sync() except AttributeError: # Not all databases can be sync'ed pass
def create_authz_session(self, sub, areq, id_token=None, oidreq=None): """ :param sub: Identifier for the user, this is the real identifier :param areq: The AuthorizationRequest instance :param id_token: An IDToken instance :param oidreq: An OpenIDRequest instance :return: The session identifier, which is the database key """ sid = self.token.key(user=sub, areq=areq) access_grant = self.token(sid=sid) _dic = { "oauth_state": "authz", "local_sub": sub, "sub": sub, "code": access_grant, "code_used": False, "authzreq": areq.to_json(), "client_id": areq["client_id"], #"expires_in": self.grant_expires_in, "client_secret_expires_at": utc_time_sans_frac() + self.grant_expires_in, "client_id_issued_at": utc_time_sans_frac(), "revoked": False, } try: _val = areq["nonce"] if _val: _dic["nonce"] = _val except (AttributeError, KeyError): pass for key in ["redirect_uri", "state", "scope", "si_redirects"]: try: _dic[key] = areq[key] except KeyError: pass if id_token: _dic["id_token"] = id_token if oidreq: _dic["oidreq"] = oidreq.to_json() self._db[sid] = _dic self.uid2sid[sub] = sid return sid
def construct_AccessTokenRequest(self, request=AccessTokenRequest, request_args=None, extra_args=None, **kwargs): grant = self.get_grant(**kwargs) if not grant.is_valid(): raise GrantExpired("Authorization Code to old %s > %s" % ( utc_time_sans_frac(), grant.grant_expiration_time)) if request_args is None: request_args = {} request_args["code"] = grant.code if "grant_type" not in request_args: request_args["grant_type"] = "authorization_code" if "client_id" not in request_args: request_args["client_id"] = self.client_id elif not request_args["client_id"]: request_args["client_id"] = self.client_id return self.construct_request(request, request_args, extra_args)
def filter_by_permission(authz, scope): """ :param authz: An IntrospectionResponse instance :param scope: The scope that access is asked for """ now = utc_time_sans_frac() try: assert now < authz["expires_at"] except KeyError: pass except AssertionError: return False for perm in authz["permissions"]: try: assert now < perm["expires_at"] except KeyError: pass except AssertionError: return False try: assert scope in perm["scopes"] except AssertionError: pass else: return True return False
def verify(self, **kwargs): super(IdToken, self).verify(**kwargs) if "aud" in self: if "client_id" in kwargs: # check that I'm among the recipients if kwargs["client_id"] not in self["aud"]: raise NotForMe( "{} not in aud:{}".format(kwargs["client_id"], self["aud"]), self) # Then azp has to be present and be one of the aud values if len(self["aud"]) > 1: try: assert "azp" in self except AssertionError: raise VerificationError("azp missing", self) else: try: assert self["azp"] in self["aud"] except AssertionError: raise VerificationError( "Mismatch between azp and aud claims", self) if "azp" in self: if "client_id" in kwargs: if kwargs["client_id"] != self["azp"]: raise NotForMe( "{} != azp:{}".format(kwargs["client_id"], self["azp"]), self) _now = time_util.utc_time_sans_frac() try: _skew = kwargs['skew'] except KeyError: _skew = 0 try: _exp = self['exp'] except KeyError: raise MissingRequiredAttribute('exp') else: if (_now - _skew) > _exp: raise EXPError('Invalid expiration time') try: _storage_time = kwargs['nonce_storage_time'] except KeyError: _storage_time = NONCE_STORAGE_TIME try: _iat = self['iat'] except KeyError: raise MissingRequiredAttribute('iat') else: if (_iat + _storage_time) < (_now - _skew): raise IATError('Issued too long ago') return True
def permission_request_allowed(self, ticket, identity): """ Verify that whatever permission requests the ticket represents they are now allow. :param ticket: The ticket :param identity: Who has the ticket :return: Dictionary, with permission request as key and identifiers of authz decisions that permits the requests as values. """ _tinfo = self.rpt_factory.unpack(ticket) if self.is_expired(_tinfo): raise TicketError('expired', '{} > {}'.format(utc_time_sans_frac(), _tinfo['exp'])) try: prrs = self.permission_requests[ticket] except KeyError: logger.warning("Someone is using a ticket that doesn't exist") raise TicketError('invalid', ticket) else: result = {} for prr in prrs: owner = self.resource_set.owner(prr['resource_set_id']) _adids = self.authz_db.match(owner, identity, **prr.to_dict()) if not _adids: # all or nothing raise TicketError('not_authorized') result[prr.to_json()] = _adids return result
def key(self, user="", areq=None): """ Return a key (the session id) that are based on some session connected data :param user: User id :param areq: The authorization request :return: A hash """ csum = hmac.new(self.secret.encode("utf-8"), digestmod=hashlib.sha224) csum.update(("%s" % utc_time_sans_frac()).encode("utf-8")) csum.update(("%f" % random.random()).encode("utf-8")) if user: csum.update(user.encode("utf-8")) if areq: try: csum.update(areq["state"].encode("utf-8")) except KeyError: pass try: for val in areq["scope"]: csum.update(val.encode("utf-8")) except KeyError: pass try: csum.update(areq["redirect_uri"].encode("utf-8")) except KeyError: pass return csum.hexdigest() # 56 bytes long, 224 bits
def filter_by_permission(intro, scope=None): """ :param intro: An IntrospectionResponse instance :param scope: The scope that access is asked for :return: list of resource_set_description ids :rtype: list """ rsids = [] now = utc_time_sans_frac() try: assert now < intro["exp"] except KeyError: pass except AssertionError: return False for perm in intro["permissions"]: try: assert now < perm["exp"] except KeyError: pass except AssertionError: continue try: assert scope in perm["scopes"] except AssertionError: pass else: rsids.append(perm["resource_set_id"]) return rsids
def test_get_session_management_id(self): now = utc_time_sans_frac() smid = "session_management_id" idval = { "nonce": "KUEYfRM2VzKDaaKD", "sub": "EndUserSubject", "iss": "https://example.com", "exp": now + 3600, "iat": now, "aud": self.consumer.client_id, "sid": smid, } idts = IdToken(**idval) _signed_jwt = idts.to_jwt(key=KC_RSA.keys(), algorithm="RS256") _state = "state" self.consumer.sdb[_state] = { "redirect_uris": ["https://example.org/cb"] } resp = AuthorizationResponse(id_token=_signed_jwt, state=_state) self.consumer.consumer_config["response_type"] = ["id_token"] self.consumer.parse_authz(resp.to_urlencoded()) assert self.consumer.sso_db["state"]["smid"] == smid assert session_get(self.consumer.sso_db, "smid", smid) == [_state]
def key(self, user="", areq=None): csum = hmac.new(self.secret, digestmod=hashlib.sha224) csum.update("%s" % utc_time_sans_frac()) csum.update("%f" % random.random()) if user: csum.update(user) if areq: try: csum.update(areq["state"]) except KeyError: pass try: for val in areq["scope"]: csum.update(val) except KeyError: pass try: csum.update(areq["redirect_uri"]) except KeyError: pass return csum.hexdigest() # 56 bytes long, 224 bits
def key(self, user="", areq=None): csum = hmac.new(self.secret, digestmod=hashlib.sha224) csum.update("%s" % utc_time_sans_frac()) csum.update("%f" % random.random()) if user: csum.update(user) if areq: try: csum.update(areq["state"]) except KeyError: pass try: for val in areq["scope"]: csum.update(val) except KeyError: pass try: csum.update(areq["redirect_uri"]) except KeyError: pass return csum.digest() # 28 bytes long, 224 bits
def assertion_jwt(cli, keys, audience, algorithm, lifetime=600): _now = utc_time_sans_frac() at = AuthnToken(iss=cli.client_id, sub=cli.client_id, aud=audience, jti=rndstr(32), exp=_now + lifetime, iat=_now) return at.to_jwt(key=keys, algorithm=algorithm)
def registration_endpoint(self, data): req = self.parse_registration_request(data) client_secret = rndstr() expires = utc_time_sans_frac() + self.registration_expires_in if req["type"] == "client_associate": client_id = rndstr(10) self.client[client_id] = { "client_secret": client_secret, "info": req.to_dict(), "expires": expires } else: client_id = req.client_id _cinfo = self.client[req.client_id] _cinfo["info"].update(req.to_dict()) _cinfo["client_secret"] = client_secret _cinfo["expires"] = expires resp = RegistrationResponseCARS(client_id=client_id, client_secret=client_secret, expires_at=expires) response = Response() response.headers = {"content-type":"application/json"} response.text = resp.to_json() return response
def construct_AccessTokenRequest(self, request=AccessTokenRequest, request_args=None, extra_args=None, **kwargs): grant = self.get_grant(**kwargs) if not grant.is_valid(): raise GrantExpired("Authorization Code to old %s > %s" % ( utc_time_sans_frac(), grant.grant_expiration_time)) if request_args is None: request_args = {} request_args["code"] = grant.code # MUST be same state as for the AuthReq shash = base64.urlsafe_b64encode( hashlib.sha256(kwargs['state'].encode('utf8')).digest()) request_args['state_hash'] = shash.decode('ascii') if "grant_type" not in request_args: request_args["grant_type"] = "authorization_code" if "client_id" not in request_args: request_args["client_id"] = self.client_id elif not request_args["client_id"]: request_args["client_id"] = self.client_id return self.construct_request(request, request_args, extra_args)
def construct_AccessTokenRequest(self, request=AccessTokenRequest, request_args=None, extra_args=None, **kwargs): grant = self.get_grant(**kwargs) if not grant.is_valid(): raise GrantExpired( "Authorization Code to old %s > %s" % (utc_time_sans_frac(), grant.grant_expiration_time)) if request_args is None: request_args = {} request_args["code"] = grant.code try: request_args['state'] = kwargs['state'] except KeyError: pass if "grant_type" not in request_args: request_args["grant_type"] = "authorization_code" if "client_id" not in request_args: request_args["client_id"] = self.client_id elif not request_args["client_id"]: request_args["client_id"] = self.client_id return self.construct_request(request, request_args, extra_args)
def assertion_jwt(cli, keys, audience, algorithm, lifetime=600): _now = utc_time_sans_frac() at = AuthnToken(iss=cli.client_id, sub=cli.client_id, aud=audience, jti=rndstr(16), exp=_now + lifetime, iat=_now) return at.to_jwt(key=keys, algorithm=algorithm)
def __call__(self, sid="", ttype="", **kwargs): """ Return a token. :param ttype: Type of token :param prev: Previous token, if there is one to go from :param sid: Session id :return: """ if not ttype and self.type: ttype = self.type else: ttype = "A" tmp = "" rnd = "" while rnd == tmp: # Don't use the same random value again rnd = rndstr(32) # Ultimate length multiple of 16 issued_at = "{}".format(utc_time_sans_frac()) if ttype == "R": # kwargs["sinfo"] is a dictionary and we do not want updates... self.token_storage[sid] = copy.deepcopy(kwargs["sinfo"]) return base64.b64encode( self.crypt.encrypt(lv_pack(rnd, ttype, sid, issued_at).encode())).decode("utf-8")
def test_faulty_idtoken(self): _now = time_util.utc_time_sans_frac() idval = { "nonce": "KUEYfRM2VzKDaaKD", "sub": "EndUserSubject", "iss": "https://alpha.cloud.nds.rub.de", "exp": _now + 3600, "iat": _now, "aud": "TestClient", } idts = IdToken(**idval) key = SYMKey(key="TestPassword") _signed_jwt = idts.to_jwt(key=[key], algorithm="HS256") # Mess with the signed id_token p = _signed_jwt.split(".") p[2] = "aaa" _faulty_signed_jwt = ".".join(p) _info = { "access_token": "accessTok", "id_token": _faulty_signed_jwt, "token_type": "Bearer", "expires_in": 3600, } at = AccessTokenResponse(**_info) with pytest.raises(BadSignature): at.verify(key=[key])
def construct_AccessTokenRequest(self, request: Type[AccessTokenRequest] = None, request_args=None, extra_args=None, **kwargs) -> AccessTokenRequest: if request is None: request = self.message_factory.get_request_type("token_endpoint") if request_args is None: request_args = {} if request is not ROPCAccessTokenRequest: grant = self.get_grant(**kwargs) if not grant.is_valid(): raise GrantExpired( "Authorization Code to old %s > %s" % (utc_time_sans_frac(), grant.grant_expiration_time)) request_args["code"] = grant.code try: request_args["state"] = kwargs["state"] except KeyError: pass if "grant_type" not in request_args: request_args["grant_type"] = "authorization_code" if "client_id" not in request_args: request_args["client_id"] = self.client_id elif not request_args["client_id"]: request_args["client_id"] = self.client_id return self.construct_request(request, request_args, extra_args)
def check_permission(authzdesc, oper): """ :param authzdesc: An AuthzDescription instance :param oper: The operation (HTTP method) """ now = utc_time_sans_frac() try: assert now < authzdesc["expires_at"] except KeyError: pass except AssertionError: return False for perm in authzdesc["permissions"]: try: assert now < perm["expires_at"] except KeyError: pass except AssertionError: return False try: assert OPER2SCOPE[oper] in perm["scopes"] except AssertionError: pass else: return True return False
def pack(self, kid='', owner='', **kwargs): keys = self.keyjar.get_signing_key(jws.alg2keytype(self.sign_alg), owner=owner, kid=kid) if not keys: raise NoSuitableSigningKeys('kid={}'.format(kid)) key = keys[0] # Might be more then one if kid == '' iat = utc_time_sans_frac() if not 'exp' in kwargs: kwargs['exp'] = iat + self.lifetime try: _encrypt = kwargs['encrypt'] except KeyError: _encrypt = self.encrypt else: del kwargs['encrypt'] _jwt = self.message_type(iss=self.iss, iat=iat, **kwargs) if 'jti' in self.message_type.c_param: try: _jti = kwargs['jti'] except: _jti = uuid.uuid4().hex _jwt['jti'] = _jti _jws = _jwt.to_jwt([key], self.sign_alg) if _encrypt: return self._encrypt(_jws) else: return _jws
def create_new_client(self, request, restrictions): """ :param request: The Client registration request :param restrictions: Restrictions on the client :return: The client_id """ _cinfo = request.to_dict() self.match_client_request(_cinfo) # create new id and secret _id = rndstr(12) while _id in self.cdb: _id = rndstr(12) _cinfo["client_id"] = _id _cinfo["client_secret"] = secret(self.seed, _id) _cinfo["client_id_issued_at"] = utc_time_sans_frac() _cinfo["client_secret_expires_at"] = utc_time_sans_frac() + \ self.secret_lifetime # If I support client info endpoint if ClientInfoEndpoint in self.endp: _cinfo["registration_access_token"] = rndstr(32) _cinfo["registration_client_uri"] = "%s%s%s?client_id=%s" % ( self.name, self.client_info_url, ClientInfoEndpoint.etype, _id) if "redirect_uris" in request: _cinfo["redirect_uris"] = self._uris_to_tuples( request["redirect_uris"]) self.load_keys(request, _id, _cinfo["client_secret"]) try: _behav = self.behavior['client_registration'] except KeyError: pass else: self.verify_correct(_cinfo, _behav) self.set_token_policy(_id, _cinfo) self.cdb[_id] = _cinfo return _id
def set(self, token, permissions=""): now = utc_time_sans_frac() _info = {"expires_at": now + self.lifetime, "issued_at": now} if permissions: _info["permissions"] = permissions self.db[token] = _info
def assertion_jwt(cli, keys, audience, algorithm, lifetime=600): _now = utc_time_sans_frac() at = AuthnToken(iss=cli.client_id, sub=cli.client_id, aud=audience, jti=rndstr(32), exp=_now + lifetime, iat=_now) logger.debug('AuthnToken: {}'.format(at.to_dict())) return at.to_jwt(key=keys, algorithm=algorithm)
def is_expired(self, token, when=None): """Return if token is still valid.""" if when is None: now = utc_time_sans_frac() else: now = when eat = self.expires_at(token) return bool(now > eat)
def register_permission(self, owner, rpt, rsid, scopes): now = utc_time_sans_frac() perm = AuthzDescription(resource_set_id=rsid, scopes=scopes, exp=now + self.session.lifetime, iat=now) self.permit.set_accepted(owner, rpt, perm)
def valid(self, token): info = self.unpack(token) if info['jti'] in self.db: if info['exp'] >= utc_time_sans_frac(): return True return False
def create_new_client(self, request, restrictions): """ :param request: The Client registration request :param restrictions: Restrictions on the client :return: The client_id """ _cinfo = request.to_dict() self.match_client_request(_cinfo) # create new id and secret _id = rndstr(12) while _id in self.cdb: _id = rndstr(12) _cinfo["client_id"] = _id _cinfo["client_secret"] = secret(self.seed, _id) _cinfo["client_id_issued_at"] = utc_time_sans_frac() _cinfo["client_secret_expires_at"] = utc_time_sans_frac( ) + self.secret_lifetime # If I support client info endpoint if ClientInfoEndpoint in self.endp: _cinfo["registration_access_token"] = rndstr(32) _cinfo["registration_client_uri"] = "%s%s%s?client_id=%s" % ( self.name, self.client_info_url, ClientInfoEndpoint.etype, _id) if "redirect_uris" in request: _cinfo["redirect_uris"] = self._uris_to_tuples( request["redirect_uris"]) self.load_keys(request, _id, _cinfo["client_secret"]) try: _behav = self.behavior['client_registration'] except KeyError: pass else: self.verify_correct(_cinfo, _behav) self.set_token_policy(_id, _cinfo) self.cdb[_id] = _cinfo return _id
def is_expired(self): now = utc_time_sans_frac() if self.exp < now: logger.debug('is_expired: {} < {}'.format(self.exp, now)) return True if self.sup: return self.sup.is_expired() else: return False
def update_to_token(self, token=None, issue_refresh=True, id_token="", oidreq=None, key=None): """ :param token: The access grant :param issue_refresh: If a refresh token should be issued :param id_token: An IDToken instance :param oidreq: An OpenIDRequest instance :param key: The session key. One of token or key must be given. :return: The session information as a dictionary """ if token: (typ, key) = self.token.type_and_key(token) if typ != "A": # not a access grant raise WrongTokenType("Not a grant token") dic = self._db[key] if dic["code_used"]: raise Exception("Access code already used!!") _at = self.token("T", token) dic["code_used"] = True else: dic = self._db[key] _at = self.token("T", sid=key) dic["access_token"] = _at dic["access_token_scope"] = "?" dic["oauth_state"] = "token" dic["token_type"] = "Bearer" dic["client_secret_expires_at"] = utc_time_sans_frac() + self.token_expires_in dic["expires_in"] = self.token_expires_in dic["client_id_issued_at"] = utc_time_sans_frac() if id_token: dic["id_token"] = id_token if oidreq: dic["oidreq"] = oidreq if issue_refresh: dic["refresh_token"] = self.token("R", token) self._db[key] = dic return dic
def upgrade_to_token(self, token=None, issue_refresh=False, id_token="", oidreq=None, key=None, access_grant=""): """ :param token: The access grant :param issue_refresh: If a refresh token should be issued :param id_token: An IDToken instance :param oidreq: An OpenIDRequest instance :param key: The session key. One of token or key must be given. :return: The session information as a dictionary """ if token: try: (typ, key) = self.token.type_and_key(token) except (ValueError, TypeError): (typ, key) = self.token.type_and_key(access_grant) token = access_grant if typ != "A": # not a access grant raise WrongTokenType("Not a grant token") dic = self._db[key] if dic["code_used"]: raise AccessCodeUsed() _at = self.token("T", token) dic["code_used"] = True else: dic = self._db[key] _at = self.token("T", sid=key) dic["access_token"] = _at dic["access_token_scope"] = "?" dic["oauth_state"] = "token" dic["token_type"] = "Bearer" dic["expires_in"] = self.token_expires_in dic["token_expires_at"] = utc_time_sans_frac() + self.token_expires_in if id_token: dic["id_token"] = id_token if oidreq: dic["oidreq"] = oidreq if issue_refresh: authn_event = dic.get('authn_event') if authn_event: uid = authn_event.uid else: uid = None refresh_token = self._refresh_db.create_token(dic['client_id'], uid, dic.get('scope'), dic['sub'], dic['authzreq']) dic["refresh_token"] = refresh_token self._db[key] = dic return dic
def _verify_id_token(self, id_token, nonce="", acr_values=None, auth_time=0, max_age=0): """ If the JWT alg Header Parameter uses a MAC based algorithm s uch as HS256, HS384, or HS512, the octets of the UTF-8 representation of the client_secret corresponding to the client_id contained in the aud (audience) Claim are used as the key to validate the signature. For MAC based algorithms, the behavior is unspecified if the aud is multi-valued or if an azp value is present that is different than the aud value. :param id_token: The ID Token tp check :param nonce: The nonce specified in the authorization request :param acr_values: Asked for acr values :param auth_time: An auth_time claim :param max_age: Max age of authentication """ try: assert self.provider_info["issuer"] == id_token["iss"] except AssertionError: raise OtherError("issuer != iss") _now = time_util.utc_time_sans_frac() try: assert _now < id_token["exp"] except AssertionError: raise OtherError("Passed best before date") if self.id_token_max_age: try: assert _now < int(id_token["iat"]) + self.id_token_max_age except AssertionError: raise OtherError("I think this ID token is to old") if nonce: try: assert nonce == id_token["nonce"] except AssertionError: raise OtherError("nonce mismatch") if acr_values: try: assert id_token["acr"] in acr_values except AssertionError: raise OtherError("acr mismatch") if max_age: try: assert _now < int(id_token["auth_time"]) + max_age except AssertionError: raise AuthnToOld("To old authentication") if auth_time: if not claims_match(id_token["auth_time"], {"auth_time": auth_time}): raise AuthnToOld("To old authentication")
def valid_client_info(cinfo): try: eta = cinfo['client_secret_expires_at'] except KeyError: pass else: if eta < utc_time_sans_frac(): return False return True
def validate_id_token(self, id_token, nonce): """http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation""" # see oic.Client.verify_id_token assert id_token["iss"] == self.client.provider_info["issuer"] assert self.client.client_id in id_token["aud"] if len(id_token["aud"]) > 1: assert "azp" in id_token and id_token["azp"] == self.client.client_id assert time_util.utc_time_sans_frac() < id_token["exp"] assert id_token["nonce"] == nonce
def verify(self, **kwargs): super(IdToken, self).verify(**kwargs) if "aud" in self: if "client_id" in kwargs: # check that I'm among the recipients if kwargs["client_id"] not in self["aud"]: raise NotForMe("", self) # Then azp has to be present and be one of the aud values if len(self["aud"]) > 1: try: assert "azp" in self except AssertionError: raise VerificationError("azp missing", self) else: try: assert self["azp"] in self["aud"] except AssertionError: raise VerificationError( "Mismatch between azp and aud claims", self) if "azp" in self: if "client_id" in kwargs: if kwargs["client_id"] != self["azp"]: raise NotForMe("", self) _now = time_util.utc_time_sans_frac() try: _skew = kwargs['skew'] except KeyError: _skew = 0 try: _exp = self['exp'] except KeyError: raise MissingRequiredAttribute('exp') else: if (_now - _skew) > _exp: raise EXPError('Invalid expiration time') try: _storage_time = kwargs['nonce_storage_time'] except KeyError: _storage_time = NONCE_STORAGE_TIME try: _iat = self['iat'] except KeyError: raise MissingRequiredAttribute('iat') else: if (_iat + _storage_time) < (_now - _skew): raise IATError('Issued too long ago') return True
def test_is_valid(): sdb = SessionDB(BASE_URL) ae1 = AuthnEvent("sub") sid = sdb.create_authz_session(ae1, AREQ) grant = sdb[sid]["code"] assert sdb.is_valid(grant) _dict = sdb.upgrade_to_token(grant) assert sdb.is_valid(grant) is False token1 = _dict["access_token"] assert sdb.is_valid(token1) rtoken = _dict["refresh_token"] assert sdb.is_valid(rtoken) dict2 = sdb.refresh_token(rtoken) token2 = dict2["access_token"] assert sdb.is_valid(token2) # replace refresh_token dict2["refresh_token"] = token2 assert sdb.is_valid(rtoken) is False # mess with the time-line dict2["token_expires_at"] = utc_time_sans_frac() - 86400 assert sdb.is_valid(token2) is False # replace access_token dict2["access_token"] = token1 assert sdb.is_valid(token2) is False ae = AuthnEvent("another:user") sid = sdb.create_authz_session(ae, AREQ) grant = sdb[sid]["code"] gdict = sdb[grant] gdict["token_expires_at"] = utc_time_sans_frac() - 86400 assert sdb.is_valid(grant) is False
def test_do_user_info_request_with_access_token_refresh(self): self.client.userinfo_endpoint = "http://oic.example.org/userinfo" token = self.client.get_token(state=self.client.state, scope="openid") token.token_expiration_time = utc_time_sans_frac() - 86400 resp = self.client.do_user_info_request(state=self.client.state) assert resp.type() == "OpenIDSchema" assert _eq(resp.keys(), ['name', 'email', 'verified', 'nickname', 'sub']) assert resp["name"] == "Melody Gardot"
def set(self, token, permissions=""): now = utc_time_sans_frac() _info = { "expires_at": now + self.lifetime, "issued_at": now, } if permissions: _info["permissions"] = permissions self.db[token] = _info
def test_construct_access_token_req_override(self): grant = Grant() grant.code = "AbCdEf" grant.grant_expiration_time = time_util.utc_time_sans_frac() + 30 self.client.grant = {"xyz": grant} atr = self.client.construct_AccessTokenRequest(state="xyz") assert atr["grant_type"] == "authorization_code" assert atr["code"] == "AbCdEf" assert atr["redirect_uri"] == self.redirect_uri
def test_no_sub_or_sid(self): lt = LogoutToken( iss="https://example.com", aud=["https://rp.example.org"], events={BACK_CHANNEL_LOGOUT_EVENT: {}}, iat=utc_time_sans_frac(), jti=rndstr(16), ) with pytest.raises(ValueError): lt.verify()
def validate_id_token(self, id_token, nonce): """http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation""" # see oic.Client.verify_id_token assert id_token["iss"] == self.client.provider_info["issuer"] assert self.client.client_id in id_token["aud"] if len(id_token["aud"]) > 1: assert "azp" in id_token and id_token[ "azp"] == self.client.client_id assert time_util.utc_time_sans_frac() < id_token["exp"] assert id_token["nonce"] == nonce
def test_with_sid(self): lt = LogoutToken( iss="https://example.com", aud=["https://rp.example.org"], events={BACK_CHANNEL_LOGOUT_EVENT: {}}, iat=utc_time_sans_frac(), jti=rndstr(16), sid=rndstr(), ) assert lt.verify()
def test_wrong_event_content(self): lt = LogoutToken( iss="https://example.com", aud=["https://rp.example.org"], events={BACK_CHANNEL_LOGOUT_EVENT: {"foo": "bar"}}, jti=rndstr(16), iat=utc_time_sans_frac(), sub="https://example.com/sub", ) with pytest.raises(ValueError): lt.verify()
def test_wrong_iss(self): lt = LogoutToken( iss="https://example.com", aud=["https://rp.example.org"], events={BACK_CHANNEL_LOGOUT_EVENT: {}}, iat=utc_time_sans_frac(), jti=rndstr(16), sub="https://example.com/sub", ) with pytest.raises(NotForMe): lt.verify(iss="https://rp.example.org")
def test_wrong_event(self): lt = LogoutToken( iss="https://example.com", aud=["https://rp.example.org"], events={"http://schemas.openid.net/event/other}": {}}, jti=rndstr(16), iat=utc_time_sans_frac(), sub="https://example.com/sub", ) with pytest.raises(ValueError): lt.verify()
def test_with_sub(self): # All the required claims. Note there must be a sub, a sid or both lt = LogoutToken( iss="https://example.com", aud=["https://rp.example.org"], events={BACK_CHANNEL_LOGOUT_EVENT: {}}, iat=utc_time_sans_frac(), jti=rndstr(16), sub="https://example.com/sub", ) assert lt.verify()
def _get_client_assertion(self): return jwt.encode( { "iss": self.client.client_id, "sub": self.client.client_id, "aud": self.client.token_endpoint, "jti": str(uuid.uuid4()), "exp": utc_time_sans_frac() + 120, }, key=self.private_key, algorithm="RS512", ).decode("utf-8")
def test_with_nonce(self): lt = LogoutToken( iss="https://example.com", aud=["https://rp.example.org"], events={BACK_CHANNEL_LOGOUT_EVENT: {}}, iat=utc_time_sans_frac(), jti=rndstr(16), nonce=rndstr(16), ) with pytest.raises(MessageException): lt.verify()
def upgrade_to_token(self, token=None, issue_refresh=True, id_token="", oidreq=None, key=None, access_grant=""): """ :param token: The access grant :param issue_refresh: If a refresh token should be issued :param id_token: An IDToken instance :param oidreq: An OpenIDRequest instance :param key: The session key. One of token or key must be given. :return: The session information as a dictionary """ if token: try: (typ, key) = self.token.type_and_key(token) except (ValueError, TypeError): (typ, key) = self.token.type_and_key(access_grant) token = access_grant if typ != "A": # not a access grant raise WrongTokenType("Not a grant token") dic = self._db[key] if dic["code_used"]: raise AccessCodeUsed() _at = self.token("T", token) dic["code_used"] = True else: dic = self._db[key] _at = self.token("T", sid=key) dic["access_token"] = _at dic["access_token_scope"] = "?" dic["oauth_state"] = "token" dic["token_type"] = "Bearer" dic["expires_in"] = self.token_expires_in dic["token_expires_at"] = utc_time_sans_frac() + self.token_expires_in if id_token: dic["id_token"] = id_token if oidreq: dic["oidreq"] = oidreq if issue_refresh: dic["refresh_token"] = self.token("R", token) self._db[key] = dic return dic
def test_wrong_iat(self): # Issued sometime in the future lt = LogoutToken( iss="https://example.com", aud=["https://rp.example.org"], events={BACK_CHANNEL_LOGOUT_EVENT: {}}, iat=utc_time_sans_frac() + 86400, jti=rndstr(16), sub="https://example.com/sub", ) with pytest.raises(ValueError): lt.verify()