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']}")