def _faulty_id_token(): idval = {'nonce': 'KUEYfRM2VzKDaaKD', 'sub': 'EndUserSubject', 'iss': 'https://alpha.cloud.nds.rub.de', 'exp': 1420823073, 'iat': 1420822473, 'aud': 'TestClient'} idts = IdToken(**idval) _signed_jwt = idts.to_jwt(key=[SYMKEY], algorithm="HS256") # Mess with the signed id_token p = _signed_jwt.split(".") p[2] = "aaa" return ".".join(p)
def test_wrong_alg(self): idval = {'nonce': 'KUEYfRM2VzKDaaKD', 'sub': 'EndUserSubject', 'iss': 'https://alpha.cloud.nds.rub.de', 'exp': 1420823073, 'iat': 1420822473, 'aud': 'TestClient'} idts = IdToken(**idval) key = SYMKey(key="TestPassword") _signed_jwt = idts.to_jwt(key=[key], algorithm="HS256") _info = {"access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600} at = AccessTokenResponse(**_info) with pytest.raises(WrongSigningAlgorithm): at.verify(key=[key], algs={"sign": "HS512"})
def test_logout(self): end_session_endpoint = 'https://provider.example.com/end_session' post_logout_uri = 'https://client.example.com/post_logout' authn = OIDCAuthentication(self.app, provider_configuration_info={'issuer': ISSUER, 'end_session_endpoint': end_session_endpoint}, client_registration_info={'client_id': 'foo', 'post_logout_redirect_uris': [post_logout_uri]}) id_token = IdToken(**{'sub': 'sub1', 'nonce': 'nonce'}) with self.app.test_request_context('/logout'): flask.session['access_token'] = 'abcde' flask.session['userinfo'] = {'foo': 'bar', 'abc': 'xyz'} flask.session['id_token'] = id_token.to_dict() flask.session['id_token_jwt'] = id_token.to_jwt() end_session_redirect = authn._logout() assert all(k not in flask.session for k in ['access_token', 'userinfo', 'id_token', 'id_token_jwt']) assert end_session_redirect.status_code == 303 assert end_session_redirect.headers['Location'].startswith(end_session_endpoint) parsed_request = dict(parse_qsl(urlparse(end_session_redirect.headers['Location']).query)) assert parsed_request['state'] == flask.session['end_session_state'] assert parsed_request['id_token_hint'] == id_token.to_jwt() assert parsed_request['post_logout_redirect_uri'] == post_logout_uri
def test_oidc_logout_redirects_to_provider(self): end_session_endpoint = 'https://provider.example.com/end_session' post_logout_uri = 'https://client.example.com/post_logout' authn = OIDCAuthentication(self.app, provider_configuration_info={'issuer': ISSUER, 'end_session_endpoint': end_session_endpoint}, client_registration_info={'client_id': 'foo', 'post_logout_redirect_uris': [post_logout_uri]}) callback_mock = MagicMock() callback_mock.__name__ = 'test_callback' # required for Python 2 id_token = IdToken(**{'sub': 'sub1', 'nonce': 'nonce'}) with self.app.test_request_context('/logout'): flask.session['id_token_jwt'] = id_token.to_jwt() resp = authn.oidc_logout(callback_mock)() assert resp.status_code == 303 assert not callback_mock.called
def test_logout_handles_provider_without_end_session_endpoint(self): post_logout_uri = 'https://client.example.com/post_logout' authn = OIDCAuthentication(self.app, provider_configuration_info={'issuer': ISSUER}, client_registration_info={'client_id': 'foo', 'post_logout_redirect_uris': [post_logout_uri]}) id_token = IdToken(**{'sub': 'sub1', 'nonce': 'nonce'}) with self.app.test_request_context('/logout'): flask.session['access_token'] = 'abcde' flask.session['userinfo'] = {'foo': 'bar', 'abc': 'xyz'} flask.session['id_token'] = id_token.to_dict() flask.session['id_token_jwt'] = id_token.to_jwt() end_session_redirect = authn._logout() assert all(k not in flask.session for k in ['access_token', 'userinfo', 'id_token', 'id_token_jwt']) assert end_session_redirect is None
def test_faulty_idtoken(self): idval = {'nonce': 'KUEYfRM2VzKDaaKD', 'sub': 'EndUserSubject', 'iss': 'https://alpha.cloud.nds.rub.de', 'exp': 1420823073, 'iat': 1420822473, '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])
from jwkest.jws import alg2keytype from oic.oic.message import IdToken from oic.utils.keyio import KeyBundle, KeyJar __author__ = 'rohe0002' b0 = KeyBundle(source="http://localhost:8090/exports/jwk.json", src_type="jwk", usage=["ver", "dec", "sig"]) b1 = KeyBundle(source="http://localhost:8090/exports/cert.pem", src_type="x509", usage=["ver", "dec", "sig"]) print b0 print b1 kj = KeyJar() kj["foobar"] = [b0, b1] idt = IdToken().from_dict({"user_id": "diana", "aud": "uo5nowsdL3ck", "iss": "https://localhost:8092", "acr": "2", "exp": 1354442188, "iat": 1354359388}) ckey = kj.get_signing_key(alg2keytype("RS256"), "foobar") _signed_jwt = idt.to_jwt(key=ckey, algorithm="RS256")
REGREQ = RegistrationRequest(contacts=["*****@*****.**"], redirect_uris=["http://example.org/jqauthz"], application_name="pacubar", client_id=CLIENT_ID, operation="register", application_type="web") RSREQ = RefreshSessionRequest(id_token="id_token", redirect_url="http://example.com/authz", state="state0") #key, type, usage, owner="." alg = "HS256" ktype = alg2keytype(alg) keys = KC_SYM_S.get(ktype) CSREQ = CheckSessionRequest(id_token=IDTOKEN.to_jwt(key=keys, algorithm="HS256")) ESREQ = EndSessionRequest(id_token=IDTOKEN.to_jwt(key=keys, algorithm="HS256"), redirect_url="http://example.org/jqauthz", state="state0") UINFO = Claims(name={"essential": True}, nickname=None, email={"essential": True}, email_verified={"essential": True}, picture=None) IDT2 = Claims(auth_time={"essential": True, "acr": {"values": ["urn:mace:incommon:iap:silver"]}}) CLAIMS = ClaimsRequest(userinfo=UINFO, id_token=IDT2)
UIREQ = UserInfoRequest(access_token="access_token", schema="openid") REGREQ = RegistrationRequest( contacts=["*****@*****.**"], redirect_uris=["http://example.org/jqauthz"], application_name="pacubar", client_id=CLIENT_ID, type="client_associate", ) RSREQ = RefreshSessionRequest(id_token="id_token", redirect_url="http://example.com/authz", state="state0") # key, type, usage, owner="." CSREQ = CheckSessionRequest(id_token=IDTOKEN.to_jwt(key=SIGN_KEY)) ESREQ = EndSessionRequest( id_token=IDTOKEN.to_jwt(key=SIGN_KEY), redirect_url="http://example.org/jqauthz", state="state0" ) IDT2 = IDTokenClaim(max_age=86400) CLAIM = Claims( name={"essential": True}, nickname=None, email={"essential": True}, email_verified={"essential": True}, picture=None ) USRINFO = UserInfoClaim(claims=CLAIM, format="signed") OIDREQ = OpenIDRequest( response_type=["code", "id_token"],
def test_complete_auth_token_idtoken(self): _state = "state0" self.consumer.consumer_config["response_type"] = ["id_token", "token"] self.consumer.registration_response = {"id_token_signed_response_alg": "RS256"} self.consumer.provider_info = {"issuer": "https://example.com"} # abs min self.consumer.authz_req = {} # Store AuthzReq with state as key args = { "client_id": self.consumer.client_id, "response_type": self.consumer.consumer_config["response_type"], "scope": ["openid"], "nonce": "nonce", } token = IdToken( iss="https://example.com", aud="client_1", sub="some_sub", exp=1565348600, iat=1565348300, nonce="nonce", ) location = ( "https://example.com/cb?state=state0&access_token=token&token_type=bearer&" "scope=openid&id_token={}".format( token.to_jwt(key=KC_RSA.keys(), algorithm="RS256") ) ) with responses.RequestsMock() as rsps: rsps.add( responses.GET, "https://example.com/authorization", status=302, headers={"location": location}, ) result = self.consumer.do_authorization_request( state=_state, request_args=args ) query = parse_qs(urlparse(result.request.url).query) assert query["client_id"] == ["client_1"] assert query["scope"] == ["openid"] assert query["response_type"] == ["id_token token"] assert query["state"] == ["state0"] assert query["nonce"] == ["nonce"] assert query["redirect_uri"] == ["https://example.com/cb"] parsed = urlparse(result.headers["location"]) with freeze_time("2019-08-09 11:00:00"): part = self.consumer.parse_authz( query=parsed.query, algs=self.consumer.sign_enc_algs("id_token") ) auth = part[0] atr = part[1] assert part[2] is None assert auth is None assert isinstance(atr, AccessTokenResponse) assert _eq( atr.keys(), ["access_token", "id_token", "token_type", "state", "scope"] ) with freeze_time("2019-08-09 11:00:00"): self.consumer.verify_id_token( atr["id_token"], self.consumer.authz_req[atr["state"]] )
application_name="pacubar", client_id=CLIENT_ID, operation="register", application_type="web") RSREQ = RefreshSessionRequest(id_token="id_token", redirect_url="http://example.com/authz", state="state0") #key, type, usage, owner="." alg = "HS256" ktype = alg2keytype(alg) keys = KC_SYM_S.get(ktype) CSREQ = CheckSessionRequest( id_token=IDTOKEN.to_jwt(key=keys, algorithm="HS256")) ESREQ = EndSessionRequest(id_token=IDTOKEN.to_jwt(key=keys, algorithm="HS256"), redirect_url="http://example.org/jqauthz", state="state0") UINFO = Claims(name={"essential": True}, nickname=None, email={"essential": True}, email_verified={"essential": True}, picture=None) IDT2 = Claims(auth_time={ "essential": True, "acr": { "values": ["urn:mace:incommon:iap:silver"]
scope=["openid"], state="state0", nonce="N0nce") UIREQ = UserInfoRequest(access_token="access_token", schema="openid") REGREQ = RegistrationRequest(contacts=["*****@*****.**"], redirect_uris=["http://example.org/jqauthz"], application_name="pacubar", client_id=CLIENT_ID, operation="register", application_type="web") RSREQ = RefreshSessionRequest(id_token="id_token", redirect_url="http://example.com/authz", state="state0") #key, type, usage, owner="." CSREQ = CheckSessionRequest(id_token=IDTOKEN.to_jwt(key=KC_HMAC_S)) ESREQ = EndSessionRequest(id_token=IDTOKEN.to_jwt(key=KC_HMAC_S), redirect_url="http://example.org/jqauthz", state="state0") IDT2 = IDTokenClaim(max_age=86400) CLAIM = Claims(name={"essential": True}, nickname=None, email={"essential": True}, email_verified={"essential": True}, picture=None) USRINFO = UserInfoClaim(claims=CLAIM, sformat="signed") OIDREQ = OpenIDRequest(response_type=["code", "id_token"], client_id=CLIENT_ID, redirect_uri="https://client.example.com/cb",
def __call__(self, request): data = request.body req = self.provider.parse_token_request(body=data) if 'grant_type' not in req: return (400, {}, 'Missing grant_type') if req['grant_type'] == 'authorization_code': authz_code = req['code'] authz_info = self.provider.authz_codes[authz_code] auth_req = authz_info['auth_req'] client_id = auth_req['client_id'] if authz_info['used']: raise Exception('code already used') return (400, {}, 'Invalid authorization code') if authz_info['exp'] < time.time(): raise Exception('code expired') return (400, {}, 'Invalid authorization code') authz_info['used'] = True access_token = { 'value': rndstr(), 'expires_in': self.provider.access_token_lifetime, 'type': 'Bearer' } at_value = access_token['value'] self.provider.access_tokens[at_value] = { 'iat': time.time(), 'exp': time.time() + self.provider.access_token_lifetime, 'sub': 'test-sub', 'client_id': client_id, 'aud': [client_id], 'scope': authz_info['granted_scope'], 'granted_scope': authz_info['granted_scope'], 'token_type': access_token['type'], 'auth_req': auth_req } resp = AccessTokenResponse() resp['access_token'] = at_value resp['token_type'] = access_token['type'] resp['expires_in'] = access_token['expires_in'] resp['refresh_token'] = None args = { 'c_hash': jws.left_hash(authz_code.encode('utf-8'), 'HS256'), 'at_hash': jws.left_hash(at_value.encode('utf-8'), 'HS256'), } id_token = IdToken(iss=self.config['issuer'], sub='test-sub', aud=client_id, iat=time.time(), exp=time.time() + self.provider.id_token_lifetime, **args) if 'nonce' in auth_req: id_token['nonce'] = auth_req['nonce'] resp['id_token'] = id_token.to_jwt([self.provider.signing_key], 'RS256') json_data = resp.to_json() return (200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', 'Pragma': 'no-cache', }, json_data) return (400, {}, 'Unsupported grant_type')
def make_id_token(self, session, loa="2", issuer="", keytype="rsa", code=None, access_token=None, user_info=None): #defaults inawhile = {"days": 1} # Handle the idtoken_claims extra = {} try: oidreq = OpenIDRequest().deserialize(session["oidreq"], "json") itc = oidreq["id_token"] logger.debug("ID Token claims: %s" % itc.to_dict()) try: inawhile = {"seconds": itc["max_age"]} except KeyError: inawhile = {} if "claims" in itc: for key, val in itc["claims"].items(): if key == "auth_time": extra["auth_time"] = time_util.utc_time_sans_frac() elif key == "acr": #["2","http://id.incommon.org/assurance/bronze"] extra["acr"] = verify_acr_level(val, loa) except KeyError: pass if user_info is None: _args = {} else: _args = user_info.to_dict() # Make sure that there are no name clashes for key in ["iss", "user_id", "aud", "exp", "acr", "nonce", "auth_time"]: try: del _args[key] except KeyError: pass if code: _args["c_hash"] = jwt.left_hash(code, "HS256") if access_token: _args["at_hash"] = jwt.left_hash(access_token, "HS256") idt = IdToken(iss=issuer, user_id=session["user_id"], aud = session["client_id"], exp = time_util.epoch_in_a_while(**inawhile), acr=loa, **_args) for key, val in extra.items(): idt[key] = val if "nonce" in session: idt.nonce = session["nonce"] # sign with clients secret key _keystore = self.keystore if keytype == "hmac": ckey = {"hmac": _keystore.get_sign_key("hmac", owner=session["client_id"])} algo = "HS256" elif keytype == "rsa": # own asymmetric key algo = "RS256" ckey = {"rsa": _keystore.get_sign_key("rsa")} else: algo = "ES256" ckey = {"ec":_keystore.get_sign_key("ec")} logger.debug("Sign idtoken with '%s'" % (ckey,)) return idt.to_jwt(key=ckey, algorithm=algo)
def test_complete_auth_token_idtoken_none_cipher_code(self): _state = "state0" self.consumer.consumer_config["response_type"] = ["code"] self.consumer.registration_response = RegistrationResponse( id_token_signed_response_alg="none") self.consumer.provider_info = ProviderConfigurationResponse( issuer="https://example.com") # abs min self.consumer.authz_req = {} # Store AuthzReq with state as key self.consumer.sdb[_state] = {"redirect_uris": []} args = { "client_id": self.consumer.client_id, "response_type": self.consumer.consumer_config["response_type"], "scope": ["openid"], "nonce": "nonce", } token = IdToken( iss="https://example.com", aud="client_1", sub="some_sub", exp=1565348600, iat=1565348300, nonce="nonce", at_hash="aaa", ) # Downgrade the algorithm to `none` location = ( "https://example.com/cb?state=state0&access_token=token&token_type=bearer&" "scope=openid&id_token={}".format( token.to_jwt(key=KC_RSA.keys(), algorithm="none"))) with responses.RequestsMock() as rsps: rsps.add( responses.GET, "https://example.com/authorization", status=302, headers={"location": location}, ) result = self.consumer.do_authorization_request(state=_state, request_args=args) query = parse_qs(urlparse(result.request.url).query) assert query["client_id"] == ["client_1"] assert query["scope"] == ["openid"] assert query["response_type"] == ["code"] assert query["state"] == ["state0"] assert query["nonce"] == ["nonce"] assert query["redirect_uri"] == ["https://example.com/cb"] parsed = urlparse(result.headers["location"]) with freeze_time("2019-08-09 11:00:00"): part = self.consumer.parse_authz(query=parsed.query) assert isinstance(part, tuple) auth = part[0] atr = part[1] idt = part[2] assert isinstance(auth, AuthorizationResponse) assert isinstance(atr, AccessTokenResponse) assert _eq( atr.keys(), ["access_token", "id_token", "token_type", "state", "scope"]) assert isinstance(idt, IdToken)
def __call__(self, request): data = request.body req = self.provider.parse_token_request(body=data) if 'grant_type' not in req: return (400, {}, 'Missing grant_type') if req['grant_type'] == 'authorization_code': authz_code = req['code'] authz_info = self.provider.authz_codes[authz_code] auth_req = authz_info['auth_req'] client_id = auth_req['client_id'] if authz_info['used']: raise Exception('code already used') return (400, {}, 'Invalid authorization code') if authz_info['exp'] < time.time(): raise Exception('code expired') return (400, {}, 'Invalid authorization code') authz_info['used'] = True access_token = { 'value': rndstr(), 'expires_in': self.provider.access_token_lifetime, 'type': 'Bearer' } at_value = access_token['value'] self.provider.access_tokens[at_value] = { 'iat': time.time(), 'exp': time.time() + self.provider.access_token_lifetime, 'sub': 'test-sub', 'client_id': client_id, 'aud': [client_id], 'scope': authz_info['granted_scope'], 'granted_scope': authz_info['granted_scope'], 'token_type': access_token['type'], 'auth_req': auth_req } resp = AccessTokenResponse() resp['access_token'] = at_value resp['token_type'] = access_token['type'] resp['expires_in'] = access_token['expires_in'] resp['refresh_token'] = None args = { 'c_hash': jws.left_hash(authz_code.encode('utf-8'), 'HS256'), 'at_hash': jws.left_hash(at_value.encode('utf-8'), 'HS256'), } id_token = IdToken( iss=self.config['issuer'], sub='test-sub', aud=client_id, iat=time.time(), exp=time.time() + self.provider.id_token_lifetime, **args) if 'nonce' in auth_req: id_token['nonce'] = auth_req['nonce'] resp['id_token'] = id_token.to_jwt( [self.provider.signing_key], 'RS256') json_data = resp.to_json() return ( 200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', 'Pragma': 'no-cache', }, json_data ) return (400, {}, 'Unsupported grant_type')