Exemple #1
0
    def test_issuer_mismatch(self):
        ISSUER = "https://login.microsoftonline.com/b4ea3de6-839e-4ad1-ae78-c78e5c0cdc06/v2.0/"
        kb = KeyBundle(JWK2["keys"])
        kj = KeyJar()
        kj.issuer_keys[ISSUER] = [kb]
        kj.issuer_keys[""] = []

        authz_resp = AuthorizationResponse().from_urlencoded(
            "id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSIsImtpZCI6Ik1u"
            "Q19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSJ9.eyJhdWQiOiIwMTZlZDBlNC1mYzUyLTRlYjgtOWVhYy1lODg1MmM4MjEwNTUiLCJpc3Mi"
            "OiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vYjRlYTNkZTYtODM5ZS00YWQxLWFlNzgtYzc4ZTVjMGNkYzA2L3YyLjAvI"
            "iwiaWF0IjoxNDM5OTIzNDY5LCJuYmYiOjE0Mzk5MjM0NjksImV4cCI6MTQzOTkyNzM2OSwidmVyIjoiMi4wIiwidGlkIjoiYjRlYTNkZT"
            "YtODM5ZS00YWQxLWFlNzgtYzc4ZTVjMGNkYzA2Iiwib2lkIjoiNDJjMzliNWUtYmQwNS00YTlhLTlhNWUtMTY5ZDc2N2ZlZjJmIiwicHJ"
            "lZmVycmVkX3VzZXJuYW1lIjoiaW50ZXJvcEBrYXV0aS5vbm1pY3Jvc29mdC5jb20iLCJzdWIiOiJFWGlaVldjakpsREN1LXZzOUxNb1V3"
            "ZGRNNEJZZ2ZISzBJNk40dWpXZkRFIiwibmFtZSI6ImludGVyb3AiLCJub25jZSI6IlpkSHRxQWwzR3c4QiJ9.tH4FKM4H9YCHX2XF4V64"
            "SsLaKh31c0oLpEVlFxFHw8jxL5HujUthZJDUMwngXZ2mPU_1G152ybKiRCV9DKaBh1rFSlZxTDBp0SV_YTwOkGYOt-sOzFUJyvVCjGmRh"
            "vFkOF1kiT3IYjDoRh72U8pMchj1duWSytLczdOc4LJmg24ya5jwqApuyQu7gVqoDH1kEqBAuhBj3a7ZDwxIt-bTKZklsht0RutZjv4Ckg"
            "8qJpzWnY7rIjSKFKfEpAAfk_LqWvTktvDMKTHXLxEPVZymoskE1LthtC8AYoNmtVPxgxf87yGCqYZBsuAnVChdnsItXP7tPeqUjC8Lm3J"
            "jabV-5g&id_token_expires_in=3599&state=6o3FmQ0QZl1zifsE&session_state=d2c97e8a-497c-4ce1-bb10-5058501164eb"
        )

        try:
            authz_resp.verify(keyjar=kj, skew=100000000)
        except MissingSigningKey:
            authz_resp.verify(keyjar=kj, sender=ISSUER, skew=100000000)
    def test_handle_authentication_response_POST(self):
        access_token = 'test_access_token'
        state = 'test_state'

        authn = self.init_app()
        auth_response = AuthorizationResponse(**{
            'state': state,
            'token_type': 'Bearer',
            'access_token': access_token
        })

        with self.app.test_request_context(
                '/redirect_uri',
                method='POST',
                data=auth_response.to_dict(),
                mimetype='application/x-www-form-urlencoded'):
            UserSession(flask.session, self.PROVIDER_NAME)
            flask.session['destination'] = '/test'
            flask.session['auth_request'] = json.dumps({
                'state': state,
                'nonce': 'test_nonce'
            })
            response = authn._handle_authentication_response()
            session = UserSession(flask.session)
            assert session.access_token == access_token
            assert response == '/test'
Exemple #3
0
    def _func(self, conv):
        _response = conv.last_response
        _content = conv.last_content
        _client = conv.client
        res = {}
        if _response.status_code == 400:
            err = ErrorResponse().deserialize(_content, "json")
            err.verify()
            if err["error"] in ["consent_required", "interaction_required"]:
                # This is OK
                res["content"] = err.to_json()
                conv.protocol_response.append((err, _content))
            else:
                self._message = "Not an error I expected"
                self._status = CRITICAL
        elif _response.status_code in [301, 302]:
            _loc = _response.headers["location"]
            callback = False
            for url in _client.redirect_uris:
                if _loc.startswith(url):
                    callback = True
                    break

            if not callback:
                self._message = "Not valid to not redirect back to RP"
                self._status = ERROR
                return res

            if "?" in _loc:
                _query = _loc.split("?")[1]
            elif "#" in _loc:
                _query = _loc.split("#")[1]
            else:  # ???
                self._message = "Expected info in the redirect"
                self._status = CRITICAL
                return res
            try:
                err = ErrorResponse().deserialize(_query, "urlencoded")
                err.verify()
                if err["error"] in ["consent_required", "interaction_required",
                                    "login_required"]:
                    # This is OK
                    res["content"] = err.to_json()
                    conv.protocol_response.append((err, _query))
                else:
                    self._message = "Not an error I expected '%s'" % err[
                        "error"]
                    self._status = CRITICAL
            except:
                resp = AuthorizationResponse().deserialize(_query, "urlencoded")
                resp.verify()
                res["content"] = resp.to_json()
                conv.protocol_response.append((resp, _query))
        else:  # should not get anything else
            self._message = "Not an response I expected"
            self._status = CRITICAL

        return res
    def test_handle_implicit_authentication_response(self, time_mock,
                                                     utc_time_sans_frac_mock):
        # freeze time since ID Token validation includes expiration timestamps
        timestamp = time.mktime(datetime(2017, 1, 1).timetuple())
        time_mock.return_value = timestamp
        utc_time_sans_frac_mock.return_value = int(timestamp)

        # mock auth response
        access_token = 'test_access_token'
        user_id = 'user1'
        exp_time = 10
        nonce = 'test_nonce'
        id_token_claims = {
            'iss': self.PROVIDER_BASEURL,
            'aud': [self.CLIENT_ID],
            'sub': user_id,
            'exp': int(timestamp) + exp_time,
            'iat': int(timestamp),
            'nonce': nonce,
            'at_hash': jws.left_hash(access_token)
        }
        id_token_jwt, id_token_signing_key = signed_id_token(id_token_claims)

        responses.add(responses.GET,
                      self.PROVIDER_BASEURL + '/jwks',
                      json={'keys': [id_token_signing_key.serialize()]})

        # mock userinfo response
        userinfo = {'sub': user_id, 'name': 'Test User'}
        userinfo_endpoint = self.PROVIDER_BASEURL + '/userinfo'
        responses.add(responses.GET, userinfo_endpoint, json=userinfo)

        authn = self.init_app(
            provider_metadata_extras={'userinfo_endpoint': userinfo_endpoint})
        state = 'test_state'
        auth_response = AuthorizationResponse(
            **{
                'state': state,
                'access_token': access_token,
                'token_type': 'Bearer',
                'id_token': id_token_jwt
            })
        with self.app.test_request_context('/redirect_uri?{}'.format(
                auth_response.to_urlencoded())):
            UserSession(flask.session, self.PROVIDER_NAME)
            flask.session['destination'] = '/'
            flask.session['auth_request'] = json.dumps({
                'state': state,
                'nonce': nonce
            })
            authn._handle_authentication_response()
            session = UserSession(flask.session)
            assert session.access_token == access_token
            assert session.id_token == id_token_claims
            assert session.id_token_jwt == id_token_jwt
            assert session.userinfo == userinfo
 def test_parse_authentication_response(self):
     facade = PyoidcFacade(ProviderConfiguration(provider_metadata=self.PROVIDER_METADATA,
                                                 client_metadata=self.CLIENT_METADATA),
                           self.REDIRECT_URI)
     auth_code = 'auth_code-1234'
     state = 'state-1234'
     auth_response = AuthorizationResponse(**{'state': state, 'code': auth_code})
     parsed_auth_response = facade.parse_authentication_response(auth_response.to_dict())
     assert isinstance(parsed_auth_response, AuthorizationResponse)
     assert parsed_auth_response.to_dict() == auth_response.to_dict()
 def test_should_handle_token_error_response(self, client_mock):
     client_mock.exchange_authorization_code.return_value = TokenErrorResponse(
         **self.ERROR_RESPONSE)
     with pytest.raises(AuthResponseErrorResponseError) as exc:
         AuthResponseHandler(client_mock).process_auth_response(
             AuthorizationResponse(**self.AUTH_RESPONSE), self.AUTH_REQUEST)
     assert exc.value.error_response == self.ERROR_RESPONSE
Exemple #7
0
 def test_should_detect_mismatching_subject(self, client_mock):
     client_mock.exchange_authorization_code.return_value = AccessTokenResponse(**self.TOKEN_RESPONSE)
     client_mock.userinfo_request.return_value = OpenIDSchema(**{'sub': 'other_sub'})
     with pytest.raises(AuthResponseMismatchingSubjectError):
         AuthResponseHandler(client_mock).process_auth_response(AuthorizationResponse(**self.AUTH_RESPONSE),
                                                                self.AUTH_RESPONSE['state'],
                                                                self.TOKEN_RESPONSE['id_token']['nonce'])
 def test_parse_authentication_response_preserves_id_token_jwt(self):
     facade = PyoidcFacade(
         ProviderConfiguration(provider_metadata=self.PROVIDER_METADATA,
                               client_metadata=self.CLIENT_METADATA),
         REDIRECT_URI)
     state = 'state-1234'
     now = int(time.time())
     id_token, id_token_signing_key = signed_id_token({
         'iss':
         self.PROVIDER_METADATA['issuer'],
         'sub':
         'test_sub',
         'aud':
         'client1',
         'exp':
         now + 1,
         'iat':
         now
     })
     responses.add(responses.GET,
                   self.PROVIDER_METADATA['jwks_uri'],
                   json={'keys': [id_token_signing_key.serialize()]})
     auth_response = AuthorizationResponse(**{
         'state': state,
         'id_token': id_token
     })
     parsed_auth_response = facade.parse_authentication_response(
         auth_response)
     assert isinstance(parsed_auth_response, AuthorizationResponse)
     assert parsed_auth_response['state'] == state
     assert parsed_auth_response['id_token_jwt'] == id_token
 def test_should_handle_token_response_without_id_token(self, client_mock):
     token_response = {'access_token': 'test_token'}
     client_mock.exchange_authorization_code.return_value = AccessTokenResponse(
         **token_response)
     result = AuthResponseHandler(client_mock).process_auth_response(
         AuthorizationResponse(**self.AUTH_RESPONSE), self.AUTH_REQUEST)
     assert result.access_token == 'test_token'
     assert result.id_token_claims is None
 def test_should_handle_token_response_without_id_token(self, client_mock):
     token_response = {'access_token': 'test_token'}
     client_mock.token_request.return_value = AccessTokenResponse(
         **token_response)
     result = AuthResponseHandler(client_mock).process_auth_response(
         AuthorizationResponse(**self.AUTH_RESPONSE),
         self.AUTH_RESPONSE['state'],
         self.TOKEN_RESPONSE['id_token']['nonce'])
     assert result.access_token == 'test_token'
     assert result.id_token_claims is None
Exemple #11
0
    def do_authentication_request(self, url, ht_args, csi):
        self.conv.trace.request(url)
        self.conv.trace.request("HT_ARGS: {}".format(ht_args))
        r = self.conv.client.http_request(url, **ht_args)
        resp = None
        if 300 < r.status_code < 400:
            r = self.conv.intermit(r)
            resp = self.conv.parse_request_response(
                r, AuthorizationResponse, body_type="urlencoded",
                state=self.conv.state, keyjar=self.conv.client.keyjar)
        elif r.status_code == 200:
            resp = AuthorizationResponse()
            if "response_mode" in csi and csi["response_mode"] == "form_post":
                forms = BeautifulSoup(r.content).findAll('form')
                for inp in forms[0].find_all("input"):
                    resp[inp.attrs["name"]] = inp.attrs["value"]
            resp.verify(keyjar=self.conv.client.keyjar)

        self.conv.trace.response(resp)
        return resp
 def test_should_handle_no_token_response(self, client_mock):
     client_mock.exchange_authorization_code.return_value = None
     client_mock.userinfo_request.return_value = None
     hybrid_auth_response = self.AUTH_RESPONSE.copy()
     hybrid_auth_response.update(self.TOKEN_RESPONSE)
     result = AuthResponseHandler(client_mock).process_auth_response(
         AuthorizationResponse(**hybrid_auth_response), self.AUTH_REQUEST)
     assert result.access_token == 'test_token'
     assert result.id_token_claims == self.TOKEN_RESPONSE[
         'id_token'].to_dict()
     assert result.id_token_jwt == self.TOKEN_RESPONSE['id_token_jwt']
Exemple #13
0
 def test_should_handle_auth_response_without_authorization_code(self, client_mock):
     auth_response = AuthorizationResponse(**self.TOKEN_RESPONSE)
     auth_response['state'] = 'test_state'
     client_mock.userinfo_request.return_value = self.USERINFO_RESPONSE
     result = AuthResponseHandler(client_mock).process_auth_response(auth_response, 'test_state')
     assert not client_mock.exchange_authorization_code.called
     assert result.access_token == 'test_token'
     assert result.expires_in == self.TOKEN_RESPONSE['expires_in']
     assert result.id_token_jwt == self.TOKEN_RESPONSE['id_token_jwt']
     assert result.id_token_claims == self.TOKEN_RESPONSE['id_token'].to_dict()
     assert result.userinfo_claims == self.USERINFO_RESPONSE.to_dict()
     assert result.refresh_token == None
Exemple #14
0
    def test_issuer_mismatch(self):
        ISSUER = "https://login.microsoftonline.com/b4ea3de6-839e-4ad1-ae78-c78e5c0cdc06/v2.0/"
        kb = KeyBundle(JWK2["keys"])
        kj = KeyJar()
        kj.issuer_keys[ISSUER] = [kb]
        kj.issuer_keys[""] = []

        authz_resp = AuthorizationResponse().from_urlencoded(
            "id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSIsImtpZCI6Ik1u"
            "Q19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSJ9.eyJhdWQiOiIwMTZlZDBlNC1mYzUyLTRlYjgtOWVhYy1lODg1MmM4MjEwNTUiLCJpc3Mi"
            "OiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vYjRlYTNkZTYtODM5ZS00YWQxLWFlNzgtYzc4ZTVjMGNkYzA2L3YyLjAvI"
            "iwiaWF0IjoxNDM5OTIzNDY5LCJuYmYiOjE0Mzk5MjM0NjksImV4cCI6MTQzOTkyNzM2OSwidmVyIjoiMi4wIiwidGlkIjoiYjRlYTNkZT"
            "YtODM5ZS00YWQxLWFlNzgtYzc4ZTVjMGNkYzA2Iiwib2lkIjoiNDJjMzliNWUtYmQwNS00YTlhLTlhNWUtMTY5ZDc2N2ZlZjJmIiwicHJ"
            "lZmVycmVkX3VzZXJuYW1lIjoiaW50ZXJvcEBrYXV0aS5vbm1pY3Jvc29mdC5jb20iLCJzdWIiOiJFWGlaVldjakpsREN1LXZzOUxNb1V3"
            "ZGRNNEJZZ2ZISzBJNk40dWpXZkRFIiwibmFtZSI6ImludGVyb3AiLCJub25jZSI6IlpkSHRxQWwzR3c4QiJ9.tH4FKM4H9YCHX2XF4V64"
            "SsLaKh31c0oLpEVlFxFHw8jxL5HujUthZJDUMwngXZ2mPU_1G152ybKiRCV9DKaBh1rFSlZxTDBp0SV_YTwOkGYOt-sOzFUJyvVCjGmRh"
            "vFkOF1kiT3IYjDoRh72U8pMchj1duWSytLczdOc4LJmg24ya5jwqApuyQu7gVqoDH1kEqBAuhBj3a7ZDwxIt-bTKZklsht0RutZjv4Ckg"
            "8qJpzWnY7rIjSKFKfEpAAfk_LqWvTktvDMKTHXLxEPVZymoskE1LthtC8AYoNmtVPxgxf87yGCqYZBsuAnVChdnsItXP7tPeqUjC8Lm3J"
            "jabV-5g&id_token_expires_in=3599&state=6o3FmQ0QZl1zifsE&session_state=d2c97e8a-497c-4ce1-bb10-5058501164eb"
        )

        try:
            authz_resp.verify(keyjar=kj, skew=100000000)
        except MissingSigningKey:
            authz_resp.verify(keyjar=kj, sender=ISSUER, skew=100000000)
Exemple #15
0
    def do_authentication_request(self, url, ht_args, csi):
        self.conv.trace.request(url)
        self.conv.trace.request("HT_ARGS: {}".format(ht_args))
        r = self.conv.client.http_request(url, **ht_args)
        resp = None
        if 300 < r.status_code < 400:
            r = self.conv.intermit(r)
            resp = self.conv.parse_request_response(
                r,
                AuthorizationResponse,
                body_type="urlencoded",
                state=self.conv.state,
                keyjar=self.conv.client.keyjar)
        elif r.status_code == 200:
            resp = AuthorizationResponse()
            if "response_mode" in csi and csi["response_mode"] == "form_post":
                forms = BeautifulSoup(r.content).findAll('form')
                for inp in forms[0].find_all("input"):
                    resp[inp.attrs["name"]] = inp.attrs["value"]
            resp.verify(keyjar=self.conv.client.keyjar)

        self.conv.trace.response(resp)
        return resp
Exemple #16
0
 def test_parse_authentication_response_preserves_id_token_jwt(self):
     facade = PyoidcFacade(
         ProviderConfiguration(
             provider_metadata=self.PROVIDER_METADATA,
             client_metadata=self.CLIENT_METADATA,
         ),
         self.REDIRECT_URI,
     )
     state = "state-1234"
     now = int(time.time())
     id_token, id_token_signing_key = signed_id_token({
         "iss":
         self.PROVIDER_METADATA["issuer"],
         "sub":
         "test_sub",
         "aud":
         "client1",
         "exp":
         now + 1,
         "iat":
         now,
     })
     responses.add(
         responses.GET,
         self.PROVIDER_METADATA["jwks_uri"],
         json={"keys": [id_token_signing_key.serialize()]},
     )
     auth_response = AuthorizationResponse(**{
         "state": state,
         "id_token": id_token
     })
     parsed_auth_response = facade.parse_authentication_response(
         auth_response)
     assert isinstance(parsed_auth_response, AuthorizationResponse)
     assert parsed_auth_response["state"] == state
     assert parsed_auth_response["id_token_jwt"] == id_token
Exemple #17
0
def authorization_response():
    return AuthorizationResponse(code='code', state='state')
class TestAuthResponseHandler:
    AUTH_RESPONSE = AuthorizationResponse(**{
        'code': 'test_auth_code',
        'state': 'test_state'
    })
    TOKEN_RESPONSE = AccessTokenResponse(
        **{
            'access_token': 'test_token',
            'id_token': IdToken(**{
                'sub': 'test_sub',
                'nonce': 'test_nonce'
            }),
            'id_token_jwt': 'test_id_token_jwt'
        })
    USERINFO_RESPONSE = OpenIDSchema(**{'sub': 'test_sub'})
    ERROR_RESPONSE = {
        'error': 'test_error',
        'error_description': 'something went wrong'
    }

    @pytest.fixture
    def client_mock(self):
        return create_autospec(PyoidcFacade, True, True)

    def test_should_detect_state_mismatch(self, client_mock):
        with pytest.raises(AuthResponseUnexpectedStateError):
            AuthResponseHandler(client_mock).process_auth_response(
                self.AUTH_RESPONSE, 'other_state')

    def test_should_detect_nonce_mismatch(self, client_mock):
        client_mock.token_request.return_value = self.TOKEN_RESPONSE
        with pytest.raises(AuthResponseUnexpectedNonceError):
            AuthResponseHandler(client_mock).process_auth_response(
                self.AUTH_RESPONSE, self.AUTH_RESPONSE['state'], 'other_nonce')

    def test_should_handle_auth_error_response(self, client_mock):
        with pytest.raises(AuthResponseErrorResponseError) as exc:
            AuthResponseHandler(client_mock).process_auth_response(
                AuthorizationErrorResponse(**self.ERROR_RESPONSE),
                self.AUTH_RESPONSE['state'])
        assert exc.value.error_response == self.ERROR_RESPONSE

    def test_should_handle_token_error_response(self, client_mock):
        client_mock.token_request.return_value = TokenErrorResponse(
            **self.ERROR_RESPONSE)
        with pytest.raises(AuthResponseErrorResponseError) as exc:
            AuthResponseHandler(client_mock).process_auth_response(
                AuthorizationResponse(**self.AUTH_RESPONSE),
                self.AUTH_RESPONSE['state'])
        assert exc.value.error_response == self.ERROR_RESPONSE

    def test_should_detect_mismatching_subject(self, client_mock):
        client_mock.token_request.return_value = AccessTokenResponse(
            **self.TOKEN_RESPONSE)
        client_mock.userinfo_request.return_value = OpenIDSchema(
            **{'sub': 'other_sub'})
        with pytest.raises(AuthResponseMismatchingSubjectError):
            AuthResponseHandler(client_mock).process_auth_response(
                AuthorizationResponse(**self.AUTH_RESPONSE),
                self.AUTH_RESPONSE['state'],
                self.TOKEN_RESPONSE['id_token']['nonce'])

    def test_should_handle_auth_response_with_authorization_code(
            self, client_mock):
        client_mock.token_request.return_value = self.TOKEN_RESPONSE
        client_mock.userinfo_request.return_value = self.USERINFO_RESPONSE
        result = AuthResponseHandler(client_mock).process_auth_response(
            self.AUTH_RESPONSE, self.AUTH_RESPONSE['state'],
            self.TOKEN_RESPONSE['id_token']['nonce'])
        assert result.access_token == 'test_token'
        assert result.id_token_claims == self.TOKEN_RESPONSE[
            'id_token'].to_dict()
        assert result.id_token_jwt == self.TOKEN_RESPONSE['id_token_jwt']
        assert result.userinfo_claims == self.USERINFO_RESPONSE.to_dict()

    def test_should_handle_auth_response_without_authorization_code(
            self, client_mock):
        auth_response = AuthorizationResponse(**self.TOKEN_RESPONSE)
        auth_response['state'] = 'test_state'
        client_mock.userinfo_request.return_value = self.USERINFO_RESPONSE
        result = AuthResponseHandler(client_mock).process_auth_response(
            auth_response, 'test_state')
        assert not client_mock.token_request.called
        assert result.access_token == 'test_token'
        assert result.id_token_jwt == self.TOKEN_RESPONSE['id_token_jwt']
        assert result.id_token_claims == self.TOKEN_RESPONSE[
            'id_token'].to_dict()
        assert result.userinfo_claims == self.USERINFO_RESPONSE.to_dict()

    def test_should_handle_token_response_without_id_token(self, client_mock):
        token_response = {'access_token': 'test_token'}
        client_mock.token_request.return_value = AccessTokenResponse(
            **token_response)
        result = AuthResponseHandler(client_mock).process_auth_response(
            AuthorizationResponse(**self.AUTH_RESPONSE),
            self.AUTH_RESPONSE['state'],
            self.TOKEN_RESPONSE['id_token']['nonce'])
        assert result.access_token == 'test_token'
        assert result.id_token_claims is None

    def test_should_handle_no_token_response(self, client_mock):
        client_mock.token_request.return_value = None
        client_mock.userinfo_request.return_value = None
        hybrid_auth_response = self.AUTH_RESPONSE.copy()
        hybrid_auth_response.update(self.TOKEN_RESPONSE)
        result = AuthResponseHandler(client_mock).process_auth_response(
            AuthorizationResponse(**hybrid_auth_response),
            self.AUTH_RESPONSE['state'],
            self.TOKEN_RESPONSE['id_token']['nonce'])
        assert result.access_token == 'test_token'
        assert result.id_token_claims == self.TOKEN_RESPONSE[
            'id_token'].to_dict()
        assert result.id_token_jwt == self.TOKEN_RESPONSE['id_token_jwt']

    @pytest.mark.parametrize(
        'response_type, expected',
        [
            ('code', False),  # Authorization Code Flow
            ('id_token', True),  # Implicit Flow
            ('id_token token', True),  # Implicit Flow
            ('code id_token', True),  # Hybrid Flow
            ('code token', True),  # Hybrid Flow
            ('code id_token token', True)  # Hybrid Flow
        ])
    def test_expect_fragment_encoded_response_by_response_type(
            self, response_type, expected):
        assert AuthResponseHandler.expect_fragment_encoded_response(
            {'response_type': response_type}) is expected

    @pytest.mark.parametrize('response_type, response_mode, expected', [
        ('code', 'fragment', True),
        ('id_token', 'query', False),
        ('code token', 'form_post', False),
    ])
    def test_expect_fragment_encoded_response_with_non_default_response_mode(
            self, response_type, response_mode, expected):
        auth_req = {
            'response_type': response_type,
            'response_mode': response_mode
        }
        assert AuthResponseHandler.expect_fragment_encoded_response(
            auth_req) is expected
class TestAuthResponseHandler:
    ISSUER = 'https://issuer.example.com'
    CLIENT_ID = 'client1'
    AUTH_REQUEST = AuthorizationRequest(**{
        'state': 'test_state',
        'nonce': 'test_nonce'
    })
    AUTH_RESPONSE = AuthorizationResponse(**{
        'code': 'test_auth_code',
        'state': AUTH_REQUEST['state']
    })
    TOKEN_RESPONSE = AccessTokenResponse(
        **{
            'access_token': 'test_token',
            'expires_in': 3600,
            'id_token': _create_id_token(ISSUER, CLIENT_ID,
                                         AUTH_REQUEST['nonce']),
            'id_token_jwt': 'test_id_token_jwt',
            'refresh_token': 'test_refresh_token'
        })
    USERINFO_RESPONSE = OpenIDSchema(**{'sub': 'test_sub'})
    ERROR_RESPONSE = {
        'error': 'test_error',
        'error_description': 'something went wrong'
    }

    @pytest.fixture
    def client_mock(self):
        return create_autospec(PyoidcFacade, True, True)

    def test_should_detect_state_mismatch(self, client_mock):
        auth_request = {
            'state': 'other_state',
            'nonce': self.AUTH_REQUEST['nonce']
        }
        with pytest.raises(AuthResponseUnexpectedStateError):
            AuthResponseHandler(client_mock).process_auth_response(
                self.AUTH_RESPONSE, auth_request)

    def test_should_detect_nonce_mismatch(self, client_mock):
        client = PyoidcFacade(
            ProviderConfiguration(
                provider_metadata=ProviderMetadata(issuer=self.ISSUER),
                client_metadata=ClientMetadata(client_id=self.CLIENT_ID)),
            redirect_uri='https://client.example.com/redirect')
        client.exchange_authorization_code = MagicMock(
            return_value=self.TOKEN_RESPONSE)
        auth_request = {
            'state': self.AUTH_RESPONSE['state'],
            'nonce': 'other_nonce'
        }
        with pytest.raises(InvalidIdTokenError):
            AuthResponseHandler(client).process_auth_response(
                self.AUTH_RESPONSE, auth_request)

    def test_should_handle_auth_error_response(self, client_mock):
        with pytest.raises(AuthResponseErrorResponseError) as exc:
            AuthResponseHandler(client_mock).process_auth_response(
                AuthorizationErrorResponse(**self.ERROR_RESPONSE),
                self.AUTH_REQUEST)
        assert exc.value.error_response == self.ERROR_RESPONSE

    def test_should_handle_token_error_response(self, client_mock):
        client_mock.exchange_authorization_code.return_value = TokenErrorResponse(
            **self.ERROR_RESPONSE)
        with pytest.raises(AuthResponseErrorResponseError) as exc:
            AuthResponseHandler(client_mock).process_auth_response(
                AuthorizationResponse(**self.AUTH_RESPONSE), self.AUTH_REQUEST)
        assert exc.value.error_response == self.ERROR_RESPONSE

    def test_should_detect_mismatching_subject(self, client_mock):
        client_mock.exchange_authorization_code.return_value = AccessTokenResponse(
            **self.TOKEN_RESPONSE)
        client_mock.userinfo_request.return_value = OpenIDSchema(
            **{'sub': 'other_sub'})
        with pytest.raises(AuthResponseMismatchingSubjectError):
            AuthResponseHandler(client_mock).process_auth_response(
                AuthorizationResponse(**self.AUTH_RESPONSE), self.AUTH_REQUEST)

    def test_should_handle_auth_response_with_authorization_code(
            self, client_mock):
        client_mock.exchange_authorization_code.return_value = self.TOKEN_RESPONSE
        client_mock.userinfo_request.return_value = self.USERINFO_RESPONSE
        result = AuthResponseHandler(client_mock).process_auth_response(
            self.AUTH_RESPONSE, self.AUTH_REQUEST)
        assert result.access_token == 'test_token'
        assert result.expires_in == self.TOKEN_RESPONSE['expires_in']
        assert result.id_token_claims == self.TOKEN_RESPONSE[
            'id_token'].to_dict()
        assert result.id_token_jwt == self.TOKEN_RESPONSE['id_token_jwt']
        assert result.userinfo_claims == self.USERINFO_RESPONSE.to_dict()
        assert result.refresh_token == self.TOKEN_RESPONSE['refresh_token']

    def test_should_handle_auth_response_without_authorization_code(
            self, client_mock):
        auth_response = AuthorizationResponse(**self.TOKEN_RESPONSE)
        auth_response['state'] = 'test_state'
        client_mock.userinfo_request.return_value = self.USERINFO_RESPONSE
        result = AuthResponseHandler(client_mock).process_auth_response(
            auth_response, self.AUTH_REQUEST)
        assert not client_mock.exchange_authorization_code.called
        assert result.access_token == 'test_token'
        assert result.expires_in == self.TOKEN_RESPONSE['expires_in']
        assert result.id_token_jwt == self.TOKEN_RESPONSE['id_token_jwt']
        assert result.id_token_claims == self.TOKEN_RESPONSE[
            'id_token'].to_dict()
        assert result.userinfo_claims == self.USERINFO_RESPONSE.to_dict()
        assert result.refresh_token == None

    def test_should_handle_token_response_without_id_token(self, client_mock):
        token_response = {'access_token': 'test_token'}
        client_mock.exchange_authorization_code.return_value = AccessTokenResponse(
            **token_response)
        result = AuthResponseHandler(client_mock).process_auth_response(
            AuthorizationResponse(**self.AUTH_RESPONSE), self.AUTH_REQUEST)
        assert result.access_token == 'test_token'
        assert result.id_token_claims is None

    def test_should_handle_no_token_response(self, client_mock):
        client_mock.exchange_authorization_code.return_value = None
        client_mock.userinfo_request.return_value = None
        hybrid_auth_response = self.AUTH_RESPONSE.copy()
        hybrid_auth_response.update(self.TOKEN_RESPONSE)
        result = AuthResponseHandler(client_mock).process_auth_response(
            AuthorizationResponse(**hybrid_auth_response), self.AUTH_REQUEST)
        assert result.access_token == 'test_token'
        assert result.id_token_claims == self.TOKEN_RESPONSE[
            'id_token'].to_dict()
        assert result.id_token_jwt == self.TOKEN_RESPONSE['id_token_jwt']

    @pytest.mark.parametrize(
        'response_type, expected',
        [
            ('code', False),  # Authorization Code Flow
            ('id_token', True),  # Implicit Flow
            ('id_token token', True),  # Implicit Flow
            ('code id_token', True),  # Hybrid Flow
            ('code token', True),  # Hybrid Flow
            ('code id_token token', True)  # Hybrid Flow
        ])
    def test_expect_fragment_encoded_response_by_response_type(
            self, response_type, expected):
        assert AuthResponseHandler.expect_fragment_encoded_response(
            {'response_type': response_type}) is expected

    @pytest.mark.parametrize('response_type, response_mode, expected', [
        ('code', 'fragment', True),
        ('id_token', 'query', False),
        ('code token', 'form_post', False),
    ])
    def test_expect_fragment_encoded_response_with_non_default_response_mode(
            self, response_type, response_mode, expected):
        auth_req = {
            'response_type': response_type,
            'response_mode': response_mode
        }
        assert AuthResponseHandler.expect_fragment_encoded_response(
            auth_req) is expected
Exemple #20
0
 def get_tokens(self, query):
     aresp = AuthorizationResponse().from_urlencoded(query)
     uid = self.client.acquire_access_token(aresp, "AAT")
     self.client.get_rpt(uid)
     return uid