Sites, Tenancy and Authentication

Sites

Tapis supports geographically distributed deployments where different components are running in different data centers and managed by different institutions. These physically isolated installations of Tapis software are referred to as sites. There is a single primary site and zero or more associate sites within a Tapis installation.

Primary Site

The primary site in a Tapis installation runs a complete set of Tapis API services and all associated 3rd-party services, such as databases and message queues. The creation of new sites is coordinated through the primary site, and the primary site runs the unique instance of the Sites and Tenants API (see Tenants below) which maintain the complete registry of all sites and tenants in the installation.

The primary site of the main Tapis installation is hosted at the Texas Advanced Computing Center at the tapis.io domain.

Associate Sites

Associate sites are required to run the Tapis Security Kernel, a compliant Token Generator API, and one or more additional Tapis services. Each associate site is managed and operated by a separate, partner institution. For Tapis services not run at the associate site, the corresponding service at the primary site is used for requests. In this way, partner institutions can choose which Tapis services to run within their institution and leverage the primary site deployment for the rest.

Deployment

The official Tapis deployment tooling targets the Kubernetes container orchestration platform. The project maintains a set of deployment templates which can be used in conjunction with configuration files to deploy Tapis services. If your institution is interested in becomming a Tapis associate site please contact us.

Details about the current list of sites is available from the tenants API. For example, one can retrieve the full list of sites as follows:

With PySDK:

>>> t.tenants.list_sites()

With CURL:

$ curl https://admin.tapis.io/v3/sites

The response will look similar to the following (the response below is truncated for brevity):

[
 base_url: https://tapis.io
 primary: True
 services: ['systems', 'files', 'security', 'tokens', 'streams', 'authenticator', 'meta', 'actors']
 site_admin_tenant_id: admin
 site_id: tacc
 tenant_base_url_template: https://${tenant_id}.tapis.io]
 .  .  .
]

Each site has a site_id as well as a list of Tapis services it provides and the tenant ID of the administrative tenant (admin_tenant) associated with it.

Tenants

Tapis is a multi-tenant platform, meaning that different projects (or tenants) can have logically isolated views of the Tapis objects (i.e., the systems, files, actors, etc.) they create for their project.

Each tenant is made up of the following:

  1. A base URL with which to access the tenant; by default, the base URL takes the form https://<tenant_id>.tapis.io where tenant_id is a short, unique identifier for the tenant in the Tapis system. For example, https://tacc.tapis.io is the base URL for the tacc tenant.

  2. An authenticator providing the rules for who can authenticate in the tenant.

Additionally, each tenant is “managed” by a site.

To see the current list of tenants registered with Tapis, we can use the tenants API.

With PySDK:

>>> t.tenants.list_tenants()

With CURL:

$ curl https://tacc.tapis.io/v3/tenants

The response will look similar to the following (the response below is truncated for brevity):

allowable_x_tenant_ids: ['tacc']
authenticator: https://tacc.tapis.io/v3/oauth2
base_url: https://tacc.tapis.io
create_time: Thu, 02 Jul 2020 23:45:16 GMT
description: Production tenant for all TACC users.
is_owned_by_associate_site: False
last_update_time: Thu, 02 Jul 2020 23:45:16 GMT
owner: CICSupport@tacc.utexas.edu
public_key: -----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz7rr5CsFM7rHMFs7uKIdcczn0uL4ebRMvH8pihrg1tW/fp5Q+5ktltoBTfIaVDrXGF4DiCuzLsuvTG5fGElKEPPcpNqaCzD8Y1v9r3tfkoPT3Bd5KbF9f6eIwrGERMTs1kv7665pliwehz91nAB9DMqqSyjyKY3tpSIaPKzJKUMsKJjPi9QAS167ylEBlr5PECG4slWLDAtSizoiA3fZ7fpngfNr4H6b2iQwRtPEV/EnSg1N3Oj1x8ktJPwbReKprHGiEDlqdyT6j58l/I+9ihR6ettkMVCq7Ho/bsIrwm5gP0PjJRvaD5Flsze7P4gQT37D1c5nbLR+K6/T0QTiyQIDAQAB
-----END PUBLIC KEY-----
security_kernel: https://tacc.tapis.io/v3/security
service_ldap_connection_id: None
tenant_id: tacc
token_service: https://tacc.tapis.io/v3/tokens
user_ldap_connection_id: tacc-all,

allowable_x_tenant_ids: ['dev']
authenticator: https://dev.tapis.io/v3/oauth2
base_url: https://dev.tapis.io
create_time: Fri, 19 Jun 2020 20:36:38 GMT
description: The dev tenant.
is_owned_by_associate_site: False
last_update_time: Fri, 19 Jun 2020 20:36:38 GMT
owner: CICSupport@tacc.utexas.edu
public_key: -----BEGIN PUBLIC KEY-----

. . .

Here we see the first two tenants registered in the Tapis framework, the tacc and dev tenants.

In general, the rules for authentication vary from tenant to tenant within Tapis. For example, in the tacc tenant, any user with a valid TACC account can authenticate and use the APIs. The dev tenant is a development sandbox with test accounts used by the core Tapis team.

This documentation focuses on the tacc tenant; however, much of what follows in the subsequent sections will be the same regardless of the tenant you are using.

Authentication

The default authenticator provided by the Tapis project is based on OAuth2, and this is the authentication mechanism in place for the tacc tenant. The OAuth2-based authentication services are available via the /v3/oauth2 endpoints.

OAuth uses different grant type flows for generating tokens in different situations. We do not provide a comprehensive guide to OAuth2; for that, we refer the reader to the OAuth2 docs. Instead, we provide a guide to the two most common use cases for users: generating tokens for themselves using the password grant flow, and generating tokens on behalf of others in a web application using the authorization code grant flow.

In the PySDK examples that follow, we will tacitly assume the tapipy.tapis.Tapis object has been instantiated as the Python object t. There are several options in the Tapis constructor, but the basic options include base_url and username, for example:

>>> t = Tapis(base_url='https://tacc.tapis.io', username='jdoe')

Password Grant - Generating a Token For Yourself

The simplest case is that you want to generate a Tapis OAuth token for yourself; to do this you can use the password grant flow, providing your username and password.

Tapis v3 tries to make this process as easy as possible by providing a simplified version of the password grant flow that does not require an OAuth client (see the OAuth Clients section).

With PySDK:

>>> t = Tapis(base_url='https://tacc.tapis.io', username='apitest', password='abcd123')
>>> t.get_tokens()

With CURL:

> curl -H "Content-type: application/json" -d '{"username": "apitest", "password": "abcde123", "grant_type": "password" }' \
https://tacc.tapis.io/v3/oauth2/tokens

In the PySDK, the access token is a first-class Python object stored on the Tapis object (the t in the examples above). We can inspect it

>>> t.access_token

access_token: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJqdGkiOiJmN2I5YjE5ZS02MDk5LTRmODItYTcyMi1iNjEwYzVkMGJhMGMiLCJpc3MiOiJodHRwczovL3RhY2MudGFwaXMuaW8vdjMvdG9rZW5zIiwic3ViIjoiYXBpdGVzdEB0YWNjIiwidGFwaXMvdGVuYW50X2lkIjoidGFjYyIsInRhcGlzL3Rva2VuX3R5cGUiOiJhY2Nlc3MiLCJ0YXBpcy9kZWxlZ2F0aW9uIjpmYWxzZSwidGFwaXMvZGVsZWdhdGlvbl9zdWIiOm51bGwsInRhcGlzL3VzZXJuYW1lIjoiYXBpdGVzdCIsInRhcGlzL2FjY291bnRfdHlwZSI6InVzZXIiLCJleHAiOjE1OTUwOTk0NTYsInRhcGlzL2NsaWVudF9pZCI6bnVsbCwidGFwaXMvZ3JhbnRfdHlwZSI6InBhc3N3b3JkIn0.alC8rRM-zNsHKcUiz3-tOJPaYtFksKb4Bit_aFE1HH_X_znnP2QkJaqc-xaRoMlQu26MN72TlJE0siIN3T38xXWBGDumHUYbvnNzT-7lk7AQU5MHSyCWx8IRDmTSbqmWOG8WBMCIV9Dh84mDd-X6eLJQ_cz1QqMAiI_cPgA9VVE22qDK3Lbz2pp9t0sm-l9XjE5y5Im8Y0B2p0ssPD0TjW20C5yngZ4-4jowDafboKlscog9ko-adrsVIjG_r-ccCUX3r8SVwQLypZFZAPKqbVzl8jt_mCi30W8AYwiaYGmH7INBbHI9hO7kwJNFMuSylejFhMslxgdzGlIAyXauwg
claims: {'jti': 'f7b9b19e-6099-4f82-a722-b610c5d0ba0c', 'iss': 'https://tacc.tapis.io/v3/tokens', 'sub': 'apitest@tacc', 'tapis/tenant_id': 'tacc', 'tapis/token_type': 'access', 'tapis/delegation': False, 'tapis/delegation_sub': None, 'tapis/username': 'apitest', 'tapis/account_type': 'user', 'exp': 1595099456, 'tapis/client_id': None, 'tapis/grant_type': 'password'}
expires_at: 2020-07-18 19:10:56+00:00
expires_in: <function Tapis.set_access_token.<locals>._expires_in at 0x7f29e213c510>
jti: f7b9b19e-6099-4f82-a722-b610c5d0ba0c
original_ttl: 14400

What we see is that the access_token.access_token is a Python string representing a JSON Web Token (JWT). JWTs are cryptographically signed with the private key associated with the tenant, and anyone can validate the signature by using the corresponding public key associated with the tenant (see Tenants section above). The public key for each tenant is available from the Tenants API. The core Tapis services will validate the access token sent on a given API call using the public key associated with the tenant to verify the JWT signature.

Using a Token

In order to use an access token in an API request to Tapis, pass the token in as the value of the X-Tapis-Token header. The PySDK will automatically send the token via this header for you. In CURL examples used throughout this documentation, we assume the raw JWT string representing an access token (like the above) has been exported as a shell variable; i.e.,

$ export JWT=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJqdGkiOiJmN2I5YjE5ZS02MDk5LTRmODItYTcyMi1iNjEwYzVkMGJhMGMiLCJpc3MiOiJodHRwczovL3RhY2MudGFwaXMuaW8vdjMvdG9rZW5zIiwic3ViIjoiYXBpdGVzdEB0YWNjIiwidGFwaXMvdGVuYW50X2lkIjoidGFjYyIsInRhcGlzL3Rva2VuX3R5cGUiOiJhY2Nlc3MiLCJ0YXBpcy9kZWxlZ2F0aW9uIjpmYWxzZSwidGFwaXMvZGVsZWdhdGlvbl9zdWIiOm51bGwsInRhcGlzL3VzZXJuYW1lIjoiYXBpdGVzdCIsInRhcGlzL2FjY291bnRfdHlwZSI6InVzZXIiLCJleHAiOjE1OTUwOTk0NTYsInRhcGlzL2NsaWVudF9pZCI6bnVsbCwidGFwaXMvZ3JhbnRfdHlwZSI6InBhc3N3b3JkIn0.alC8rRM-zNsHKcUiz3-tOJPaYtFksKb4Bit_aFE1HH_X_znnP2QkJaqc-xaRoMlQu26MN72TlJE0siIN3T38xXWBGDumHUYbvnNzT-7lk7AQU5MHSyCWx8IRDmTSbqmWOG8WBMCIV9Dh84mDd-X6eLJQ_cz1QqMAiI_cPgA9VVE22qDK3Lbz2pp9t0sm-l9XjE5y5Im8Y0B2p0ssPD0TjW20C5yngZ4-4jowDafboKlscog9ko-adrsVIjG_r-ccCUX3r8SVwQLypZFZAPKqbVzl8jt_mCi30W8AYwiaYGmH7INBbHI9hO7kwJNFMuSylejFhMslxgdzGlIAyXauwg

With that variable set, we can use the -H flag with curl to set the X-Tapis-Token header as follows:

$ curl -H "X-Tapis-Token: $JWT" ....

Note also the claims associated with the access token. These claims provide information about the token, including the user it represents (apitest in the above example), the tenant it belongs to (tacc above) when it expires, etc. Tapis tokens always include the following standard claims:

Claim

Description

Example Value

sub

The subject of the token; the subject uniquely identifies the user in a Tapis installation. The format is user @ tenant

apitest@tacc

exp

The expiry associated with the token.

1595099456

jti

Unique identifier for the token.

f7b9b19e-6099-4f82-a722-b610c5d0ba0c

iss

The identifier (URL) of the issuer of the JWT. For Tapis, the issuer will be a Tokens API.

https://tacc.tapis.io/v3/tokens

Additional custom claims specific to Tapis are namespaced with tapis/ at the beginning of the claim name. The authenticator for each tenant may optionally choose to support one or more of these additional claims. The following claims are encouraged and supported by the default OAuth2 Tapis authenticator.

Claim

Description

Example Value

tapis/tenant_id

The tenant of the subject.

tacc

tapis/username

The username of the subject.

apitest

tapis/token_type

Type of token: access or refresh

access

tapis/account_type

Type of account: user or service

user

tapis/delegation

Whether a delegation flow was used to generate this token. (true or false).

false

tapis/delegation_sub

For a delegation token, the subject who actually generated the token. In form user @ tenant

superuser@tacc

tapis/client_id

The id of the OAuth client used to generate the token.

tacc.CIC.tokenapp

tapis/grant_type

The grant type used to generate the token.

authorization_code

The authenticator for your tenant may include additional claims not listed here.

OAuth Clients

In order to use the more advanced OAuth2 flows, including any use of the authorization code grant type, the implicit grant type, and to generate refresh tokens with the password grant type, you must generate an OAuth2 client. Clients in OAuth2 represent applications (for example, a web or mobile application) that will interact with the OAuth2 server to generate tokens on behalf of one or more users. Clients are created and managed using the /v3/oauth2/clients endpoints.

Creating Clients

To create a client, make a POST request the the Clients API. All fields are optional; if you do not pass a client_id or client_key in the request, the clients API will generate random ones for you. In order to use the authorize_code and implicit grant types you will need to set the callback_url when registering your client (see Authorization Code Grant - Generating Tokens For Users). For a complete list of available parameters, see the API live-docs for Clients.

With PySDK:

>>> t.authenticator.create_client(client_id='test', callback_url='https://foo.example.com/oauth2/callback')

With CURL:

$ curl -H "X-Tapis-Token: $JWT" -H "Content-type: application/json" -d '{"client_id": "test", "callback_url": "https://foo.example.com/oauth2/callback"}' https://tacc.tapis.io/v3/oauth2/clients

The response will be similar to

callback_url: https://foo.example.com/oauth2/callback
client_id: test
client_key: WQZlQlMoxOynW
create_time: Sat, 18 Jul 2020 19:09:47 GMT
description:
display_name: https://foo.example.com/oauth2/callback
last_update_time: Sat, 18 Jul 2020 19:09:47 GMT
owner: apitest
tenant_id: tacc

Listing Clients

With PySDK:

>>> t.authenticator.list_clients()

With CURL:

$ curl -H "X-Tapis-Token: $JWT" https://tacc.tapis.io/v3/oauth2/clients

The response will be similar to

[
callback_url: https://foo.example.com/oauth2/callback
client_id: test
client_key: WQZlQlMoxOynW
create_time: Sat, 18 Jul 2020 19:09:47 GMT
description:
display_name: https://foo.example.com/oauth2/callback
last_update_time: Sat, 18 Jul 2020 19:09:47 GMT
owner: apitest
tenant_id: tacc]

Deleting Clients

You can also delete clients you are no longer using; just pass the client_id of the client to be deleted:

With PySDK:

>>> t.authenticator.delete_client(client_id='test')

With CURL:

$ curl -H "X-Tapis-Token: $JWT" -X DELETE https://tacc.tapis.io/v3/oauth2/clients/test

A null response is returned from a successful delete request.

Authorization Code Grant - Generating Tokens For Users

An important aspect of OAuth2 is that it enables applications to generate tokens on behalf of users without the applications needing to possess user credentials (i.e., passwords). In this section, we discuss using the OAuth2 authorization code grant type to do just that.

Assuming a Model-View-Controller (MVC) architecture, there are two controllers that must be written to support the authorization code grant type flow.

  1. A controller to determine if the user already has a valid access token and direct them to the OAuth2 authorization server when they do not. This controller starts the authorization code process. To do so, it should:

  • First inform the user that they will be asked to authenticate with their tenant username and password and then be asked to grant authorization to your client application.

  • Redirect the user to the OAuth2 server’s authorization URL. In the default Tapis authenticator, the authorization URL path is /v3/oauth2/authorize; for example, https://tacc.tapis.io/v3/oauth2/authorize in the tacc tenant.

  • The redirect request should include the following query parameters:

    • client_id: the id of your client.

    • redirect_uri: the URI to redirect back to with the authorization code. This must match the callback_url parameter associated with your client.

    • response_type: should always have the value code.

  1. A controller to process the authorization code returned and retrieve an access token on the user’s behalf. This controller receives requests containing authorization codes from the OAuth2 server after the user has successfully authenticated with said OAuth2 server, and it immediately exchanges the code for a token.

  • Responds to GET requests to the URL defined in the callback_url parameter of your client.

  • Retrieves the code query parameter from the request.

  • Makes a POST request to the OAuth2 server’s tokens endpoint to generate a token. In the default Tapis authenticator, the tokens URL path is /v3/oauth2/tokens; for example, https://tacc.tapis.io/v3/oauth2/tokens in the tacc tenant. The POST body must include the following parameters:

    • code: the code the controller just received in the request from the OAuth2 server.

    • redirect_uri: should be the same as the callback_url parameter of your client.

    • grant_type: should always have the value authorization_code.

Note that many popular web frameworks support OAuth2 flows with minimal custom coding required.

The final step to using the authorization code grant type is to register a client (see above) with a callback_url parameter equal to the URL within your web application where it will handle converting authorization codes into access tokens (i.e., controller 2 above).

Implicit Grant - Directly Returning Tokens For Users

As mentioned in the authorization code grant section, OAuth2 provides an extra wall of security where the application does not encounter credentials of the user (i.e., passwords). In this section, we discuss using the OAuth2 implicit grant type.

Just like the authorization code grant type, the implicit grant type retrieves an access token on behalf of the user to login. In addition, the implicit grant type requires a redirect to the authorization server.

The difference between the two grant types is that the access token is directly delivered through the browser, rather than a code that is later exchanged for the access token. This allows the implicit grant type to be easier to set up, but much less secure as it cannot authenticate the client. The typical use case for the implicit grant type would be for applications where the client secrets cannot be securely stored.

This is how to start the implicit flow:

  • Redirect the user to the OAuth2 server’s authorization URL. In the default Tapis authenticator, the authorization URL path is /v3/oauth2/authorize; for example, https://tacc.tapis.io/v3/oauth2/authorize in the tacc tenant.

  • The redirect request should include the following query parameters:

    • client_id: the id of your client.

    • redirect_uri: the URI to redirect back to with the access token. This must match the callback_url parameter associated with your client.

    • response_type: should always have the value token.

Once the user completes the login process through the authorization server, the server redirects the user to the redirect uri along with the access token in the browser.

The final step is to collect the access token in the url and store it in the cookies.

The implicit grant type also requires a client (see above) with a callback_url parameter equal to the URL within your web application where it will handle storing the access tokens in the cookies.

Implicit Grant Tutorial

This section serves as a small tutorial to further understand how the implicit grant works and how to set it up.

The first step is to create an OAuth2 client as shown in the OAuth Clients section. While setting up the client, make sure to set the callback_url to the website that the user will be redirected to (redirect_uri) after successfully authenticating on the authorization server. The whole flow will not work if these two values are not identical. Additionally, make sure to remember the client_id as it will be needed later.

Note

The callback_url of the client created can be changed later on if needed, but the client_id cannot.

The next step is to redirect the user to the authorization server’s website. The most common way is to create a button on the website that takes the user to the authorization server’s website. Make sure to follow this specific formatting for the website url in order to prevent any errors from occuring:

https://{tenant}.tapis.io/v3/oauth2/login?client_id={client_id}&redirect_uri={redirect_uri}&response_type={response_type}

This is the formatting that will be used for all OAuth2 authentication. Everything in curly brackets needs to be replace with the correct values to successfully set up the implicit flow. Make sure to drop the curly brackets when replacing them with their respective values.

  • {tenant}: This is the tenant value. Examples are tacc, scoped, dev, etc.

  • {client_id}: This is the client id for the client that was created in the first step.

  • {redirect_uri}: This is the value that needs to be the same as the callback_url.

Important

The value that replaces this {redirect_uri} needs to be URL Encoded!! Use an online url encoder to help format the value if needed.

Example of a properly url encoded value:

From: https://tacc.tapis.io/v3/oauth2/login?
To:   https%3A%2F%2Ftacc.tapis.io%2Fv3%2Foauth2%2Flogin%3F
  • {response_type}: This is the value that will be delivered through the browser and is dependent on the authorization type that is being used.

  • For implicit, use token.

  • For authorization code, use code.

Once the user has authenticated through the server, the user will automatically be redirected to the redirect_uri or callback_url. When redirected, the access_token (code for authorization code) will be returned in the url. Use a method to capture this value and store it in the cookies.

Important

Note that for both access_token and code, there are more parameters that are returned alongside these values through the url like state. Therefore, make sure to capture the value that is between the “access_token=” (or “code=”) and “&state”.

Finally, since the user does not need to physically see the redirect page, store the token in the cookies and redirect the user one last time to the main page.

Note

For the authorization code grant type, everything in this tutorial is pretty much identical. However, there is one more final step of exchanging the code returned through the url with the token to complete the flow.

The Tapis Token Web Application

Tapis provides a graphical interface via a web application that enables users to generate tokens. The Tapis Web Application is available by default for any tenant using the default Tapis authenticator, including the tacc tenant. The Tapis Token Web Application serves as an example of an application using the authorization code grant type.

The Tapis Token Web Application and its source code are available at the following URLs: