> This page is for Taurus PROTECT, version v3.54.
> For other versions, use one of these documentation indexes:
> - v3.58 (default): https://taurushq.ferndocs.com/protect-capital/v3.58/llms.txt
> - v3.56: https://taurushq.ferndocs.com/protect-capital/v3.56/llms.txt
> - v3.54: https://taurushq.ferndocs.com/protect-capital/v3.54/llms.txt
> - v3.52: https://taurushq.ferndocs.com/protect-capital/v3.52/llms.txt
> - v3.50: https://taurushq.ferndocs.com/protect-capital/v3.50/llms.txt
> - v3.48: https://taurushq.ferndocs.com/protect-capital/v3.48/llms.txt
> - v3.46: https://taurushq.ferndocs.com/protect-capital/v3.46/llms.txt
> - v3.44: https://taurushq.ferndocs.com/protect-capital/v3.44/llms.txt
> - v3.42: https://taurushq.ferndocs.com/protect-capital/v3.42/llms.txt
> - v3.40: https://taurushq.ferndocs.com/protect-capital/v3.40/llms.txt
> - v3.38: https://taurushq.ferndocs.com/protect-capital/v3.38/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://taurushq.ferndocs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://taurushq.ferndocs.com/_mcp/server.

# Short-Lived Access Tokens

## Basic Description

Short-Lived Access Tokens are an opt-in authentication scheme that issues a **short-lived access token** (default **validity of 5 minutes**) backed by a **long-lived, single-use refresh token**. This narrows the window during which a leaked token remains usable, while the refresh token keeps the user signed in and is revocable server-side.

The access token is a JWT and is used exactly like a [Bearer Authentication](/protect-capital/v3.54/reference/bearer-authentication) token. You pass it in the `Authorization` header of every authenticated API call. When it expires, you exchange the refresh token for a new pair instead of logging in again.

To obtain the initial token pair, pass your Taurus-PROTECT credentials to the **api/rest/v2/authentication/token** endpoint as described below.

This scheme runs alongside the existing Bearer Authentication; both are enabled by default and existing integrations keep working unchanged. For the full documentation of these endpoints, please refer to the API reference:

* [AuthenticateV2](/protect-capital/v3.54/reference/authentication/authenticate-v-2): password login
* [RefreshV2](/protect-capital/v3.54/reference/authentication/refresh-v-2): rotate the refresh token
* [LogoutV2](/protect-capital/v3.54/reference/authentication/logout-v-2): end the session

### Required Roles

TPUser

### Required Input Parameters

This is a POST method with the following json structure as input in the body of the call:

```json
{
  "email": "john@example.com",
  "password": "secret",
  "totp": "12345",
  "username": "john_example"
}
```

**password**: This is a required parameter. The value of this field is the clear text password of the user determined by one of the options below.

Pick one of two ways to identify the user:
**username**: The username given to the user on creation.
**email**: The email address of the user.
If multi factor authentication is enabled for the user, use the **totp** parameter to set the one time token.

> Preconditions
>
> It is important to note that the user credentials required for this endpoint need to pre-exist on the system. You will not be able to perform this call without valid credentials on Taurus-PROTECT.

## Token Model

| Token             | Default lifetime                    | Nature                                       | Sent on                                   |
| ----------------- | ----------------------------------- | -------------------------------------------- | ----------------------------------------- |
| **Access token**  | 5 minutes                           | Short-lived JWT carrying identity and claims | Every authenticated API call              |
| **Refresh token** | 30 minutes idle / 12 hours absolute | Opaque, single-use, revocable                | The `refresh` and `logout` endpoints only |

* The **access token** is a JWT: decode it for its claims exactly as with Bearer Authentication.
* The **refresh token** is an opaque string, **not** a JWT. Do not attempt to decode it; store it and present it as-is.
* **Rotation**: every call to `refresh` consumes the current refresh token and returns a new one. Always replace your stored refresh token with the one from the latest response.
* **Idle expiry** (sliding): a refresh token is valid for 30 minutes of inactivity; each successful refresh resets this window.
* **Absolute expiry** (hard cap): a session lives at most 12 hours regardless of activity, after which the user must sign in again.
* **Reuse detection**: presenting a refresh token that has already been rotated away invalidates the **entire session**. Never refresh the same token twice, and never run two refresh loops against one session in parallel.

## Response

`api/rest/v2/authentication/token` returns the token pair together with a small set of claims read from the access token:

```json
{
  "access":  { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expiresAt": "2026-07-21T12:05:00Z" },
  "refresh": { "token": "b3BhcXVlLXJlZnJlc2gtdG9rZW4...",          "expiresAt": "2026-07-21T12:30:00Z" },
  "isMFASetupRequired": false,
  "isLoggedInWithSSO": false
}
```

* **access.expiresAt**: when the access token expires (≈ now + 5 minutes).
* **refresh.expiresAt**: the refresh token's **idle** expiry (≈ now + 30 minutes). The 12-hour absolute cap is enforced server-side and is not returned here.
* **isMFASetupRequired**: `true` if the account must complete MFA setup before proceeding.
* **isLoggedInWithSSO**: `true` if the session was established via SSO.

## Transport Modes

The tokens can be delivered in two ways. The client chooses per request with the **`Token-Transport`** request header:

| `Token-Transport` value | Behaviour                                                                               |
| ----------------------- | --------------------------------------------------------------------------------------- |
| `jwt` (default)         | Token strings are returned in the response body, as shown above.                        |
| `cookie`                | Token strings are removed from the response body and set as secure, `HttpOnly` cookies. |

If the header is absent, `jwt` is used. Choose the mode that fits your client:

|                           | `jwt`                            | `cookie`                                                    |
| ------------------------- | -------------------------------- | ----------------------------------------------------------- |
| Where tokens live         | Response body → you store them   | Browser cookies (`HttpOnly`)                                |
| Reachable from JavaScript | Yes                              | No                                                          |
| Best for                  | Server-to-server, CLIs, Electron | Browser applications on a single strict origin              |
| Requires HTTPS            | Recommended                      | **Required** (`Secure` cookies)                             |
| Requires strict CORS      | No                               | **Yes**. See [CORS](#cors-requirement-for-cookie-transport) |

Electron applications cannot use `SameSite=Strict` cookies and should use `jwt` transport.

## Using the Access Token

Pass the access token as a Bearer credential on every authenticated request, exactly as with Bearer Authentication:

```
Authorization: Bearer <access.token>
```

When using **cookie** transport, the `__Secure-auth-access` cookie is sent automatically by the browser and converted to the credential server-side: you do **not** set the `Authorization` header yourself.

## Refreshing

When the access token is at or near its `expiresAt`, exchange the refresh token for a new pair at **api/rest/v2/authentication/refresh**:

```json
{ "refreshToken": "<current refresh.token>" }
```

The response has the same shape as the login response and contains a **new** access token and a **new** refresh token. Store both. With **cookie** transport, the refresh token is read from the `__Secure-auth-refresh` cookie, so the request body may be omitted. Send `Token-Transport: cookie` on this call as well.

## Logging Out

Call **api/rest/v2/authentication/logout** to revoke the session (and all of its refresh tokens):

```json
{ "refreshToken": "<current refresh.token>" }
```

With cookie transport this also clears the authentication cookies.

## Cookie Attributes

When `Token-Transport: cookie` is used, the server sets:

| Cookie                  | Path                          | Attributes                          |
| ----------------------- | ----------------------------- | ----------------------------------- |
| `__Secure-auth-access`  | `/api/rest/`                  | `HttpOnly; Secure; SameSite=Strict` |
| `__Secure-auth-refresh` | `/api/rest/v2/authentication` | `HttpOnly; Secure; SameSite=Strict` |

Because the cookies are `Secure`, cookie transport requires the API to be served over **HTTPS**.

## CORS Requirement for Cookie Transport

The authentication cookies are credentialed. If your browser client and the API are served from **different origins**, you **must** set the `daemon.cors_origin` configuration paramter when using cookie transport.

## Single Sign-On

SSO sign-in issues a v2 token pair through dedicated endpoints. The redirect-to-IdP and assertion/callback steps are unchanged; only the final token-issuance step returns a v2 pair:

* [SAMLSessionV2](/protect-capital/v3.54/reference/authentication-saml/saml-session-v-2): **api/rest/v2/authentication/saml/token**
* [OIDCSessionV2](/protect-capital/v3.54/reference/authentication-oidc/oidc-session-v-2): **api/rest/v2/authentication/oidc/token**

Both return the same response shape described under [Response](#response) and support the same `Token-Transport` header.

## Call Example

You can find a basic example in cURL below.
Please note that you will need to update the `BASEURL` for the command to function.

**`cURL`**

```curl cURL
export BASEURL=https://taurus-protect-instance.com

# 1. Log in to obtain the access + refresh token pair
curl --location '$BASEURL/api/rest/v2/authentication/token' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data-raw '{
  "email": "user1@bank.com",
  "password": "1234567896"
}'

# 2. Call the API with the access token
curl --location '$BASEURL/api/rest/v1/whitelisted-addresses' \
--header 'Authorization: Bearer <access.token>'

# 3. When the access token expires, rotate the refresh token
curl --location '$BASEURL/api/rest/v2/authentication/refresh' \
--header 'Content-Type: application/json' \
--data-raw '{ "refreshToken": "<refresh.token>" }'
```

> Call Result
>
> A successful response to the login call returns the token pair and claims:

```json
{
  "access":  { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expiresAt": "2026-07-21T12:05:00Z" },
  "refresh": { "token": "b3BhcXVlLXJlZnJlc2gtdG9rZW4...",          "expiresAt": "2026-07-21T12:30:00Z" },
  "isMFASetupRequired": false,
  "isLoggedInWithSSO": false
}
```

To use cookie transport instead, add `--header 'Token-Transport: cookie'` to the calls above; the token strings will be delivered as `__Secure-auth-*` cookies rather than in the response body.