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:
A base URL with which to access the tenant; by default, the base URL takes the form
https://<tenant_id>.tapis.io
wheretenant_id
is a short, unique identifier for the tenant in the Tapis system. For example,https://tacc.tapis.io
is the base URL for thetacc
tenant.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 |
|
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. |
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 |
tapis/account_type |
Type of account: |
user |
tapis/delegation |
Whether a delegation flow was used
to generate this token. ( |
false |
tapis/delegation_sub |
For a delegation token, the
subject who actually generated the
token. In form
|
|
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.
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 thetacc
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 thecallback_url
parameter associated with your client.response_type
: should always have the valuetoken
.
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 aretacc
,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 thecallback_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
, usetoken
.For
authorization code
, usecode
.
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:
Token App (
tacc
tenant): https://tacc.tapis.io/v3/oauth2/webappToken App source code: https://github.com/tapis-project/authenticator