.. _using_globus_app: .. py:currentmodule:: globus_sdk Using a GlobusApp ================= Programmatic communication with Globus services relies on the authorization of requests. Management and resolution of this authorization can become an arduous task, especially when a script needs to interact with different services each carrying an individual set of complex authentication and authorization requirements. To assist with this task, this library provides a utility construct called a GlobusApp. A :py:class:`~GlobusApp` is a distinct object which will manage Globus Auth requirements (i.e., **scopes**) identified by their associated service (i.e., **resource server**). In addition to storing them, a GlobusApp provides a mechanism to resolve unmet requirements through browser- and API-based authorization flows, supplying the resulting tokens to bound clients as requested. There are two flavors of GlobusApp: * :py:class:`~UserApp`, a GlobusApp for interactions between an end user and Globus services. Operations are performed as a *user identity*. * :py:class:`~ClientApp`, a GlobusApp for interactions between a client (i.e. service account) and Globus services. Operations are performed as a *client identity*. Setup ----- A GlobusApp is a heavily configurable object. For common scripting usage however, instantiation only requires two parameters: #. **App Name** - A human readable name to identify your app in HTTP requests and token caching (e.g., "My Cool Weathervane"). #. **Client Info** - either a *Native Client's* ID or a *Confidential Client's* ID and secret pair. * There are important distinctions to consider when choosing your client type; see `Developing an Application Using Globus Auth `_. A simplified heuristic to help choose the client type however is: * Use a *Confidential Client* when your client needs to own cloud resources itself and will be used in a trusted environment where you can securely hold a secret. * Use a *Native Client* when your client will be facilitating interactions between a user and a service, particularly if it is bundled within a script or cli tool to be distributed to end-users' machines. .. Note:: Both UserApps and ClientApps require a client. In a UserApp, the client sends requests representing a user identity; whereas in a ClientApp, the requests represent the client identity itself. Once instantiated, a GlobusApp can be passed to any service client using the init ``app`` keyword argument (e.g. ``TransferClient(app=my_app)``). Doing this will bind the app to the client, registering a default set of scopes requirements for the service client's resource server and configuring the app as the service client's auth provider. .. tab-set:: .. tab-item:: UserApp Construct a UserApp then bind it to a Transfer client and a Flows client. .. Note:: ``UserApp.__init__(...)`` currently only supports Native clients. Confidential client support is forthcoming. .. code-block:: python import globus_sdk CLIENT_ID = "..." my_app = globus_sdk.UserApp("my-user-app", client_id=CLIENT_ID) transfer_client = globus_sdk.TransferClient(app=my_app) flows_client = globus_sdk.FlowsClient(app=my_app) .. tab-item:: ClientApp Construct a ClientApp, then bind it to a Transfer client and a Flows client. .. Note:: ``ClientApp.__init__(...)`` requires the `client_secret` keyword argument. Native clients, which lack secrets, are not allowed. .. code-block:: python import globus_sdk CLIENT_ID = "..." CLIENT_SECRET = "..." my_app = globus_sdk.ClientApp( "my-client-app", client_id=CLIENT_ID, client_secret=CLIENT_SECRET ) transfer_client = globus_sdk.TransferClient(app=my_app) flows_client = globus_sdk.FlowsClient(app=my_app) Usage ----- From this point, the app manages scope validation, token caching and routing for any bound clients. In the above example, listing a client's or user's flows becomes as simple as: .. code-block:: python flows = flows_client.list_flows()["flows"] If cached tokens are missing, expired, or otherwise insufficient (e.g., the first time you run the script), the app will automatically initiate an auth flow to acquire new tokens. With a UserApp, the app will print a URL to the terminal with a prompt instructing a the user to follow the link and enter the code they're given back into the terminal. With a ClientApp, the app will retrieve tokens programmatically through a Globus Auth API. Once this auth flow has finished, the app will cache tokens for future use and invocation of your requested method will proceed as expected. Manually Running Login Flows ---------------------------- While your app will automatically initiate and oversee login flows when needed, sometimes an author may want to explicitly control when an authorization occurs. To manually trigger a login flow, call ``GlobusApp.login(...)``. The app will evaluate the current scope requirements against available tokens, initiating a login flow if it determines that any requirements across any resource servers are unmet. Resulting tokens will be cached for future use. This method accepts two optional keyword args: - ``auth_params``, a collection of additional auth parameters to customize the login. This allows for specifications such as requiring that a user be logged in with an MFA token or rendering the authorization webpage with a specific message. - ``force``, a boolean flag instructing the app to perform a login flow regardless of whether it is required. .. code-block:: python from globus_sdk.gare import GlobusAuthorizationParameters ... my_app.login( auth_params=GlobusAuthorizationParameters( session_message="Please authenticate with MFA", session_required_mfa=True, ) ) Manually Defining Scope Requirements ------------------------------------ Globus service client classes all maintain an internal list of default scope requirements to be attached to any bound app. These scopes represent an approximation of a "standard set" for each service. This list however is not sufficient for all use cases. For example, the FlowsClient defines its default scopes as ``flows:view_flows`` and ``flows:run_status`` (read-only access). These scopes will not be sufficient for a script which needs to create new flows or modify existing ones. For that script, the author must manually attach the ``flows:manage_flows`` scope to the app. This can be done in one of two ways: #. Through a service client initialization, using the ``app_scopes`` kwarg. .. code-block:: python from globus_sdk import Scope, FlowsClient FlowsClient(app=my_app, app_scopes=[Scope(FlowsClient.scopes.manage_flows)]) This approach results in an app which only requires the ``flows:manage_flows`` scope. The default scopes (``flows:view_flows`` and ``flows:run_status``) are not registered. #. Through a service client's ``add_app_scope`` method. .. code-block:: python from globus_sdk import FlowsClient flows_client = FlowsClient(app=my_app) flows_client.add_app_scope(FlowsClient.scopes.manage_flows) This approach will add the ``flows:manage_flows`` scope to the app's existing set of scopes. Since ``app_scopes`` was omitted in the client initialization, the default scopes are registered as well.