---
title: "Tokens & refresh"
description: "Using tokens on requests, refreshing them, and verifying them in your own backend."
---

> Documentation Index
> Fetch the complete documentation index at: https://truememory-docs.mvstg.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Tokens & refresh

All requests go to the **Truememory Auth base URL** (see [Before you start](/authentication/overview/#before-you-start) for prerequisites: base URL, CORS whitelisting, OAuth client IDs).

## Using Tokens

Include the `id_token` as a Bearer token in all requests to Truememory services. Note that the AI Gateway (`your-tie-host`) is a separate service from Truememory Auth (`your-auth-host`):

```bash
curl -X POST https://your-tie-host/v1/chat/completions \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{"model": "anthropic/claude-sonnet-4-5", "messages": [{"role": "user", "content": "Hello"}]}'
```

The token is a JWT signed by Firebase. Truememory verifies the signature, checks expiry, and extracts the user identity.

## Token Refresh

ID tokens expire after **1 hour**. Use the refresh token to get a new one without re-authenticating:

```bash
POST https://your-auth-host/auth/refresh
Content-Type: application/json

{
  "refresh_token": "AMf-xB..."
}
```

**Response:**

```json
{
  "id_token": "eyJhbGciOiJSUzI1NiIs...",
  "refresh_token": "AMf-xB...",
  "expires_in": "3600"
}
```

The `refresh_token` in the response is the **same value you sent** — Truememory does not rotate refresh tokens. Re-reading it from the response is still the right habit, so your client keeps working unchanged if rotation is introduced later.

:::caution[The refresh token is a long-lived credential]
It mints ID tokens until it is revoked, and Truememory Auth exposes no revocation endpoint. Where you store it decides whether an XSS bug costs you an hour or the whole account — see [Suggested implementation](/authentication/token-handling/) for the recommended split (refresh token server-side, ID token in memory).
:::

Implement automatic refresh in your API client. Refresh **before** expiry rather than only on `401`, and de-duplicate concurrent refreshes so N parallel requests don't fire N refresh calls:

```typescript
let idToken: string | null = null;
let expiresAt = 0;
let inFlight: Promise<string> | null = null;

async function refreshIdToken(): Promise<string> {
  // Posts to your own backend, which holds the refresh token in an httpOnly
  // cookie. See "Suggested implementation" for the route handler.
  const res = await fetch("/api/auth/refresh", { method: "POST" });
  if (!res.ok) {
    idToken = null;
    throw new Error("Session expired");
  }

  const data = await res.json();
  idToken = data.id_token;
  expiresAt = Date.now() + Number(data.expires_in) * 1000;
  return data.id_token;
}

async function getIdToken(): Promise<string> {
  // Refresh 5 minutes early so a request never expires mid-flight.
  if (idToken && Date.now() < expiresAt - 5 * 60 * 1000) return idToken;

  inFlight ??= refreshIdToken().finally(() => {
    inFlight = null;
  });
  return inFlight;
}

async function fetchWithAuth(url: string, options: RequestInit = {}) {
  let token = await getIdToken();

  let response = await fetch(url, {
    ...options,
    headers: { ...options.headers, Authorization: `Bearer ${token}` },
  });

  if (response.status === 401) {
    // Refresh once, retry once — never loop.
    idToken = null;
    token = await getIdToken();
    response = await fetch(url, {
      ...options,
      headers: { ...options.headers, Authorization: `Bearer ${token}` },
    });
  }

  return response;
}
```

## Verifying Tokens in Your Backend

If your app has its own backend that needs to verify Truememory tokens (e.g. protecting your own API routes), this section explains how.

Truememory Auth issues **Firebase ID tokens** — JWTs signed by Firebase's service account keys with the GCP project ID as the audience. These are **not** standard Google OAuth2 tokens.

### Token structure

```json
{
  "iss": "https://securetoken.google.com/<gcp-project-id>",
  "aud": "<gcp-project-id>",
  "sub": "5iexdslxx...",
  "tie_user_id": "4b822c30-...",
  "email": "user@example.com",
  "email_verified": false,
  "firebase": {
    "sign_in_provider": "password"
  }
}
```

Key claims:

- `aud` — Always the GCP project ID (e.g. `mv-stg-tie-core`). This is Firebase's design, not configurable.
- `sub` — Firebase/GIP UID.
- `tie_user_id` — Truememory's canonical user UUID (custom claim). Use this when calling Truememory APIs. Set automatically on register, login, and OAuth sign-in.

### Verification approaches

There are two valid approaches depending on your security requirements:

| Approach | Library | Signature verified | When to use |
|----------|---------|-------------------|-------------|
| **Full verification** | `firebase-admin` | Yes | Public-facing APIs, production backends |
| **Trusted decode** | Manual JWT decode | No | Internal services that trust Truememory over HTTPS |

:::note[`google-auth-library` does not work for Truememory tokens]
`google-auth-library`'s `OAuth2Client.verifyIdToken()` validates against Google Sign-In tokens (`accounts.google.com` issuer, OAuth Client ID audience). Truememory issues Firebase ID tokens which use a different issuer (`securetoken.google.com/<project>`), audience (GCP project ID), and signing keys. Use `firebase-admin` or manual JWT decode instead.
:::

Source: https://truememory-docs.mvstg.com/authentication/tokens/index.mdx
