Try on DesignSafe

Tapis Authentication#

Tapis Authentication into TACC

by Silvia Mazzoni, DesignSafe, 2025

Tapis connects you to TACC. You can submit jobs as well as monitor them – whether you submitted them using Tapis or any other DesignSafe Application that uses it, such as the OpenSeesMP and OpenSeesSP job submitted from the web portal.

Just like you need a badge to access a secure building, you need a token to access different tools and data inside the Tapis platform. Tapis tokens are digital keys that prove your identity when you’re using Tapis services.

When you log in to Tapis (using your username and password or other method), you get a token. This token is what tells Tapis, “I’m allowed to use these tools.” The token is a long string with apparently-random characters.

This module will teach you how to obtain a tapis token, how to save it to a file, and how to retreive it from this file. We will create a python function with the content we generate in this module and will use this function in the next modules.

Tapis Authentication Tokens#

Just like you need a badge to access a secure building, you need a token to access different tools and data inside the Tapis platform. Tapis tokens are digital keys that prove your identity when you’re using Tapis services.

When you log in to Tapis (using your username and password or other method), you get a token. This token is what tells Tapis, “I’m allowed to use these tools.” The token is a long string with apparently-random characters.

Why Tokens?#

Tapis is built to help researchers and developers run jobs, access data, manage systems, and more — often from different computers and applications. Tokens make this secure and flexible.

Tokens let Tapis:

  • Know who you are

  • Check what you’re allowed to do

  • Let you interact with the system without needing to send your password every time

How Tokens Work (Simplified)#

  1. You log in You use your credentials to log in through the Tapis authentication service (either directly or through a script or app).

  2. Tapis gives you a token The system returns a token — a long string of letters and numbers — that represents you for a limited amount of time.

  3. You use the token in your requests When you want to call a Tapis API (e.g., to run a job or access files), you include the token in the header of your request:

    Authorization: Bearer <your_token_here>
    
  4. Tapis checks the token Behind the scenes, Tapis checks that the token is valid and sees what you’re allowed to do. If everything checks out, the action goes through.

  5. Tokens expire Tokens are only valid for a short period (usually minutes). After that, you’ll need to log in again to get a new one.

Save the Token#

You can save this token to a file and use it until it expires. This way you don’t have to enter your password as often.

Saving your token means:

  • You don’t need to log in again every time you run a script (as long as the token hasn’t expired).

  • You can load it into other scripts without exposing your username/password.

Tips for Beginners

  • Treat your token like a password — never share it or post it publicly.

  • You don’t need to remember your token — just copy and paste it when needed.

Connect#

We’ll use a Python utility function to authenticate and establish a session with Tapis:

  • Checks if a valid token file already exists

  • If no token is found — or the token has expired — it will prompt you to enter your username and password

  • You can also pass your username as a function argument to avoid typing it interactively

This simplifies your workflow and ensures you’re always connected with valid credentials — without the need to manage tokens manually.

Ths function is available in CommunityData, so you can use it across all your DesignSafe notebooks with a single command.

connect_tapis.py
# /home/jupyter/CommunityData/OpenSees/TrainingMaterial/training-OpenSees-on-DesignSafe/OpsUtils/OpsUtils/Tapis/connect_tapis.py
def connect_tapis(token_filePath: str = "~/.tapis_tokens.json",
                  base_url: str = "https://designsafe.tapis.io",
                  username: str = "",
                  password: str = "",
                  force_connect: bool = False):
    """
    Connect to a Tapis platform (e.g., DesignSafe) with automatic token handling.

    Behavior
    --------
    - Looks for a saved access token at `token_filePath` (default: ~/.tapis_tokens.json).
    - If present and not expired, uses it to create an authenticated Tapis client.
    - If missing/expired, or when `force_connect=True`, prompts for credentials,
      requests new tokens, and saves them back to `token_filePath`.
    - Prints expiration details for transparency.

    Parameters
    ----------
    token_filePath : str, default "~/.tapis_tokens.json"
        Path to the JSON file that stores the Tapis `access_token` and `expires_at`.
    base_url : str, default "https://designsafe.tapis.io"
        Tapis API endpoint base URL.
    username : str, default ""
        Optional preset username. If empty, you will be prompted.
    password : str, default ""
        Optional preset password. If empty, you will be prompted (securely).
    force_connect : bool, default False
        If True, ignores any valid saved token and performs a fresh login.

    Returns
    -------
    object
        An authenticated `Tapis` client object ready to use.

    Notes
    -----
    - The token file stores: `{"access_token": "...", "expires_at": "...ISO8601..."}`.
    - Expiry timestamps are treated as UTC if no timezone is present.
    - If the saved token cannot be parsed/validated, a fresh login is performed.

    Example
    -------
    t = connect_tapis()                        # use saved token or prompt as needed
    jobs = t.jobs.getJobList()                 # now you're authenticated

    Author
    ------
    Silvia Mazzoni, DesignSafe (silviamazzoni@yahoo.com)

    Date
    ----
    2025-08-14

    Version
    -------
    1.0
    """
    from tapipy.tapis import Tapis
    from getpass import getpass
    from datetime import datetime, timezone
    import json
    import os

    def _parse_expires_at(s: str) -> datetime | None:
        """Parse ISO8601 expiry, accepting 'Z' and naive strings; return aware UTC dt or None."""
        if not s:
            return None
        try:
            # normalize trailing 'Z' to +00:00
            s_norm = s.replace("Z", "+00:00")
            dt = datetime.fromisoformat(s_norm)
            if dt.tzinfo is None:
                dt = dt.replace(tzinfo=timezone.utc)
            return dt.astimezone(timezone.utc)
        except Exception:
            return None

    def getTokensLoop():
        username = getpass("Username: ")
        password = getpass("Password: ")
        t = Tapis(base_url=base_url, username=username, password=password)
        try:
            t.get_tokens()
            return t
        except Exception as e:
            print(f" ** Warning ** could get token : {e},\n TRY AGAIN!")
            t= getTokensLoop()
            return t

        
    print(" -- Checking Tapis token --")
    token_path = os.path.expanduser(token_filePath)
    now = datetime.now(timezone.utc)

    t = None
    saved_expires_at = None
    valid_token = False

    # Try to load a saved token
    if os.path.exists(token_path):
        try:
            with open(token_path, "r") as f:
                tokens = json.load(f)
            saved_expires_at = _parse_expires_at(tokens.get("expires_at"))
            if tokens.get("access_token") and saved_expires_at and saved_expires_at > now:
                print(" Token loaded from file. Token is still valid!")
                t = Tapis(base_url=base_url, access_token=tokens["access_token"])
                valid_token = True
            else:
                print(" Token file found but token is missing/expired.")
                if saved_expires_at:
                    print(" Token expired at:", saved_expires_at.isoformat())
        except Exception as e:
            print(f" Could not read/parse token file ({token_path}): {e}")
    else:
        print(" No saved tokens found.")

    if force_connect:
        print(" Forcing a connection to Tapis (fresh login).")

    if not valid_token or force_connect:
        print("-- Connect to Tapis --")
        if not username:
            # username isn't sensitive; echoing can help avoid typos, but keeping your original choice:
            username = getpass("Username: ")
        if not password:
            password = getpass("Password: ")
        t = Tapis(base_url=base_url, username=username, password=password)
        try:
            t.get_tokens()
        except Exception as e:
            print(f" ** Warning ** could get token : {e},\n TRY AGAIN!")
            t= getTokensLoop()
        # Save the new token back to the chosen path
        try:
            tokens = {
                "access_token": t.access_token.access_token,
                "expires_at": t.access_token.expires_at.isoformat(),
            }
            os.makedirs(os.path.dirname(token_path), exist_ok=True)
            with open(token_path, "w") as f:
                json.dump(tokens, f)
            print(f" Token saved to {token_path}")
            saved_expires_at = _parse_expires_at(tokens["expires_at"])
        except Exception as e:
            print(f" Warning: could not save token to {token_path}: {e}")

    # Print expiry info (use stored/parsed date if needed)
    exp_to_show = saved_expires_at
    try:
        # if available, prefer the client object's value
        if getattr(t, "access_token", None) and getattr(t.access_token, "expires_at", None):
            exp_to_show = _parse_expires_at(str(t.access_token.expires_at)) or exp_to_show
    except Exception:
        pass

    if exp_to_show:
        print(" Token expires at:", exp_to_show.isoformat())
        print(" Token expires in:", str(exp_to_show - now))
    else:
        print(" Token expiry time unavailable.")

    print("-- LOG IN SUCCESSFUL! --")
    return t
# NOTE: Your browser's autofill may not work here.
t=OpsUtils.connect_tapis(force_connect=True)
 -- Checking Tapis token --
 Token file found but token is missing/expired.
 Token expired at: 2025-09-02T23:45:26+00:00
 Forcing a connection to Tapis (fresh login).
-- Connect to Tapis --
 ** Warning ** could get token : message: Invalid username/password combination.,
 TRY AGAIN!
 ** Warning ** could get token : message: Either a refresh_token or a username and password are required for get_tokens().,
 TRY AGAIN!
 ** Warning ** could get token : message: Invalid username/password combination.,
 TRY AGAIN!
 Token saved to /home/jupyter/.tapis_tokens.json
 Token expires at: 2025-09-05T02:30:48+00:00
 Token expires in: 4:00:33.043411
-- LOG IN SUCCESSFUL! --
# NOTE: If your files have disappeared from the jupyter file browser in the left pane, please take a screenshot and submit a ticket to DesignSafe.
# Save all your work and reload the page in the browser -- the files will reappear.