# Firebase: “Firebase ID token has expired” (auth/id-token-expired)

> Your server rejects Firebase auth/id-token-expired: the client sent an ID token older than an hour. Get a fresh token per request with getIdToken().

Canonical: https://app369.com/fix/firebase-auth-id-token-expired/
Last updated: 2026-11-08

verifyIdToken on your server throws auth/id-token-expired, and API calls start failing about an hour after sign-in. Firebase ID tokens last one hour. The client SDK refreshes them, but only when you ask it for the token again. An app that saved the token once, in state, localStorage or a fixed header, keeps sending the old one.

**Short answer**

Do not store the ID token. Call getIdToken() on the current user right before each request; the SDK returns the cached token and refreshes it when it has expired. On the server, answer auth/id-token-expired with a 401, and let the client retry once with getIdToken(true).

## **Why the token is expired, and the fix**

1. 01 ### **The app reuses a token it saved earlier**
  > “Firebase ID tokens are short lived and last for an hour; the refresh token can be used to retrieve new ID tokens.” [**Firebase, Manage user sessions**](https://firebase.google.com/docs/auth/admin/manage-sessions) The usual cause: the token was read once after sign-in and put in state, localStorage or an API client’s default header. Ask the SDK for it on every request instead.```
async function api(path, init = {}) {
  const token = await auth.currentUser.getIdToken();
  return fetch(path, {
    ...init,
    headers: { ...init.headers, Authorization: \`Bearer ${token}\` },
  });
}
```
2. 02 ### **The server error is not handled**
  > “The provided Firebase ID token is expired.” [**Firebase, Admin Authentication API errors (auth/id-token-expired)**](https://firebase.google.com/docs/auth/admin/errors) verifyIdToken rejects an expired token with this code. Return 401 instead of 500, so the client knows to get a new token and retry once.```
try {
  const decoded = await getAuth().verifyIdToken(idToken);
  req.uid = decoded.uid;
} catch (e) {
  if (e.code === 'auth/id-token-expired') return res.status(401).end();
  throw e;
}
```
3. 03 ### **Force a refresh after a 401** Passing true makes the SDK fetch a new token even if its cached one looks valid. Use it for the one retry, not on every call.```
const token = await auth.currentUser.getIdToken(/* forceRefresh */ true);
```
4. 04 ### **The server clock is off** If every token fails, even fresh ones, check the server’s clock. A server clock that runs far ahead, past the token’s expiry time, makes valid tokens look expired. A device with a wrong clock can also hand out a stale token. Turn on automatic time sync.
5. 05 ### **A long-lived connection kept the first token** A WebSocket, server-sent events stream or GraphQL subscription that sent the token once at connect time is still using it an hour later. Send a fresh token on each reconnect, or re-authenticate the connection before the hour is up.
6. 06 ### **Revoked is a different error**
  > “If the user's token is revoked, the user should be signed out on the client or asked to reauthenticate” [**Firebase, Manage user sessions (Detect ID token revocation)**](https://firebase.google.com/docs/auth/admin/manage-sessions) With checkRevoked set, a token whose refresh token was revoked fails with auth/id-token-revoked. That one needs the person to sign in again, not a refresh.

Checked against the vendor’s docs on November 8, 2026: [Firebase docs: Manage user sessions](https://firebase.google.com/docs/auth/admin/manage-sessions); [Firebase docs: Verify ID tokens](https://firebase.google.com/docs/auth/admin/verify-id-tokens); [Firebase docs: Admin Authentication API errors](https://firebase.google.com/docs/auth/admin/errors).

## **Rather hand it over?**

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

- [from $450**App Fix Ticket**One reproducible bug in an existing mobile or web app, diagnosed and fixed.**See scope and start**](https://app369.com/fixed-price/flutter-firebase-bug-fix/)
- [from $450**AI-Built App Rescue**A Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.****No issues worth fixing, no charge.**** If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.**See scope and start**](https://app369.com/fixed-price/ai-app-rescue/)

Related: [**Firebase auth/user-disabled**](https://app369.com/fix/firebase-auth-user-disabled/) · [**Firebase auth/requires-recent-login**](https://app369.com/fix/firebase-auth-requires-recent-login/) · [**Supabase “JWT expired”**](https://app369.com/fix/supabase-jwt-expired/) · [**Firebase “FIS_AUTH_ERROR”**](https://app369.com/fix/firebase-fis-auth-error/)

Questions

## **More on this error**

<details>

<summary>**Can I make Firebase ID tokens last longer than an hour? **+</summary>

No. ID tokens last an hour. Keep sessions long by refreshing the token on the client; for server-rendered sites, Firebase also offers session cookies created from an ID token.

</details>

<details>

<summary>**Does getIdToken() call Firebase every time? **+</summary>

No. It returns the cached token while it is still valid and only fetches a new one when needed, so calling it before each request is cheap.

</details>

<details>

<summary>**Can you fix it for me? **+</summary>

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.

</details>

A different error?

## **Describe it and get a written quote**

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; Simon replies by email, usually within one business day.

[**Describe your problem **→](https://app369.com/contact/) [**All fixes**](https://app369.com/fix/)