def client_secret_expiration_time(): ''' Returns client_secret expiration time. Split for easy customization. ''' return utc_time_sans_frac() + 86400
def client_secret_expiration_time(delta=86400): """ Returns client_secret expiration time. Split for easy customization. """ return utc_time_sans_frac() + delta
def test_entity_statement(): client_info = RegistrationRequest( **{ "application_type": "web", "claims": ["sub", "name", "email", "picture"], "id_token_signing_alg_values_supported": ["RS256", "RS512"], "redirect_uris": ["https://foodle.uninett.no/callback"], "response_types": ["code"] }) metadata = Metadata() metadata['openid_relying_party'] = client_info iat = utc_time_sans_frac() # seconds since epoch exp = iat + 3600 entity_statement = EntityStatement(metadata=metadata, iss='https://example.com', sub='https://foo.example.com', iat=iat, exp=exp) jes = entity_statement.to_json() _es = EntityStatement().from_json(jes) assert set(_es.keys()) == {'metadata', 'iss', 'sub', 'iat', 'exp'} assert set(_es['metadata'].keys()) == {'openid_relying_party'} assert set(_es['metadata']['openid_relying_party'].keys()) == { 'application_type', 'claims', 'id_token_signing_alg_values_supported', 'redirect_uris', 'response_types' }
def mint_token(self, token_class, grant, session_id, based_on=None, **kwargs): usage_rules = grant.usage_rules.get(token_class, {}) token = grant.mint_token( session_id=session_id, endpoint_context=self.server_get("endpoint_context"), token_class=token_class, based_on=based_on, usage_rules=usage_rules, **kwargs, ) _exp_in = usage_rules.get("expires_in") if isinstance(_exp_in, str): _exp_in = int(_exp_in) if _exp_in: token.expires_at = utc_time_sans_frac() + _exp_in _mngr = self.server_get("endpoint_context").session_manager _mngr.set(_mngr.unpack_session_key(session_id), grant) return token
def is_expired(self): now = utc_time_sans_frac() if self.exp < now: return True if self.sup: return self.sup.is_expired() else: return False
def client_secret_expiration_time(self): """ Returns client_secret expiration time. """ if not self.kwargs.get("client_secret_expires", True): return 0 _expiration_time = self.kwargs.get("client_secret_expires_in", 2592000) return utc_time_sans_frac() + _expiration_time
def calculate_expiration_time(delta=86400): """ Returns client_secret expiration time. Split for easy customization. """ if delta <= 0: return 0 return utc_time_sans_frac() + delta
def dpop_header(service_context: ServiceContext, service_endpoint: str, http_method: str, headers: Optional[dict] = None, **kwargs) -> dict: """ :param service_context: :param service_endpoint: :param http_method: :param headers: :param kwargs: :return: """ provider_info = service_context.provider_info dpop_key = service_context.add_on['dpop'].get('key') if not dpop_key: algs_supported = provider_info["dpop_signing_alg_values_supported"] if not algs_supported: # does not support DPoP return headers chosen_alg = '' for alg in service_context.add_on['dpop']["sign_algs"]: if alg in algs_supported: chosen_alg = alg break if not chosen_alg: return headers # Mint a new key dpop_key = key_by_alg(chosen_alg) service_context.add_on['dpop']['key'] = dpop_key service_context.add_on['dpop']['alg'] = chosen_alg header_dict = { "typ": "dpop+jwt", "alg": service_context.add_on['dpop']['alg'], "jwk": dpop_key.serialize(), "jti": uuid.uuid4().hex, "htm": http_method, "htu": provider_info[service_endpoint], "iat": utc_time_sans_frac() } _dpop = DPoPProof(**header_dict) _dpop.key = dpop_key jws = _dpop.create_header() if headers is None: headers = {"dpop": jws} else: headers["dpop"] = jws return headers
def upgrade_to_token( self, grant=None, issue_refresh=False, id_token="", oidreq=None, key=None, scope=None, ): """ :param grant: 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 grant or key must be given. :return: The session information as a SessionInfo instance """ if grant: # The caller is responsible for checking if the access code exists. _tinfo = self.handler["code"].info(grant) key = _tinfo["sid"] session_info = self[key] # mint a new access token _at = self._make_at(_tinfo["sid"], session_info) # make sure the code can't be used again self.revoke_token(key, "code", session_info) else: session_info = self[key] _at = self._make_at(key, session_info) session_info["access_token"] = _at session_info["oauth_state"] = "token" session_info["token_type"] = self.handler["access_token"].token_type if scope: session_info["access_token_scope"] = scope if id_token: session_info["id_token"] = id_token if oidreq: session_info["oidreq"] = oidreq if self.handler["access_token"].lifetime: session_info["expires_in"] = self.handler["access_token"].lifetime session_info["expires_at"] = self.handler[ "access_token"].lifetime + utc_time_sans_frac() if issue_refresh and "refresh_token" in self.handler: session_info = self.replace_refresh_token(key, session_info) self[key] = session_info return session_info
def verify(self, **kwargs): super(LogoutToken, self).verify(**kwargs) if 'nonce' in self: raise MessageException('"nonce" is prohibited from appearing in ' 'a LogoutToken.') # Check the 'events' JSON _keys = list(self['events'].keys()) if len(_keys) != 1: raise ValueError('Must only be one member in "events"') if _keys[0] != "http://schemas.openid.net/event/backchannel-logout": raise ValueError('Wrong member in "events"') if self['events'][_keys[0]] != {}: raise ValueError('Wrong member value in "events"') # There must be either a 'sub' or a 'sid', and may contain both if not ('sub' in self or 'sid' in self): raise ValueError('There MUST be either a "sub" or a "sid"') try: if kwargs['aud'] not in self['aud']: raise NotForMe('Not among intended audience') except KeyError: pass try: if kwargs['iss'] != self['iss']: raise NotForMe('Wrong issuer') except KeyError: pass _now = utc_time_sans_frac() try: _skew = kwargs['skew'] except KeyError: _skew = 0 try: _exp = self['iat'] except KeyError: pass else: if self['iat'] > (_now + _skew): raise ValueError('Invalid issued_at time') _allowed = kwargs.get("allowed_sign_alg") if _allowed and self.jws_header['alg'] != _allowed: _msg = "Wrong token signing algorithm, {} != {}".format( self.jws_header['alg'], kwargs["allowed_sign_alg"]) raise UnsupportedAlgorithm(_msg) return True
def construct_request_parameter(self, req, request_param, audience=None, expires_in=0, **kwargs): """ Construct a request parameter """ alg = self.get_request_object_signing_alg(**kwargs) kwargs["request_object_signing_alg"] = alg _context = self.client_get("service_context") if "keys" not in kwargs and alg and alg != "none": kwargs["keys"] = _context.keyjar if alg == "none": kwargs["keys"] = [] _srv_cntx = _context # This is the issuer of the JWT, that is me ! _issuer = kwargs.get("issuer") if _issuer is None: kwargs['issuer'] = _srv_cntx.client_id if kwargs.get("recv") is None: try: kwargs['recv'] = _srv_cntx.provider_info['issuer'] except KeyError: kwargs['recv'] = _srv_cntx.issuer del kwargs['service'] if expires_in: req['exp'] = utc_time_sans_frac() + int(expires_in) _mor_args = { k: kwargs[k] for k in [ "keys", "issuer", "request_object_signing_alg", "recv", "with_jti", "lifetime" ] if k in kwargs } _req = make_openid_request(req, **_mor_args) # Should the request be encrypted _req = request_object_encryption(_req, _context, **kwargs) if request_param == "request": req["request"] = _req else: # MUST be request_uri req["request_uri"] = self.store_request_on_file(_req, **kwargs)
def valid_service_context(service_context, when=0): """ Check if the client_secret has expired :param service_context: A :py:class:`oidcservice.service_context.ServiceContext` instance :param when: A time stamp against which the expiration time is to be checked :return: True if the client_secret is still valid """ eta = getattr(service_context, 'client_secret_expires_at', 0) now = when or utc_time_sans_frac() if eta != 0 and eta < now: return False return True
def verify(self, **kwargs): super(JsonWebToken, self).verify(**kwargs) _now = utc_time_sans_frac() try: _skew = kwargs["skew"] except KeyError: _skew = 0 try: _exp = self["exp"] except KeyError: pass else: if (_now - _skew) > _exp: raise EXPError("Invalid expiration time") try: _iat = self["iat"] except KeyError: pass else: if _iat > (_now + _skew): raise EXPError("Invalid issued-at time") try: _nbf = self["nbf"] except KeyError: pass else: if _nbf > (_now - _skew): raise EXPError("Not valid yet") try: _aud = self["aud"] except KeyError: pass else: try: if kwargs["aud"] not in _aud: raise NotForMe("Not among intended audience") except KeyError: pass if "iss" in kwargs and "iss" in self: if kwargs["iss"] != self["iss"]: raise ValueError("Wrong issuer") return True
def verify(self, **kwargs): super(JsonWebToken, self).verify(**kwargs) _now = utc_time_sans_frac() try: _skew = kwargs['skew'] except KeyError: _skew = 0 try: _exp = self['exp'] except KeyError: pass else: if (_now - _skew) > _exp: raise EXPError('Invalid expiration time') try: _iat = self['iat'] except KeyError: pass else: if _iat > (_now + _skew): raise EXPError('Invalid issued-at time') try: _nbf = self['nbf'] except KeyError: pass else: if _nbf > (_now - _skew): raise EXPError('Not valid yet') try: _aud = self['aud'] except KeyError: pass else: try: if kwargs['aud'] not in _aud: raise NotForMe('Not among intended audience') except KeyError: pass if 'iss' in kwargs and 'iss' in self: if kwargs['iss'] != self['iss']: raise ValueError('Wrong issuer') return True
def test_expired_access_token(self): access_token = self._get_access_token(AUTH_REQ) access_token.expires_at = utc_time_sans_frac() - 1000 _context = self.introspection_endpoint.server_get("endpoint_context") _req = self.introspection_endpoint.parse_request({ "token": access_token.value, "client_id": "client_1", "client_secret": _context.cdb["client_1"]["client_secret"], }) _resp = self.introspection_endpoint.process_request(_req) assert _resp["response_args"]["active"] is False
def evaluate_metadata_statement(self, metadata, keyjar=None): """ Computes the resulting metadata statement from a compounded metadata statement. If something goes wrong during the evaluation an exception is raised :param metadata: The compounded metadata statement as a dictionary :return: A list of :py:class:`fedoidc.operator.LessOrEqual` instances, one per FO. """ # start from the innermost metadata statement and work outwards res = dict([(k, v) for k, v in metadata.items() if k not in IgnoreKeys]) les = [] if 'metadata_statements' in metadata: for fo, ms in metadata['metadata_statements'].items(): if isinstance(ms, str): ms = json.loads(ms) for _le in self.evaluate_metadata_statement(ms): if isinstance(ms, Message): le = LessOrEqual(sup=_le, **ms.to_dict()) else: # Must be a dict le = LessOrEqual(sup=_le, **ms) if le.is_expired(): logger.error( 'This metadata statement has expired: {}'.format(ms) ) logger.info('My time: {}'.format(utc_time_sans_frac())) continue le.eval(res) les.append(le) return les else: # this is the innermost try: _iss = metadata['iss'] except: le = LessOrEqual() le.eval(res) else: le = LessOrEqual(iss=_iss, exp=metadata['exp']) le.eval(res) les.append(le) return les
def test_expired_access_token(self): _context = self.introspection_endpoint.endpoint_context _token = self._create_at("diana", lifetime=6000, with_jti=True) _info = self.token_endpoint.endpoint_context.sdb[_token] _info['expires_at'] = utc_time_sans_frac() - 1000 self.token_endpoint.endpoint_context.sdb[_token] = _info _req = self.introspection_endpoint.parse_request({ "token": _token, "client_id": "client_1", "client_secret": _context.cdb["client_1"]["client_secret"], }) _resp = self.introspection_endpoint.process_request(_req) assert _resp["response_args"]["active"] is False
def _mint_access_token(self, grant, session_id, token_ref=None): _session_info = self.session_manager.get_session_info(session_id) usage_rules = grant.usage_rules.get("access_token", {}) _exp_in = usage_rules.get("expires_in", 0) _token = grant.mint_token( _session_info, endpoint_context=self.endpoint_context, token_class="access_token", token_handler=self.session_manager.token_handler["access_token"], based_on=token_ref, # Means the token (tok) was used to mint this token usage_rules=usage_rules, ) if isinstance(_exp_in, str): _exp_in = int(_exp_in) if _exp_in: _token.expires_at = utc_time_sans_frac() + _exp_in return _token
def get_valid_access_token(self, state): """ Find a valid access token. :param state: :return: An access token if a valid one exists and when it expires. Otherwise raise exception. """ exp = 0 token = None indefinite = [] now = utc_time_sans_frac() client = self.get_client_from_session_key(state) _context = client.client_get("service_context") for cls, typ in [(AccessTokenResponse, 'refresh_token_response'), (AccessTokenResponse, 'token_response'), (AuthorizationResponse, 'auth_response')]: try: response = _context.state.get_item(cls, typ, state) except KeyError: pass else: if 'access_token' in response: access_token = response["access_token"] try: _exp = response['__expires_at'] except KeyError: # No expiry date, lives for ever indefinite.append((access_token, 0)) else: if _exp > now and _exp > exp: # expires sometime in the future exp = _exp token = (access_token, _exp) if indefinite: return indefinite[0] else: if token: return token else: raise OidcServiceError('No valid access token')
def create_session(self, request, user_id, acr, time_stamp, authn_method): _context = self.server_get("endpoint_context") _mngr = _context.session_manager authn_event = create_authn_event( user_id, authn_info=acr, time_stamp=time_stamp, ) _exp_in = authn_method.kwargs.get("expires_in") if _exp_in and "valid_until" in authn_event: authn_event["valid_until"] = utc_time_sans_frac() + _exp_in _token_usage_rules = _context.authz.usage_rules(request["client_id"]) return _mngr.create_session( authn_event=authn_event, auth_req=request, user_id=user_id, client_id=request["client_id"], token_usage_rules=_token_usage_rules, )
def _mint_code(self, grant, client_id): session_id = self.session_manager.encrypted_session_id(self.user_id, client_id, grant.id) usage_rules = grant.usage_rules.get("authorization_code", {}) _exp_in = usage_rules.get("expires_in") # Constructing an authorization code is now done _code = grant.mint_token( session_id=session_id, endpoint_context=self.endpoint_context, token_class="authorization_code", token_handler=self.session_manager.token_handler["authorization_code"], usage_rules=usage_rules, ) if _exp_in: if isinstance(_exp_in, str): _exp_in = int(_exp_in) if _exp_in: _code.expires_at = utc_time_sans_frac() + _exp_in return _code
def process_request(self, request=None, **kwargs): """ :param request: The authorization request as a dictionary :param kwargs: :return: """ _introspect_request = self.request_cls(**request) if "error" in _introspect_request: return _introspect_request _token = _introspect_request["token"] _resp = self.response_cls(active=False) if factory(_token): _info = self.do_jws(_token, _resp) # expired ? if "exp" in _info: now = utc_time_sans_frac() if _info["exp"] < now: return {"response_args": _resp} else: # A non-jws access token _info = self.do_access_token(_token) if not _info: return {"response_args": _resp} if "release" in self.kwargs: if "username" in self.kwargs["release"]: try: _info["username"] = self.endpoint_context.userinfo.search( sub=_info["sub"]) except KeyError: pass _resp.update(_info) _resp.weed() _resp["active"] = True return {"response_args": _resp}
def assertion_jwt(client_id, keys, audience, algorithm, lifetime=600): """ Create a signed Json Web Token containing some information. :param client_id: The Client ID :param keys: Signing keys :param audience: Who is the receivers for this assertion :param algorithm: Signing algorithm :param lifetime: The lifetime of the signed Json Web Token :return: A Signed Json Web Token """ _now = utc_time_sans_frac() _token = AuthnToken(iss=client_id, sub=client_id, aud=audience, jti=rndstr(32), exp=_now + lifetime, iat=_now) LOGGER.debug('AuthnToken: %s', _token.to_dict()) return _token.to_jwt(key=keys, algorithm=algorithm)
def has_active_authentication(self, state): """ Find out if the user has an active authentication :param state: :return: True/False """ client = self.get_client_from_session_key(state) # Look for Id Token in all the places where it can be _arg = client.client_get( "service_context").state.multiple_extend_request_args( {}, state, ['__verified_id_token'], ['auth_response', 'token_response', 'refresh_token_response']) if _arg: _now = utc_time_sans_frac() exp = _arg['__verified_id_token']['exp'] return _now < exp else: return False
def construct_request_parameter(self, req, request_method, audience=None, expires_in=0, **kwargs): """Construct a request parameter""" alg = self.get_request_object_signing_alg(**kwargs) kwargs["request_object_signing_alg"] = alg if "keys" not in kwargs and alg and alg != "none": kwargs["keys"] = self.service_context.keyjar _srv_cntx = self.service_context kwargs['issuer'] = _srv_cntx.get('client_id') # set the audience if audience: kwargs["recv"] = _srv_cntx.get('provider_info')[audience] else: try: kwargs['recv'] = _srv_cntx.get('provider_info')['issuer'] except KeyError: kwargs['recv'] = _srv_cntx.get('issuer') del kwargs['service'] if expires_in: req['exp'] = utc_time_sans_frac() + int(expires_in) _req = make_openid_request(req, **kwargs) # Should the request be encrypted _req = request_object_encryption(_req, self.service_context, **kwargs) if request_method == "request": req["request"] = _req else: # MUST be request_uri req["request_uri"] = self.store_request_on_file(_req, **kwargs)
def _introspect(self, token): try: info = self.endpoint_context.sdb[token] except KeyError: return None # Make sure that the token is an access_token or a refresh_token if token not in info.get("access_token") and token != info.get( "refresh_token" ): return None eat = info.get("expires_at") if eat and eat < utc_time_sans_frac(): return None if info: # Now what can be returned ? ret = info.to_dict() ret["iss"] = self.endpoint_context.issuer if "scope" not in ret: ret["scope"] = " ".join(info["authn_req"]["scope"]) return ret
def process_request(self, request=None, **kwargs): """ :param request: The authorization request as a dictionary :param kwargs: :return: """ _introspect_request = self.request_cls(**request) _jwt = JWT(key_jar=self.endpoint_context.keyjar) try: _jwt_info = _jwt.unpack(_introspect_request["token"]) except Exception: return {"response": {"active": False}} # expired ? if "exp" in _jwt_info: now = utc_time_sans_frac() if _jwt_info["exp"] < now: return {"response": {"active": False}} if "release" in self.kwargs: if "username" in self.kwargs["release"]: try: _jwt_info[ "username"] = self.endpoint_context.userinfo.search( sub=_jwt_info["sub"]) except KeyError: return {"response": {"active": False}} _resp = self.response_cls(**_jwt_info) _resp.weed() _resp["active"] = True return {"response_args": _resp}
def verify(self, **kwargs): super(IdToken, self).verify(**kwargs) try: if kwargs["iss"] != self["iss"]: raise IssuerMismatch("{} != {}".format(kwargs["iss"], self["iss"])) except KeyError: pass 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 {}'.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: if "azp" in self: if self["azp"] not in self["aud"]: raise VerificationError("Mismatch between azp and aud claims", self) else: raise VerificationError("azp missing", 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") elif _iat > _now + _skew: raise IATError("Issued sometime in the future") if _exp < _iat: raise IATError("Expiration time can not be earlier the issued at") if "nonce" in kwargs and "nonce" in self: if kwargs["nonce"] != self["nonce"]: raise ValueError("Not the same nonce") return True
def pack_init(self, lifetime=0): self["iat"] = utc_time_sans_frac() if lifetime: self["exp"] = self["iat"] + lifetime
from oidcrp.oauth2 import Client sys.path.insert(0, '.') _dirname = os.path.dirname(os.path.abspath(__file__)) BASE_PATH = os.path.join(_dirname, "data", "keys") _key = import_private_rsa_key_from_file(os.path.join(BASE_PATH, "rsa.key")) KC_RSA = KeyBundle({"key": _key, "kty": "RSA", "use": "sig"}) CLIENT_ID = "client_1" IDTOKEN = IdToken(iss="http://oidc.example.org/", sub="sub", aud=CLIENT_ID, exp=utc_time_sans_frac() + 86400, nonce="N0nce", iat=time.time()) class DB(object): def __init__(self): self.db = {} def set(self, key, value): self.db[key] = value def get(self, item): return self.db[item]