diff --git a/docs/user_guide/usage_patterns/index.rst b/docs/user_guide/usage_patterns/index.rst index 77bf590f4..792e1c3fe 100644 --- a/docs/user_guide/usage_patterns/index.rst +++ b/docs/user_guide/usage_patterns/index.rst @@ -29,3 +29,4 @@ transfers. data_transfer/index sessions_and_consents/index + service_accounts/index diff --git a/docs/user_guide/usage_patterns/service_accounts/index.rst b/docs/user_guide/usage_patterns/service_accounts/index.rst new file mode 100644 index 000000000..c82384307 --- /dev/null +++ b/docs/user_guide/usage_patterns/service_accounts/index.rst @@ -0,0 +1,17 @@ +.. _userguide_service_accounts: + +Service Accounts +================ + +"Service Accounts" or "Client Identities" (the terms are synonymous) are +automated users which authenticate with Globus via client credentials. +Using this type of credential allows for automated usage of Globus services +which does not rely on user interaction or login flows. + +.. toctree:: + :caption: Using Service Accounts with the SDK + :maxdepth: 1 + + understanding_service_accounts + registering_a_service_account + using_client_app/index diff --git a/docs/user_guide/usage_patterns/service_accounts/registering_a_service_account.rst b/docs/user_guide/usage_patterns/service_accounts/registering_a_service_account.rst new file mode 100644 index 000000000..5faf5ae9c --- /dev/null +++ b/docs/user_guide/usage_patterns/service_accounts/registering_a_service_account.rst @@ -0,0 +1,80 @@ +.. _userguide_register_service_account: + +Registering a Service Account +============================= + +In order to be used as a Service Account or "Client Identity", a client must +be registered in Globus Auth under the correct type. + +This is similar to the +:ref:`getting started documentation on registering an app `, +but using some different settings. + +Creating the Client +------------------- + +The following steps will walk you through how to register a client for use as a +service account. +One topic not covered here is how to securely store and manage your secrets -- +the guidance below will simply say "save", and it is the user's responsibility +to decide how to save and secure these credentials. + +1. Navigate to the `Developer Site `_ + +2. Select "Register a service account or application credential for automation" + +3. Create or Select a Project + + * A project is a collection of apps with a shared list of administrators. + * If you don't own any projects, you will automatically be prompted to create one. + * If you do, you will be prompted to either select an existing or create a new one. + +4. Creating or selecting a project will prompt you for another login, sign in with an + account that administers your project. + +5. Give your App a name. This will appear as the identity's "name" where a + user's full name might appear. + +6. Click "Register App". This will create your app and take you to a page + describing it. + +7. Copy the "Client UUID" and save it -- this is your ``client_id`` in the + Python SDK's terms. + +8. Click "Add Client Secret", fill in the label, and save the secret -- this is + your ``client_secret`` in the Python SDK's terms. + + +Saving and Retrieving Client IDs and Secrets +-------------------------------------------- + +The Globus SDK does not offer special capabilities for storage and retrieval of +client IDs and client secrets. + +In examples in SDK documentation, you will see the client ID and secret written +as hardcoded constants, e.g. + +.. code-block:: python + + import globus_sdk + + CLIENT_ID = "YOUR ID HERE" + CLIENT_SECRET = "YOUR SECRET HERE" + + client = globus_sdk.ConfidentialAppAuthClient(CLIENT_ID, CLIENT_SECRET) + +You can replace those variables with sources of your choice. +For example, you could make them environment variables: + +.. code-block:: python + + import os + + CLIENT_ID = os.getenv("CLIENT_ID") + CLIENT_SECRET = os.getenv("CLIENT_SECRET") + + if not (CLIENT_ID and CLIENT_SECRET): + raise RuntimeError("CLIENT_ID and CLIENT_SECRET must both be set.") + +Selecting an appropriate storage and retrieval mechanism for client credentials +is considered a user responsibility by the SDK. diff --git a/docs/user_guide/usage_patterns/service_accounts/understanding_service_accounts.rst b/docs/user_guide/usage_patterns/service_accounts/understanding_service_accounts.rst new file mode 100644 index 000000000..7be3f2a2e --- /dev/null +++ b/docs/user_guide/usage_patterns/service_accounts/understanding_service_accounts.rst @@ -0,0 +1,67 @@ +.. _userguide_understanding_service_accounts: + +Understanding Service Accounts +============================== + +Some clients registered in Globus may operate as independent "service accounts", +distinct actors with their own identities. +These are also referred to as "client identities" because the actor in question +is the client itself. +Service accounts useful for a wide range of automation tasks, in which users +want to leverage Globus APIs, but without handling login flows and user +credentials. + +Service Accounts are Clients +---------------------------- + +To create a service account, users must register a new Globus Auth client as +a Service Account, capable of performing a *client credentials grant* to get +its tokens. +Once it is so registered, such a client will have an ID and secret, which can be +passed into interfaces in the SDK. + +The client credentials (ID and secret) are used to get tokens to power +interactions with Globus APIs, and SDK's :class:`globus_sdk.GlobusApp` and +:class:`globus_sdk.ClientCredentialsAuthorizer` will automatically cache and +reload these tokens appropriately. + +.. note:: + + In order to be used as a Service Account, a client must have an ID and secret. + However, not every client with an ID and secret supports this usage! Clients + may be registered with various different OAuth2 grant types enabled, and some + clients have an ID and secret but are used to authenticate user logins! + + Put another way: having client credentials is *necessary but not sufficient* + for this kind of usage. + + +Disambiguation: "Clients" vs "Client Identities" vs "Service Accounts" +---------------------------------------------------------------------- + +There are two different meanings for the word "client", from different domains. + +Conventionally, a library's adapter for a web API is a "client". +In the SDK, we primarily use the word "client" to refer to these API connectors, +as in ``TransferClient``, ``GroupsClient``, etc. + +OAuth2 calls an application registered with the service a "client". +A "Client Identity" is a Globus Auth concept which uses this meaning of +"client". + +As a result of this ambiguity, this documentation will prefer to refer to +"Client Identities" as "Service Accounts", which is the term which is used in +other Globus documentation and the web interface. + + +Service Account Permissions +--------------------------- + +Service Accounts have their own assigned identities, group memberships, and +permissions within Globus. +They are not implicitly linked in any way to the user who created them, or the +administrators who manage them. +Their permissions are isolated. + +Users of Service Accounts need to separately assign permissions to these +identities, depending on what resources the client is meant to access. diff --git a/docs/user_guide/usage_patterns/service_accounts/using_client_app/client_app_ls.py b/docs/user_guide/usage_patterns/service_accounts/using_client_app/client_app_ls.py new file mode 100644 index 000000000..a2766d864 --- /dev/null +++ b/docs/user_guide/usage_patterns/service_accounts/using_client_app/client_app_ls.py @@ -0,0 +1,27 @@ +import globus_sdk + +# your client credentials +CLIENT_ID = "YOUR ID HERE" +CLIENT_SECRET = "YOUR SECRET HERE" + +# the ID of "tutorial collection 1" +TUTORIAL_COLLECTION = "6c54cade-bde5-45c1-bdea-f4bd71dba2cc" + +# create a ClientApp named "app" +with globus_sdk.ClientApp( + "sample-app", + client_id=CLIENT_ID, + client_secret=CLIENT_SECRET, + config=globus_sdk.GlobusAppConfig(token_storage="memory"), +) as app: + # create a TransferClient named "tc", bound to "app" + with globus_sdk.TransferClient(app=app) as tc: + + # because the tutorial collection is of a type which uses a `data_access` + # scope for fine grained access control, the `data_access` requirement needs + # to be registered with the app + tc.add_app_data_access_scope(TUTORIAL_COLLECTION) + + # iterate over the listing results, printing each filename + for entry in tc.operation_ls(TUTORIAL_COLLECTION, path="/home/share/godata"): + print(entry["name"]) diff --git a/docs/user_guide/usage_patterns/service_accounts/using_client_app/index.rst b/docs/user_guide/usage_patterns/service_accounts/using_client_app/index.rst new file mode 100644 index 000000000..34f7755fb --- /dev/null +++ b/docs/user_guide/usage_patterns/service_accounts/using_client_app/index.rst @@ -0,0 +1,97 @@ +.. _userguide_using_client_app: + +Using a ClientApp with a Service Account +======================================== + +Once you have a client ID and secret from an app registration for a service +account, the SDK has a few tools which can use those credentials to acquire +tokens, to talk to various Globus Services. + +The easiest tool for this job is a ``ClientApp``, a flavor of ``GlobusApp`` +designed to work with service accounts. + +Instantiating a ClientApp +------------------------- + +Constructing an app takes three required parameters, + +- a human readable name to identify your app in HTTP requests and token caching (e.g., "My Cool Weathervane"). + - this does not need match the name you supplied during client registration. +- the client ID +- the client secret + +as in: + +.. code-block:: python + + import globus_sdk + + CLIENT_ID = "YOUR ID HERE" + CLIENT_SECRET = "YOUR SECRET HERE" + + app = globus_sdk.ClientApp( + "sample-app", + client_id=CLIENT_ID, + client_secret=CLIENT_SECRET, + ) + +Using the App with a Globus Service +----------------------------------- + +The resulting app can then be passed to any SDK client class to create an API +client object which uses the app for authentication requirements. +For example, to use the ``app`` object to run an ``ls`` on one of the tutorial +collections: + +.. code-block:: python + + TUTORIAL_COLLECTION = "6c54cade-bde5-45c1-bdea-f4bd71dba2cc" + + with globus_sdk.TransferClient(app=app) as tc: + tc.add_app_data_access_scope(TUTORIAL_COLLECTION) + ls_result = tc.operation_ls(TUTORIAL_COLLECTION, path="/home/share/godata") + +.. note:: + + Unfortunately, there are two different meanings of the word "client" in use + in this example! + + A ``TransferClient`` is a "client" in the sense that it is a local object + which provides access to the Globus Transfer Service. + The ``CLIENT_ID``, ``CLIENT_SECRET``, and ``ClientApp`` are all using the + word "client" in reference to the OAuth2 standard's definition of a "client" + as a registered app, a different meaning for the same word. + +Using Memory Storage +-------------------- + +Unlike user logins, client credentials can't be "logged out" vs "logged in" -- +unless they are deleted via the Globus Auth service, they are always active. + +As a result, unlike applications which provide user logins, ``ClientApp``\s will +very often prefer to store any tokens they are using in memory. The tokens will +be cached and reused over the lifetime of the process, but never persisted to +disk. + +To configure an app in this way, simply add a ``config`` to the app +initialization to select the ``"memory"`` storage type: + +.. code-block:: python + + app = globus_sdk.ClientApp( + "sample-app", + client_id=CLIENT_ID, + client_secret=CLIENT_SECRET, + config=globus_sdk.GlobusAppConfig(token_storage="memory"), + ) + +Complete Example +---------------- + +In addition to leveraging all of the elements described above, this example enhances the code sample to +use the ``ClientApp``'s context manager interface to close token storage. +We also add a loop of ``print()`` usages on the ``ls`` result to show some output: + +.. literalinclude:: client_app_ls.py + :caption: ``client_app_ls.py`` [:download:`download `] + :language: python