Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move login flows #1057

Merged
merged 6 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions changelog.d/20240918_120618_derek_move_login_flows.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

Changed
~~~~~~~

- LoginFlowManagers have been moved from ``globus_sdk.experimental.login_flow_managers``
to ``globus_sdk.login_flows``. (:pr:`NUMBER`)
kurtmckee marked this conversation as resolved.
Show resolved Hide resolved


1 change: 1 addition & 0 deletions docs/authorization/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ Components of the Globus SDK which handle application authorization.

globus_authorizers
scopes_and_consents/index
login_flows
gare
100 changes: 100 additions & 0 deletions docs/authorization/login_flows.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@

Login Flow Managers
===================

.. currentmodule:: globus_sdk.login_flows

This page provides references for the LoginFlowManager abstract class and some concrete
implementations.

A login flow manager is a class responsible for driving a user through a login flow,
with the ultimate goal of obtaining tokens. The tokens are required to make
requests against any Globus services.

Interface
---------

.. autoclass:: LoginFlowManager
:members:

Command Line
------------

As the name might suggest, a CommandLineLoginFlowManager drives user logins through
the command line (stdin/stdout). When run, the manager will print a URL to the console
then prompt a user to navigate to that url and enter the resulting auth code
derek-globus marked this conversation as resolved.
Show resolved Hide resolved
back into the terminal.

Example Code:

.. code-block:: pycon

>>> from globus_sdk import NativeAppAuthClient
>>> from globus_sdk.scopes import TransferScopes
>>> from globus_sdk.login_flows import CommandLineLoginFlowManager

>>> login_client = NativeAppAuthClient(client_id=client_id)
>>> manager = CommandLineLoginFlowManager(login_client)
>>>
>>> token_response = manager.run_login_flow(
... GlobusAuthorizationParameters(required_scopes=[TransferScopes.all])
... )
Please authenticate with Globus here:
-------------------------------------
https://auth.globus.org/v2/oauth2/authorize?cli...truncated...
-------------------------------------

Enter the resulting Authorization Code here:

.. autoclass:: CommandLineLoginFlowManager
:members:
:member-order: bysource
:show-inheritance:

Local Server
------------

A LocalServerLoginFlowManager drives more automated but less portable login flows
derek-globus marked this conversation as resolved.
Show resolved Hide resolved
compared with its command line counterpart. When run, rather than printing the
authorization URL, the manager will open it in the user's default browser. Alongside
this, the manager will start a local web server to receive the auth code upon completion
of the login flow.

This provides a more user-friendly login experience as there is no manually copy/pasting
of links and codes but also requires that the python process is running in an
derek-globus marked this conversation as resolved.
Show resolved Hide resolved
environment with access to a supported browser. This flow is not suitable for headless
environments (e.g., while ssh-ed into a cluster node).

.. warning::

Globus Auth requires that redirect URIs, including the local server used
here, be pre-registered with the client in use.

Before using this flow, navigate to the
`Globus Developers Pane <https://auth.globus.org/v2/web/developers>`_ and ensure
that `https://localhost` is listed as an allowed "Redirect URL" for your client.
derek-globus marked this conversation as resolved.
Show resolved Hide resolved


Example Usage:

.. code-block:: pycon

>>> from globus_sdk import NativeAppAuthClient
>>> from globus_sdk.scopes import TransferScopes
>>> from globus_sdk.login_flows import LocalServerLoginFlowManager

>>> login_client = NativeAppAuthClient(client_id=client_id)
>>> manager = LocalServerLoginFlowManager(login_client)
>>>
>>> token_response = manager.run_login_flow(
... GlobusAuthorizationParameters(required_scopes=[TransferScopes.all])
... )

.. autoclass:: LocalServerLoginFlowManager
:members:
:member-order: bysource
:show-inheritance:

.. autoexception:: LocalServerLoginError

.. autoexception:: LocalServerEnvironmentalLoginError
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ namespaces = false

[tool.setuptools.package-data]
globus_sdk = ["py.typed"]
"globus_sdk.experimental.login_flow_manager.local_server_login_flow_manager.html_files" = ["*.html"]
"globus_sdk.login_flows.local_server_login_flow_manager.html_files" = ["*.html"]

[tool.setuptools.dynamic.version]
attr = "globus_sdk.__version__"
Expand Down
2 changes: 1 addition & 1 deletion src/globus_sdk/experimental/globus_app/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

from globus_sdk import AuthLoginClient
from globus_sdk._types import UUIDLike
from globus_sdk.experimental.login_flow_manager import LoginFlowManager
from globus_sdk.experimental.tokenstorage import TokenStorage
from globus_sdk.login_flows import LoginFlowManager

if sys.version_info < (3, 8):
from typing_extensions import Protocol, runtime_checkable
Expand Down
10 changes: 5 additions & 5 deletions src/globus_sdk/experimental/globus_app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
import typing as t

from globus_sdk.config import get_environment_name
from globus_sdk.experimental.login_flow_manager import (
CommandLineLoginFlowManager,
LocalServerLoginFlowManager,
LoginFlowManager,
)
from globus_sdk.experimental.tokenstorage import (
JSONTokenStorage,
MemoryTokenStorage,
SQLiteTokenStorage,
TokenStorage,
)
from globus_sdk.login_flows import (
CommandLineLoginFlowManager,
LocalServerLoginFlowManager,
LoginFlowManager,
)

from ._types import (
LoginFlowManagerProvider,
Expand Down
5 changes: 1 addition & 4 deletions src/globus_sdk/experimental/globus_app/user_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,8 @@
NativeAppAuthClient,
)
from globus_sdk._types import ScopeCollectionType, UUIDLike
from globus_sdk.experimental.login_flow_manager import (
CommandLineLoginFlowManager,
LoginFlowManager,
)
from globus_sdk.gare import GlobusAuthorizationParameters
from globus_sdk.login_flows import CommandLineLoginFlowManager, LoginFlowManager

from ._types import LoginFlowManagerProvider
from .app import GlobusApp
Expand Down
37 changes: 37 additions & 0 deletions src/globus_sdk/experimental/login_flow_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from __future__ import annotations

import sys
import typing as t

__all__ = (
"CommandLineLoginFlowManager",
"LocalServerLoginFlowManager",
"LoginFlowManager",
)

# legacy aliases
# (when accessed, these will emit deprecation warnings)
if t.TYPE_CHECKING:
from globus_sdk.login_flows import (
CommandLineLoginFlowManager,
LocalServerLoginFlowManager,
LoginFlowManager,
)
else:

def __getattr__(name: str) -> t.Any:
import globus_sdk.login_flows as login_flows_module
from globus_sdk.exc import warn_deprecated

warn_deprecated(
"'globus_sdk.experimental.login_flow_manager' has been renamed to "
"'globus_sdk.login_flows'. "
f"Importing '{name}' from `globus_sdk.experimental` is deprecated. "
f"Use `globus_sdk.login_flows.{name}` instead."
)

value = getattr(login_flows_module, name, None)
if value is None:
raise AttributeError(f"module {__name__} has no attribute {name}")
setattr(sys.modules[__name__], name, value)
return value

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
from .command_line_login_flow_manager import CommandLineLoginFlowManager
from .local_server_login_flow_manager import LocalServerLoginFlowManager
from .local_server_login_flow_manager import (
LocalServerEnvironmentalLoginError,
LocalServerLoginError,
LocalServerLoginFlowManager,
)
from .login_flow_manager import LoginFlowManager

__all__ = [
"LoginFlowManager",
"CommandLineLoginFlowManager",
"LocalServerLoginError",
"LocalServerEnvironmentalLoginError",
"LocalServerLoginFlowManager",
"LoginFlowManager",
]
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,22 @@

class CommandLineLoginFlowManager(LoginFlowManager):
"""
A ``CommandLineLoginFlowManager`` is a ``LoginFlowManager`` that uses the command
line for interacting with the user during its interactive login flows.

Example usage:

>>> login_client = globus_sdk.NativeAppAuthClient(...)
>>> login_flow_manager = CommandLineLoginFlowManager(login_client)
>>> scopes = [globus_sdk.scopes.TransferScopes.all]
>>> auth_params = GlobusAuthorizationParameters(required_scopes=scopes)
>>> tokens = login_flow_manager.run_login_flow(auth_params)

A login flow manager which drives authorization-code token grants through the
command line.

:param AuthLoginClient login_client: The client that will be making Globus
Auth API calls required for a login flow.

.. note::
If this client is a :class:`globus_sdk.ConfidentialAppAuthClient`, an
explicit `redirect_uri` param is required.

:param str redirect_uri: The redirect URI to use for the login flow. When the
`login_client` is a native client, this defaults to a globus-hosted URI.
derek-globus marked this conversation as resolved.
Show resolved Hide resolved
:param bool request_refresh_tokens: A signal of whether refresh tokens are expected
to be requested, in addition to access tokens.
:param str native_prefill_named_grant: A string to prefill in a Native App login
flow. This value is only used if the `login_client` is a native client.
"""

def __init__(
Expand All @@ -40,18 +45,6 @@ def __init__(
request_refresh_tokens: bool = False,
native_prefill_named_grant: str | None = None,
) -> None:
"""
:param login_client: The ``AuthLoginClient`` that will be making the Globus
Auth API calls needed for the authentication flow. Note that this
must either be a NativeAppAuthClient or a templated
ConfidentialAppAuthClient, standard ConfidentialAppAuthClients cannot
use the web auth-code flow.
:param redirect_uri: The redirect URI to use for the login flow. Defaults to
a globus-hosted helper web auth-code URI for NativeAppAuthClients.
:param request_refresh_tokens: Control whether refresh tokens will be requested.
:param native_prefill_named_grant: The named grant label to prefill on the
consent page when using a NativeAppAuthClient.
"""
super().__init__(
login_client,
request_refresh_tokens=request_refresh_tokens,
Expand All @@ -73,15 +66,13 @@ def for_globus_app(
cls, app_name: str, login_client: AuthLoginClient, config: GlobusAppConfig
) -> CommandLineLoginFlowManager:
"""
Create a ``CommandLineLoginFlowManager`` for a given ``GlobusAppConfig``.

:param app_name: The name of the app to use for prefilling the named grant in
native auth flows.
:param login_client: The ``AuthLoginClient`` to use to drive Globus Auth flows.
:param config: A ``GlobusAppConfig`` to configure the login flow.
:returns: A ``CommandLineLoginFlowManager`` instance.
:raises: GlobusSDKUsageError if a login_redirect_uri is not set on the config
but a ConfidentialAppAuthClient is used.
Creates a ``CommandLineLoginFlowManager`` for use in a GlobusApp.
derek-globus marked this conversation as resolved.
Show resolved Hide resolved

:param app_name: The name of the app. Will be prefilled in native auth flows.
:param login_client: A client used to make Globus Auth API calls.
:param config: A GlobusApp-bounded object used to configure login flow manager.
:raises: GlobusSDKUsageError if login_redirect_uri is not set on the config
derek-globus marked this conversation as resolved.
Show resolved Hide resolved
but a ConfidentialAppAuthClient is supplied.
"""
return cls(
login_client,
Expand Down Expand Up @@ -110,6 +101,9 @@ def print_authorize_url(self, authorize_url: str) -> None:
"""
Prompt the user to authenticate using the provided ``authorize_url``.

This method is publicly exposed to allow for simpler customization through
subclassing and overriding.

derek-globus marked this conversation as resolved.
Show resolved Hide resolved
:param authorize_url: The URL at which the user will login and consent to
application accesses.
"""
Expand All @@ -128,6 +122,11 @@ def print_authorize_url(self, authorize_url: str) -> None:
def prompt_for_code(self) -> str:
"""
Prompt the user to enter an authorization code.

This method is publicly exposed to allow for simpler customization through
subclassing and overriding.

:returns str: The authorization code entered by the user.
derek-globus marked this conversation as resolved.
Show resolved Hide resolved
"""
code_prompt = "Enter the resulting Authorization Code here: "
return input(code_prompt).strip()
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from .errors import LocalServerEnvironmentalLoginError, LocalServerLoginError
from .local_server_login_flow_manager import LocalServerLoginFlowManager

__all__ = [
"LocalServerLoginError",
"LocalServerEnvironmentalLoginError",
"LocalServerLoginFlowManager",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class LocalServerLoginError(Exception):
"""An error raised during a LocalServerLoginFlowManager's run."""


class LocalServerEnvironmentalLoginError(LocalServerLoginError):
"""
Error raised when a local server login flow fails to start due to incompatible
environment conditions (e.g., a remote session or text-only browser).
"""
Loading