Sites, Tenancy and Authentication


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 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.


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

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

 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}]
 .  .  .

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.


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> where tenant_id is a short, unique identifier for the tenant in the Tapis system. For example, 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

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

allowable_x_tenant_ids: ['tacc']
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
public_key: -----BEGIN PUBLIC KEY-----
-----END PUBLIC KEY-----
service_ldap_connection_id: None
tenant_id: tacc
user_ldap_connection_id: tacc-all,

allowable_x_tenant_ids: ['dev']
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
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.


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='', 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='', username='apitest', password='abcd123')
>>> t.get_tokens()

With CURL:

> curl -H "Content-type: application/json" -d '{"username": "apitest", "password": "abcde123", "grant_type": "password" }' \

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': '', '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:



Example Value


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



The expiry associated with the token.



Unique identifier for the token.



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

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.



Example Value


The tenant of the subject.



The username of the subject.



Type of token: access or refresh



Type of account: user or service



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



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



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



The grant type used to generate the token.


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 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 grant type 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='')

With CURL:

$ curl -H "X-Tapis-Token: $JWT" -H "Content-type: application/json" -d '{"client_id": "test", "callback_url": ""}'

The response will be similar to

client_id: test
client_key: WQZlQlMoxOynW
create_time: Sat, 18 Jul 2020 19:09:47 GMT
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"

The response will be similar to

client_id: test
client_key: WQZlQlMoxOynW
create_time: Sat, 18 Jul 2020 19:09:47 GMT
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

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, 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, 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).

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: