Auth in Gladier

By Default, Gladier will automatically initiate a login as needed. This behavior can be customized if Glaider needs to be used as part of a larger app.

Scopes

Gladier requires scopes for the following Services:

  • Globus Flows Service
    • https://auth.globus.org/scopes/eec9b274-0c81-4334-bdc2-54e90e689b9a/run

    • https://auth.globus.org/scopes/eec9b274-0c81-4334-bdc2-54e90e689b9a/run_manage

    • https://auth.globus.org/scopes/eec9b274-0c81-4334-bdc2-54e90e689b9a/view_flows

    • https://auth.globus.org/scopes/eec9b274-0c81-4334-bdc2-54e90e689b9a/run_status

    • https://auth.globus.org/scopes/eec9b274-0c81-4334-bdc2-54e90e689b9a/manage_flows

  • Globus Compute
    • openid

    • https://auth.globus.org/scopes/facd7ccc-c5f4-42aa-916b-a0e270e2c2a9/all

  • Deployed Flow
    • Scope varies per-flow

Note that the Deployed Flow will be unique for each Glaider Client, and the scope will not exist until the flow is deployed. This sometimes requires multiple logins with Globus, first to fetch the base flows service scopes to deploy the flow, then a second login to get tokens to run the newly deployed flow.

Note also, that dependent scopes underlying the deployed flow may also change if the flow is modified to add additional services. For example, a flow could be initially deployed to do a simple transfer task, then modified and run again but with an additional search ingest task. If this happens, a new login must take place in order for the modified flow to be run again.

Storage

By default, tokens in Gladier are stored in ~/.gladier-secrets.cfg

Customizing Auth

The default behavior of Auth in Gladier can be changed by passing a custom Login Manager into any Gladier Client:

from gladier import CallbackLoginManager

def callback(scopes: List[str]) -> Mapping[str, Union[AccessTokenAuthorizer, RefreshTokenAuthorizer]]:
    authorizers_by_scope = do_my_login(scopes)
    return authorizers_by_scope

callback_login_manager = CallbackLoginManager(
    # A dict of authorizers mapped by scope can be provided if available
    initial_authorizers,
    # If additional logins are needed, the callback is called.
    callback=callback
)

MyGladierClient(login_manager=callback_login_manager)

my_custom_login_function should be capable of both completeing a Globus Auth flow and storing tokens for future invacations. Ideally, initial_authorizers will contain all of the scopes needed so no login is needed.

Complete example

The complete example below uses Fair Research Login to demontrate a customized login flow. Please note, this example assumes customization using a Native App. Those using the authorization code grant, such as in a webservice, must modify their app accordingly.

from typing import List, Mapping, Union
from globus_sdk import AccessTokenAuthorizer, RefreshTokenAuthorizer
from fair_research_login import NativeClient, LoadError
from gladier import generate_flow_definition, GladierBaseClient, CallbackLoginManager


# A simple shell tool will be used for demonstration
@generate_flow_definition
class GladierTestClient(GladierBaseClient):
    gladier_tools = [
        "gladier_tools.posix.shell_cmd.ShellCmdTool",
    ]


# Fair Research Login is used for simplicity
frl = NativeClient(client_id="7414f0b4-7d05-4bb6-bb00-076fa3f17cf5")

try:
    # Try to use a previous login to avoid a new login flow
    initial_authorizers = frl.get_authorizers_by_scope()
except LoadError:
    # Passing in an empty dict will trigger the callback below
    initial_authorizers = {}


def callback(
    scopes: List[str],
) -> Mapping[str, Union[AccessTokenAuthorizer, RefreshTokenAuthorizer]]:
    # 'force' is used for any underlying scope changes. For example, if a flow adds transfer
    # functionality since it was last run, running it again would require a re-login.
    frl.login(requested_scopes=scopes, force=True, refresh_tokens=True)
    return frl.get_authorizers_by_scope()


custom_login_manager = CallbackLoginManager(
    initial_authorizers,
    # If additional logins are needed, the callback is used.
    callback=callback,
)

# Pass in any custom login manager to modify the behavior. Everything else stays the same.
client = GladierTestClient(login_manager=custom_login_manager)
run = client.run_flow(
    flow_input={
        "input": {
            "args": "echo 'Hello Custom Login!'",
            "capture_output": True,
            "compute_endpoint": "4b116d3c-1703-4f8f-9f6f-39921e5864df",
        }
    }
)

run_id = run["run_id"]
client.progress(run_id)
print(f"Flow result: {client.get_status(run_id)['status']}")