示例#1
0
    def test_token_error_reponse_returns_default_error_if_no_error_view_set(
            self):
        token_endpoint = ISSUER + '/token'
        error_response = {
            'error': 'invalid_request',
            'error_description': 'test error'
        }
        responses.add(responses.POST,
                      token_endpoint,
                      body=json.dumps(error_response),
                      content_type='application/json')

        authn = OIDCAuthentication(self.app,
                                   provider_configuration_info={
                                       'issuer': ISSUER,
                                       'token_endpoint': token_endpoint
                                   },
                                   client_registration_info=dict(
                                       client_id='abc', client_secret='foo'))
        state = 'test_tate'
        with self.app.test_request_context('/redirect_uri?code=foo&state=' +
                                           state):
            flask.session['state'] = state
            response = authn._handle_authentication_response()
        assert response == 'Something went wrong with the authentication, please try to login again.'
示例#2
0
 def test_reauthentication_necessary_with_valid_access_token(self):
     authn = OIDCAuthentication(
         self.app,
         provider_configuration_info={'issuer': ISSUER},
         client_registration_info={'client_id': 'foo'})
     access_token = 'test token'
     assert authn._reauthentication_necessary(access_token) is False
示例#3
0
 def test_configurable_userinfo_endpoint_method_is_used(self, method):
     state = 'state'
     nonce = 'nonce'
     sub = 'foobar'
     authn = OIDCAuthentication(
         self.app,
         provider_configuration_info={
             'issuer': ISSUER,
             'token_endpoint': '/token'
         },
         client_registration_info={'client_id': 'foo'},
         userinfo_endpoint_method=method)
     authn.client.do_access_token_request = MagicMock(
         return_value=AccessTokenResponse(
             **{
                 'id_token': IdToken(**{
                     'sub': sub,
                     'nonce': nonce
                 }),
                 'access_token': 'access_token'
             }))
     userinfo_request_mock = MagicMock(return_value=OpenIDSchema(
         **{'sub': sub}))
     authn.client.do_user_info_request = userinfo_request_mock
     with self.app.test_request_context('/redirect_uri?code=foo&state=' +
                                        state):
         flask.session['state'] = state
         flask.session['nonce'] = nonce
         flask.session['destination'] = '/'
         authn._handle_authentication_response()
     userinfo_request_mock.assert_called_with(method=method, state=state)
示例#4
0
    def auth(self, app):
        """ Creates the OIDCAuthentication object to be used to protect routes.

        Authentication is requested with the following audiences:
        - lando-api: The LANDO_API_OIDC_IDENTIFIER environment variable will
            be included as an audience. This allows lando-api to verify that
            tokens created by lando-ui were intended to be used by the api.

        Authentication is requested with the following scopes:
        - openid - Permission to get a unique identifier for the user. This
            also permits querying Auth0 at https://OIDC_DOMAIN/userinfo for
            additional user information.
        - email - Permission to get the user's email address.
        - profile - Permission to get additional information about the user
          such as their real name, picture url, and LDAP information.
        """
        oidc = OIDCAuthentication(
            app,
            provider_configuration_info=self.provider_info(),
            client_registration_info=self.client_info(),
            extra_request_args={
                "audience": [self.oidc_config.lando_api_oidc_id()],
                'scope': ['openid', 'profile', 'email']
            })
        return oidc
示例#5
0
    def test_does_not_need_refresh(self):
        authn = OIDCAuthentication(
            self.app,
            provider_configuration_info={'issuer': ISSUER},
            client_registration_info={
                'client_id': 'foo',
                'session_refresh_interval_seconds': 1
            },
        )
        client_mock = MagicMock()
        callback_mock = MagicMock()
        now = time.time()
        callback_mock.__name__ = 'test_callback'  # required for Python 2
        authn.client = client_mock
        id_token = IdToken(**{'sub': 'sub1', 'nonce': 'nonce', 'exp': 0})
        with self.app.test_request_context('/'):
            flask.session['destination'] = '/'
            flask.session['access_token'] = 'test token'
            flask.session['id_token'] = id_token.to_dict()
            flask.session['id_token_jwt'] = id_token.to_jwt()
            flask.session['last_authenticated'] = now + 100
            authn.oidc_auth(callback_mock)()
            session = Session(
                flask_session=flask.session,
                client_registration_info=authn.client_registration_info)

            assert session.needs_refresh() is False
示例#6
0
    def init_app(self,
                 provider_metadata_extras=None,
                 client_metadata_extras=None,
                 **kwargs):
        required_provider_metadata = {
            'issuer': self.PROVIDER_BASEURL,
            'authorization_endpoint': self.PROVIDER_BASEURL + '/auth',
            'jwks_uri': self.PROVIDER_BASEURL + '/jwks'
        }
        if provider_metadata_extras:
            required_provider_metadata.update(provider_metadata_extras)
        provider_metadata = ProviderMetadata(**required_provider_metadata)

        required_client_metadata = {
            'client_id': self.CLIENT_ID,
            'client_secret': 'secret1'
        }
        if client_metadata_extras:
            required_client_metadata.update(client_metadata_extras)
        client_metadata = ClientMetadata(**required_client_metadata)

        provider_configurations = {
            self.PROVIDER_NAME:
            ProviderConfiguration(provider_metadata=provider_metadata,
                                  client_metadata=client_metadata,
                                  **kwargs)
        }
        authn = OIDCAuthentication(provider_configurations)
        authn.init_app(self.app)
        return authn
示例#7
0
def get_auth(app):
    auth = OIDCAuthentication(
        app,
        issuer=app.config['OIDC_ISSUER'],
        client_registration_info=app.config['OIDC_CLIENT_CONFIG'],
    )
    return auth
示例#8
0
    def auth(self, app):
        o = OIDCAuthentication(
            app,
            provider_configuration_info=self.provider_info(),
            client_registration_info=self.client_info())

        return o
示例#9
0
    def test_token_error_reponse_calls_to_error_view_if_set(self):
        token_endpoint = ISSUER + '/token'
        error_response = {
            'error': 'invalid_request',
            'error_description': 'test error'
        }
        responses.add(responses.POST,
                      token_endpoint,
                      body=json.dumps(error_response),
                      content_type='application/json')

        authn = OIDCAuthentication(self.app,
                                   provider_configuration_info={
                                       'issuer': ISSUER,
                                       'token_endpoint': token_endpoint
                                   },
                                   client_registration_info=dict(
                                       client_id='abc', client_secret='foo'))
        error_view_mock = MagicMock()
        authn._error_view = error_view_mock
        state = 'test_tate'
        with self.app.test_request_context('/redirect_uri?code=foo&state=' +
                                           state):
            flask.session['state'] = state
            authn._handle_authentication_response()
        error_view_mock.assert_called_with(**error_response)
示例#10
0
 def get_oidc(self, app):
     extra_request_args = {"scope": ["openid", "profile"]}
     o = OIDCAuthentication(
         app,
         issuer="https://{DOMAIN}".format(
             DOMAIN=self.oidc_config.OIDC_DOMAIN),
         client_registration_info=self.client_info(),
         extra_request_args=extra_request_args,
     )
     return o
示例#11
0
 def auth(self, app):
     o = OIDCAuthentication(app,
                            issuer='https://' +
                            self.provider_info()['issuer'],
                            client_registration_info=self.client_info())
     """ Patch rewrites redirect_uri to only
     SSL if running in production or stage. """
     if os.getenv('environment', 'production') is not 'development':
         redirect_uri = o.client.registration_response['redirect_uris'][0]
         o.client.registration_response['redirect_uris'][0] = \
             redirect_uri.replace('http', 'http')
     return o
示例#12
0
 def test_no_userinfo_request_is_done_if_no_userinfo_endpoint_method_is_specified(
         self):
     state = 'state'
     authn = OIDCAuthentication(
         self.app,
         provider_configuration_info={'issuer': ISSUER},
         client_registration_info={'client_id': 'foo'},
         userinfo_endpoint_method=None)
     userinfo_request_mock = MagicMock()
     authn.client.do_user_info_request = userinfo_request_mock
     authn._do_userinfo_request(state, None)
     assert not userinfo_request_mock.called
示例#13
0
    def test_authenticatate_with_extra_request_parameters(self):
        extra_params = {"foo": "bar", "abc": "xyz"}
        authn = OIDCAuthentication(
            self.app,
            provider_configuration_info={'issuer': ISSUER},
            client_registration_info={'client_id': 'foo'},
            extra_request_args=extra_params)

        with self.app.test_request_context('/'):
            a = authn._authenticate()
        request_params = dict(parse_qsl(urlparse(a.location).query))
        assert set(extra_params.items()).issubset(set(request_params.items()))
示例#14
0
 def auth(self, app):
     o = OIDCAuthentication(
         app,
         provider_configuration_info=self.provider_info(),
         client_registration_info=self.client_info())
     """ Patch rewrites redirect_uri to only
     SSL if running in production or stage. """
     if os.environ['ENVIRONMENT'] == 'Production':
         redirect_uri = o.client.registration_response['redirect_uris'][0]
         o.client.registration_response['redirect_uris'][0] = \
             redirect_uri.replace('http', 'https')
     return o
示例#15
0
    def test_session_expiration_set_to_id_token_exp(self):
        token_endpoint = ISSUER + '/token'
        userinfo_endpoint = ISSUER + '/userinfo'
        exp_time = 10
        epoch_int = int(time.mktime(datetime(2017, 1, 1).timetuple()))
        id_token = IdToken(
            **{
                'sub': 'sub1',
                'iat': epoch_int,
                'iss': ISSUER,
                'aud': 'foo',
                'nonce': 'test',
                'exp': epoch_int + exp_time
            })
        token_response = {
            'access_token': 'test',
            'token_type': 'Bearer',
            'id_token': id_token.to_jwt()
        }
        userinfo_response = {'sub': 'sub1'}
        responses.add(responses.POST,
                      token_endpoint,
                      body=json.dumps(token_response),
                      content_type='application/json')
        responses.add(responses.POST,
                      userinfo_endpoint,
                      body=json.dumps(userinfo_response),
                      content_type='application/json')
        authn = OIDCAuthentication(
            self.app,
            provider_configuration_info={
                'issuer': ISSUER,
                'token_endpoint': token_endpoint,
                'userinfo_endpoint': userinfo_endpoint
            },
            client_registration_info={
                'client_id': 'foo',
                'client_secret': 'foo'
            },
        )

        self.app.config.update({'SESSION_PERMANENT': True})
        with self.app.test_request_context(
                '/redirect_uri?state=test&code=test'):
            flask.session['destination'] = '/'
            flask.session['state'] = 'test'
            flask.session['nonce'] = 'test'
            flask.session['id_token'] = id_token.to_dict()
            flask.session['id_token_jwt'] = id_token.to_jwt()
            authn._handle_authentication_response()
            assert flask.session.permanent is True
            assert int(flask.session.permanent_session_lifetime) == exp_time
示例#16
0
 def test_reauthenticate_if_no_session(self):
     authn = OIDCAuthentication(
         self.app,
         provider_configuration_info={'issuer': ISSUER},
         client_registration_info={'client_id': 'foo'})
     client_mock = MagicMock()
     callback_mock = MagicMock()
     callback_mock.__name__ = 'test_callback'  # required for Python 2
     authn.client = client_mock
     with self.app.test_request_context('/'):
         authn.oidc_auth(callback_mock)()
     assert client_mock.construct_AuthorizationRequest.called
     assert not callback_mock.called
    def auth(self, app):
        if config.fake_account:
            return FakeOIDCAuthentication()

        oidc = OIDCAuthentication(
            app,
            issuer='https://{DOMAIN}/'.format(DOMAIN=config.oidc_domain),
            client_registration_info=self.client_info(),
            extra_request_args={
                'scope': ['openid', 'profile', 'email'],
            },
        )
        return oidc
示例#18
0
    def test_store_internal_redirect_uri_on_static_client_reg(self):
        responses.add(responses.GET,
                      ISSUER + '/.well-known/openid-configuration',
                      body=json.dumps(
                          dict(issuer=ISSUER,
                               token_endpoint=ISSUER + '/token')),
                      content_type='application/json')

        authn = OIDCAuthentication(self.app,
                                   issuer=ISSUER,
                                   client_registration_info=dict(
                                       client_id='abc', client_secret='foo'))
        assert len(authn.client.registration_response['redirect_uris']) == 1
        assert authn.client.registration_response['redirect_uris'][
            0] == 'http://localhost/redirect_uri'
示例#19
0
 def test_dont_reauthenticate_with_valid_id_token(self):
     authn = OIDCAuthentication(
         self.app,
         provider_configuration_info={'issuer': ISSUER},
         client_registration_info={'client_id': 'foo'})
     client_mock = MagicMock()
     callback_mock = MagicMock()
     callback_mock.__name__ = 'test_callback'  # required for Python 2
     authn.client = client_mock
     with self.app.test_request_context('/'):
         flask.session['destination'] = '/'
         flask.session['access_token'] = 'test token'
         authn.oidc_auth(callback_mock)()
     assert not client_mock.construct_AuthorizationRequest.called
     assert callback_mock.called is True
示例#20
0
 def test_dont_reauthenticate_silent_if_authentication_not_expired(self):
     authn = OIDCAuthentication(
         self.app,
         provider_configuration_info={'issuer': ISSUER},
         client_registration_info={
             'client_id': 'foo',
             'session_refresh_interval_seconds': 999
         })
     client_mock = MagicMock()
     callback_mock = MagicMock()
     callback_mock.__name__ = 'test_callback'  # required for Python 2
     authn.client = client_mock
     with self.app.test_request_context('/'):
         flask.session['last_authenticated'] = time.time(
         )  # freshly authenticated
         authn.oidc_auth(callback_mock)()
     assert not client_mock.construct_AuthorizationRequest.called
     assert callback_mock.called
示例#21
0
 def test_reauthenticate_if_no_session(self):
     authn = OIDCAuthentication(
         self.app,
         provider_configuration_info={'issuer': ISSUER},
         client_registration_info={'client_id': 'foo'},
     )
     client_mock = MagicMock()
     callback_mock = MagicMock()
     callback_mock.__name__ = 'test_callback'  # required for Python 2
     authn.client = client_mock
     id_token = IdToken(**{'sub': 'sub1', 'nonce': 'nonce'})
     with self.app.test_request_context('/'):
         flask.session['destination'] = '/'
         flask.session['access_token'] = None
         flask.session['id_token_jwt'] = None
         authn.oidc_auth(callback_mock)()
     assert client_mock.construct_AuthorizationRequest.called is True
     assert callback_mock.called is False
示例#22
0
 def test_authentication_error_reponse_returns_default_error_if_no_error_view_set(
         self):
     state = 'test_tate'
     error_response = {
         'error': 'invalid_request',
         'error_description': 'test error'
     }
     authn = OIDCAuthentication(
         self.app,
         provider_configuration_info={'issuer': ISSUER},
         client_registration_info=dict(client_id='abc',
                                       client_secret='foo'))
     with self.app.test_request_context(
             '/redirect_uri?{error}&state={state}'.format(
                 error=urlencode(error_response), state=state)):
         flask.session['state'] = state
         response = authn._handle_authentication_response()
     assert response == 'Something went wrong with the authentication, please try to login again.'
示例#23
0
 def test_authentication_error_reponse_calls_to_error_view_if_set(self):
     state = 'test_tate'
     error_response = {
         'error': 'invalid_request',
         'error_description': 'test error'
     }
     authn = OIDCAuthentication(
         self.app,
         provider_configuration_info={'issuer': ISSUER},
         client_registration_info=dict(client_id='abc',
                                       client_secret='foo'))
     error_view_mock = MagicMock()
     authn._error_view = error_view_mock
     with self.app.test_request_context(
             '/redirect_uri?{error}&state={state}'.format(
                 error=urlencode(error_response), state=state)):
         flask.session['state'] = state
         authn._handle_authentication_response()
     error_view_mock.assert_called_with(**error_response)
示例#24
0
    def test_should_register_client_if_not_registered_before(self):
        registration_endpoint = self.PROVIDER_BASEURL + '/register'
        provider_metadata = ProviderMetadata(
            self.PROVIDER_BASEURL,
            self.PROVIDER_BASEURL + '/auth',
            self.PROVIDER_BASEURL + '/jwks',
            registration_endpoint=registration_endpoint)
        provider_configurations = {
            self.PROVIDER_NAME:
            ProviderConfiguration(
                provider_metadata=provider_metadata,
                client_registration_info=ClientRegistrationInfo())
        }
        authn = OIDCAuthentication(provider_configurations)
        authn.init_app(self.app)

        # register logout view to force 'post_logout_redirect_uris' to be included in registration request
        logout_view_mock = self.get_view_mock()
        self.app.add_url_rule('/logout', view_func=logout_view_mock)
        authn.oidc_logout(logout_view_mock)

        responses.add(responses.POST,
                      registration_endpoint,
                      json={
                          'client_id': 'client1',
                          'client_secret': 'secret1'
                      })
        view_mock = self.get_view_mock()
        with self.app.test_request_context('/'):
            auth_redirect = authn.oidc_auth(self.PROVIDER_NAME)(view_mock)()

        self.assert_auth_redirect(auth_redirect)
        registration_request = json.loads(
            responses.calls[0].request.body.decode('utf-8'))
        expected_registration_request = {
            'redirect_uris':
            ['http://{}/redirect_uri'.format(self.CLIENT_DOMAIN)],
            'post_logout_redirect_uris':
            ['http://{}/logout'.format(self.CLIENT_DOMAIN)]
        }
        assert registration_request == expected_registration_request
示例#25
0
    def auth(self, app):
        """ Creates the OIDCAuthentication object to be used to protect routes.

        Authentication is requested with the following audiences:
        - lando-api: The LANDO_API_OIDC_IDENTIFIER environment variable will
            be included as an audience. This allows lando-api to verify that
            tokens created by lando-ui were intended to be used by the api.

        Authentication is requested with the following scopes:
        - openid - Permission to get a unique identifier for the user. This
            also permits querying Auth0 at https://OIDC_DOMAIN/userinfo for
            additional user information.
        - email - Permission to get the user's email address.
        - profile - Permission to get additional information about the user
          such as their real name, picture url, and LDAP information.
        """
        oidc = OIDCAuthentication({"AUTH0": self.provider_configuration}, app=app)
        auth0 = oidc.clients["AUTH0"]
        oidc.clients["AUTH0"]._parse_response = parse_response_wrapper(auth0)
        oidc.clients["AUTH0"].userinfo_request = userinfo_request_wrapper(auth0)
        return oidc
示例#26
0
    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
示例#27
0
    def test_authenticated_session(self):
        authn = OIDCAuthentication(
            self.app,
            provider_configuration_info={'issuer': ISSUER},
            client_registration_info={'client_id': 'foo'},
        )
        client_mock = MagicMock()
        callback_mock = MagicMock()
        callback_mock.__name__ = 'test_callback'  # required for Python 2
        authn.client = client_mock
        id_token = IdToken(**{'sub': 'sub1', 'nonce': 'nonce', 'exp': 0})
        with self.app.test_request_context('/'):
            flask.session['destination'] = '/'
            flask.session['access_token'] = 'test token'
            flask.session['id_token'] = id_token.to_dict()
            flask.session['id_token_jwt'] = id_token.to_jwt()
            authn.oidc_auth(callback_mock)()
            session = Session(
                flask_session=flask.session,
                client_registration_info=authn.client_registration_info)

            assert session.authenticated() is True
示例#28
0
    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
示例#29
0
 def test_oidc_logout_handles_redirects_from_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
     state = 'end_session_123'
     with self.app.test_request_context('/logout?state=' + state):
         flask.session['end_session_state'] = state
         authn.oidc_logout(callback_mock)()
         assert 'end_session_state' not in flask.session
     assert callback_mock.called
示例#30
0
 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