Tutorial#

First Steps#

This is a tutorial in the use of the Globus SDK. It takes you through a simple step-by-step flow for registering your application, getting tokens, and using them with our service.

These are the steps we will take:

  1. Create a Client

  2. Login and get tokens!

  3. Use tokens to access the service

  4. Explore the OAuthTokenResponse

  5. Do a login flow with Refresh Tokens

  6. Selected Examples

That should be enough to get you up and started.

Background on OAuth2 Clients#

Globus uses OAuth2 to handle authentication. In order to login, your application must be registered with Globus Auth. This is called a “client” in OAuth2, but Globus will also sometimes call this an “app”.

If you plan to create your own application, you should create a new client by following the instructions below. However, just for the purposes of this tutorial, we have created a tutorial client which you may use.

Step 1: Create a Client#

Note

You can skip this section and jump right in by using the CLIENT_ID seen in the example code blocks below! That is the ID of the tutorial client, which lets you get started quickly and easily. Come back and create a client of your own when you’re ready!

In order to complete an OAuth2 flow to get tokens, you must have a client or “app” definition registered with Globus.

The following steps will walk you through creating a Native App to be used as your Globus interaction client.

  1. Navigate to the Developer Site

  2. Select “Register a thick client or script that will be installed and run by users on their devices.”

  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 is what users will see when they are asked to authorize your app.

  6. Click “Register App”. This will create your app and take you to a page describing it.

  7. Copy the “Client UUID” from the page.

    • This ID can be thought of as your App’s “username”. It is non-secure information and as such, feel free to hardcode it into scripts.

In the rest of the tutorial we will assume in all code samples that the Client UUID is available in the variable CLIENT_ID.

Step 2: Login and get tokens!#

Talking to Globus Services as a user requires that you authenticate to your new App and get it Tokens, credentials proving that you logged into it and gave it permission to access the service.

No need to worry about creating your own login pages and such – for this type of app, Globus provides all of that for you. Run the following code sample to get your Access Tokens:

import globus_sdk

# this is the tutorial client ID
# replace this string with your ID for production use
CLIENT_ID = "61338d24-54d5-408f-a10d-66c06b59f6d2"
client = globus_sdk.NativeAppAuthClient(CLIENT_ID)

client.oauth2_start_flow()
authorize_url = client.oauth2_get_authorize_url()
print(f"Please go to this URL and login:\n\n{authorize_url}\n")

auth_code = input("Please enter the code you get after login here: ").strip()
token_response = client.oauth2_exchange_code_for_tokens(auth_code)

globus_auth_data = token_response.by_resource_server["auth.globus.org"]
globus_transfer_data = token_response.by_resource_server["transfer.api.globus.org"]

# most specifically, you want these tokens as strings
AUTH_TOKEN = globus_auth_data["access_token"]
TRANSFER_TOKEN = globus_transfer_data["access_token"]

The Globus SDK offers several features for managing credentials. The following components are useful for further reading:

These are covered by several of the available Examples as well.

Step 3: Use tokens to access the service#

Continuing from the example above, you have two credentials to Globus Services on hand: the AUTH_TOKEN and the TRANSFER_TOKEN.

We’ll focus on the TRANSFER_TOKEN for now. It’s used to access the Transfer service.

# a GlobusAuthorizer is an auxiliary object we use to wrap the token. In
# more advanced scenarios, other types of GlobusAuthorizers give us
# expressive power
authorizer = globus_sdk.AccessTokenAuthorizer(TRANSFER_TOKEN)
tc = globus_sdk.TransferClient(authorizer=authorizer)

# high level interface; provides iterators for list responses
print("My Endpoints:")
for ep in tc.endpoint_search(filter_scope="my-endpoints"):
    print("[{}] {}".format(ep["id"], ep["display_name"]))

Note that the TRANSFER_TOKEN is only valid for a limited time. You’ll have to login again when it expires.

Advanced Tutorial#

In the first steps of the Tutorial, we did a login flow to get an Access Token, and used it. However, we didn’t explain what that token is and how it works.

In this section, not only will we talk through more detail on Access Tokens, but we’ll also explore more advanced use cases and their near-cousins, Refresh Tokens.

Step 4: Explore the OAuthTokenResponse#

In the basic tutorial, we extracted an access token with these steps:

token_response = client.oauth2_exchange_code_for_tokens(auth_code)
globus_transfer_data = token_response.by_resource_server["transfer.api.globus.org"]
TRANSFER_TOKEN = globus_transfer_data["access_token"]

It’s worth looking closer at the token response itself, as it is of particular interest.

This is the ultimate product of the login flow, and it contains the credentials resulting from login.

To recap, the whole flow can be done like so:

CLIENT_ID = "61338d24-54d5-408f-a10d-66c06b59f6d2"
client = globus_sdk.NativeAppAuthClient(CLIENT_ID)

client.oauth2_start_flow()
authorize_url = client.oauth2_get_authorize_url()
print(f"Please go to this URL and login:\n\n{authorize_url}\n")

auth_code = input("Please enter the code here: ").strip()
token_response = client.oauth2_exchange_code_for_tokens(auth_code)

Though it has a few attributes and methods, by far the most important thing about token_response to understand is token_response.by_resource_server.

Let’s take a look at str(token_response.by_resource_server):

>>> str(token_response.by_resource_server)
{
  "auth.globus.org": {
    "access_token": "AQBX8YvVAAAAAAADxhAtF46RxjcFuoxN1oSOmEk-hBqvOejY4imMbZlC0B8THfoFuOK9rshN6TV7I0uwf0hb",
    "scope": "openid email profile",
    "token_type": "Bearer",
    "expires_at_seconds": 1476121216,
    "refresh_token": None
  },
  "transfer.api.globus.org": {
    "access_token": "AQBX8YvVAAAAAAADxg-u9uULMyTkLw4_15ReO_f2E056wLqjAWeLP51pgakLxYmyUDfGTd4SnYCiRjFq3mnj",
    "scope": "urn:globus:auth:scope:transfer.api.globus.org:all",
    "token_type": "Bearer",
    "expires_at_seconds": 1476121286,
    "refresh_token": None
  }
}

The keys in the token response, "auth.globus.org" and "transfer.api.globus.org", are the services which require tokens. These are the Resource Servers in the response, and for each one, the response contains the following info:

  • access_token: a credential which authenticates access to the Resource Server

  • scope: a list of activities for which the access_token grants permissions

  • token_type: the kind of authorization for which the token is used. All Globus tokens are sent as Bearer Auth headers

  • expires_at_seconds: a POSIX timestamp for the time when the access_token expires

  • refresh_token: a credential which can be used to replace or “refresh” the access_token when it expires. None unless explicitly requested. Details on refresh_token are in the next section

Note

The keys into by_resource_server are the registered resource_server value for the service.

For Globus hosted services like Globus Auth and Globus Transfer, the resource_server is the hostname for the service, and can be retrieved via the resource_server class attribute for the relevant client. e.g., globus_sdk.TransferClient.resource_server.

For other services, including Globus Connect Server v5, the resource_server value will be the ID of the service client. For Globus Connect Server v5, this is the ID of the Endpoint.

Step 5: Do a login flow with Refresh Tokens#

As described above, there is enough code to do a login flow and get an Access Token. However, that token will expire after a short duration, after which the user will need to login again.

This can be avoided by requesting a Refresh Token, which is valid indefinitely (unless revoked). The purpose of Refresh Tokens is to allow an application to replace its Access Tokens without a fresh login.

The code above can easily include Refresh Tokens by modifying the call to oauth2_start_flow as follows:

CLIENT_ID = "61338d24-54d5-408f-a10d-66c06b59f6d2"
client = globus_sdk.NativeAppAuthClient(CLIENT_ID)

client.oauth2_start_flow(refresh_tokens=True)
authorize_url = client.oauth2_get_authorize_url()
print(f"Please go to this URL and login:\n\n{authorize_url}\n")

auth_code = input("Please enter the code here: ").strip()
token_response = client.oauth2_exchange_code_for_tokens(auth_code)

If you peek at the token_response now, you’ll see that the "refresh_token" fields are no longer nulled.

However, this only solves half of the problem. When should a new Access Token be requested? The Globus SDK solves this problem for you with the GlobusAuthorizer objects introduced above. The key is the RefreshTokenAuthorizer object, which handles refreshes.

Let’s assume you want to do this with the TransferClient.

# get credentials for the Globus Transfer service
globus_transfer_data = token_response.by_resource_server["transfer.api.globus.org"]
# the refresh token and access token are often abbreviated as RT and AT
transfer_rt = globus_transfer_data["refresh_token"]
transfer_at = globus_transfer_data["access_token"]
expires_at_s = globus_transfer_data["expires_at_seconds"]

# construct a RefreshTokenAuthorizer
# note that `client` is passed to it, to allow it to do the refreshes
authorizer = globus_sdk.RefreshTokenAuthorizer(
    transfer_rt, client, access_token=transfer_at, expires_at=expires_at_s
)

# and try using `tc` to make TransferClient calls. Everything should just
# work -- for days and days, months and months, even years
tc = globus_sdk.TransferClient(authorizer=authorizer)

With the above code, tc is a TransferClient which can authenticate indefinitely, refreshing the Access Token whenever it expires.

Step 6: Selected Examples#

This example builds upon everything documented above. It will also include the use of new features not covered by this tutorial. In particular, it will use the scopes module to provide scope strings as constants, TransferData as a helper to construct a transfer task document, and the requested_scopes argument to oauth2_start_flow (instead of the default scopes).

Like the Minimal File Transfer Script, this example builds upon the tutorial, specifying scopes. It demonstrates some simple output processing as well.