Example #1
0
def test_oidc_device_flow_auto_detect(requests_mock, caplog, mode, use_pkce,
                                      client_secret, expected_fields):
    client_id = "myclient"
    oidc_discovery_url = "http://oidc.test/.well-known/openid-configuration"
    oidc_mock = OidcMock(
        requests_mock=requests_mock,
        expected_grant_type="urn:ietf:params:oauth:grant-type:device_code",
        expected_client_id=client_id,
        oidc_discovery_url=oidc_discovery_url,
        expected_fields=expected_fields,
        state={
            "device_code_callback_timeline":
            ["authorization_pending", "slow_down", "great success"]
        },
        scopes_supported=["openid", "df"])
    provider = OidcProviderInfo(discovery_url=oidc_discovery_url,
                                scopes=["df"])
    display = []
    authenticator = OidcDeviceAuthenticator(client_info=OidcClientInfo(
        client_id=client_id, provider=provider, client_secret=client_secret),
                                            display=display.append,
                                            use_pkce=use_pkce)
    with mock.patch.object(openeo.rest.auth.oidc.time, "sleep") as sleep:
        with caplog.at_level(logging.INFO):
            tokens = authenticator.get_tokens()
    assert oidc_mock.state["access_token"] == tokens.access_token
    assert re.search(
        r"visit https://auth\.test/dc and enter the user code {c!r}".format(
            c=oidc_mock.state['user_code']), display[0])
    assert display[1] == "Authorized successfully."
    assert sleep.mock_calls == [mock.call(2), mock.call(2), mock.call(7)]
    assert re.search(
        r"Authorization pending\..*Polling too fast, will slow down\..*Authorized successfully\.",
        caplog.text,
        flags=re.DOTALL)
Example #2
0
    def authenticate_oidc_device(self,
                                 client_id: str = None,
                                 client_secret: str = None,
                                 provider_id: str = None,
                                 store_refresh_token=False,
                                 use_pkce: Union[bool, None] = None,
                                 **kwargs) -> 'Connection':
        """
        Authenticate with OAuth Device Authorization grant/flow

        :param use_pkce: Use PKCE instead of client secret.
            If not set explicitly to `True` (use PKCE) or `False` (use client secret),
            it will be attempted to detect the best mode automatically.
            Note that PKCE for device code is not widely supported among OIDC providers.

        .. versionchanged:: 0.5.1 Add :py:obj:`use_pkce` argument
        """
        provider_id, client_info = self._get_oidc_provider_and_client_info(
            provider_id=provider_id,
            client_id=client_id,
            client_secret=client_secret,
            default_client_grant_types=[
                DefaultOidcClientGrant.DEVICE_CODE_PKCE
            ],
        )
        authenticator = OidcDeviceAuthenticator(client_info=client_info,
                                                use_pkce=use_pkce,
                                                **kwargs)
        return self._authenticate_oidc(authenticator,
                                       provider_id=provider_id,
                                       store_refresh_token=store_refresh_token)
Example #3
0
    def authenticate_oidc(self,
                          provider_id: str = None,
                          client_id: Union[str, None] = None,
                          client_secret: Union[str, None] = None,
                          store_refresh_token: bool = True):
        """
        Do OpenID Connect authentication, first trying refresh tokens and falling back on device code flow.

        .. versionadded:: 0.6.0
        """
        provider_id, client_info = self._get_oidc_provider_and_client_info(
            provider_id=provider_id,
            client_id=client_id,
            client_secret=client_secret,
            default_client_grant_types=[
                DefaultOidcClientGrant.DEVICE_CODE_PKCE,
                DefaultOidcClientGrant.REFRESH_TOKEN
            ])

        # Try refresh token first.
        refresh_token = self._get_refresh_token_store().get_refresh_token(
            issuer=client_info.provider.issuer,
            client_id=client_info.client_id)
        if refresh_token:
            try:
                _log.info(
                    "Found refresh token: trying refresh token based authentication."
                )
                authenticator = OidcRefreshTokenAuthenticator(
                    client_info=client_info, refresh_token=refresh_token)
                con = self._authenticate_oidc(
                    authenticator,
                    provider_id=provider_id,
                    store_refresh_token=store_refresh_token)
                # TODO: pluggable/jupyter-aware display function?
                print("Authenticated using refresh token.")
                return con
            except OidcException as e:
                _log.info(
                    "Refresh token based authentication failed: {e}.".format(
                        e=e))

        # Fall back on device code flow
        # TODO: make it possible to do other fallback flows too?
        _log.info("Trying device code flow.")
        authenticator = OidcDeviceAuthenticator(client_info=client_info)
        con = self._authenticate_oidc(authenticator,
                                      provider_id=provider_id,
                                      store_refresh_token=store_refresh_token)
        print("Authenticated using device code flow.")
        return con
Example #4
0
    def authenticate_oidc_device(
            self, client_id: str=None, client_secret: str=None, provider_id: str = None,
            store_refresh_token=False,
            **kwargs
    ) -> 'Connection':
        """
        Authenticate with OAuth Device Authorization grant/flow

        WARNING: this API is in experimental phase
        """
        provider_id, client_info = self._get_oidc_provider_and_client_info(
            provider_id=provider_id, client_id=client_id, client_secret=client_secret
        )
        authenticator = OidcDeviceAuthenticator(client_info=client_info, **kwargs)
        return self._authenticate_oidc(authenticator, provider_id=provider_id, store_refresh_token=store_refresh_token)
    def authenticate_oidc_device(self,
                                 client_id: str,
                                 client_secret: str,
                                 provider_id: str = None,
                                 **kwargs) -> 'Connection':
        """
        Authenticate with OAuth Device Authorization grant/flow

        WARNING: this API is in experimental phase
        """
        provider_id, provider = self._get_oidc_provider(provider_id)
        client_info = OidcClientInfo(client_id=client_id,
                                     provider=provider,
                                     client_secret=client_secret)
        authenticator = OidcDeviceAuthenticator(client_info=client_info,
                                                **kwargs)
        return self._authenticate_oidc(authenticator, provider_id=provider_id)