Documentation

Authentication

Read tokens, refresh expired sessions, attach auth headers, and choose auth behavior per endpoint.

Backward compatible

Upgrading from 0.2.x

This upgrade has no breaking changes. Existing bearer-token configurations that use createTokenProvider continue to work without changes.

If you created a token provider only to refresh an HttpOnly cookie session, replace it with API-level refresh.

The direct refresh operation may return no data, including a 204 No Content response. Configure refresh on the API or in tokenProvider, not both.

TypeScript
1const api = createMicroApi({2  name: "main",3  baseUrl: "/api",4  refresh: {5    fn: () => auth.refresh.fn(),6  },7});

Breaking change

Migrating from 0.1.x to 0.2.0

Version 0.2.0 removes refresh.selectAccessToken.

Save refreshed tokens in refresh.onSuccess. micro-rq waits for this callback before retrying the original request.

The provider now calls getAccessToken for every request. Clearing application token storage therefore logs out future requests immediately.

TypeScript
1refresh: {2  fn: ({ refreshToken }) => auth.refresh.fn({ refreshToken }),3- selectAccessToken: (tokens) => tokens.accessToken,4  onSuccess: (tokens) => {5    localStorage.setItem("accessToken", tokens.accessToken);6    localStorage.setItem("refreshToken", tokens.refreshToken);7  },8}

Authentication overview

Authentication is optional.

For readable access tokens, create a token provider and pass it to createMicroApi.

For HttpOnly cookies, let the browser manage the cookie and use API-level refresh only when automatic refresh is needed.

Create a token provider

createTokenProvider keeps auth logic in one place.

It reads the current access token, can call your refresh endpoint, and lets you save or clear tokens after refresh.

When several requests receive 401 at the same time, they share one refresh call instead of starting many refresh requests.

The provider does not keep its own access-token copy. It calls getAccessToken for each request, so clearing your application's token storage logs out future requests immediately.

Key points

  • getAccessToken returns the current access token or null.
  • getRefreshToken returns the refresh token when your refresh flow needs one.
  • refresh.fn calls your refresh endpoint.
  • refresh.onSuccess is awaited; save returned tokens there before the original request is retried.
  • refresh.onError is where you clear auth state or notify the app.
TypeScript
1import { createTokenProvider } from "micro-rq";2 3export const tokenProvider = createTokenProvider({4  getAccessToken: () => localStorage.getItem("accessToken"),5  getRefreshToken: () => localStorage.getItem("refreshToken"),6  refresh: {7    fn: async ({ refreshToken }) => {8      const { auth } = await import("./auth");9 10      return auth.refresh.fn({ refreshToken });11    },12    onSuccess: (tokens) => {13      localStorage.setItem("accessToken", tokens.accessToken);14      localStorage.setItem("refreshToken", tokens.refreshToken);15    },16    onError: () => {17      localStorage.removeItem("accessToken");18      localStorage.removeItem("refreshToken");19    },20  },21});
TypeScript
1import { createMicroApi } from "micro-rq";2import { tokenProvider } from "./token-provider";3 4export const api = createMicroApi({5  name: "main",6  baseUrl: "/api",7  tokenProvider,8  authHeader: (token) => ({9    Authorization: `Bearer ${token}`,10  }),11});
TypeScript
1import { api } from "./api";2 3type AuthTokens = {4  accessToken: string;5  refreshToken: string;6};7 8export const auth = api.resource("auth", {9  refresh: api.post<AuthTokens, { refreshToken?: string | null }>("/auth/refresh", {10    authMode: "none",11  }),12});

Warning

Refresh endpoint warning

A refresh request should not trigger the same refresh flow again.

If your refresh endpoint uses the same authenticated api, set that endpoint to authMode: "none".

Without that, a failed refresh request can try to refresh itself and create a loop.

If the refresh endpoint needs its own auth behavior, put it on a separate API client.

TypeScript
1export const publicApi = createMicroApi({2  name: "public",3  baseUrl: "/api",4});5 6export const auth = publicApi.resource("auth", {7  refresh: publicApi.post<AuthTokens, { refreshToken?: string | null }>(8    "/auth/refresh",9    {10      // Use "optional" or "required" when this refresh endpoint11      // has its own auth behavior and cannot be authMode: "none".12      authMode: "optional",13    },14  ),15});

Attach auth to the API client

After creating the token provider, attach it to your API client.

Use authHeader to turn the access token into request headers.

Most APIs use a Bearer token, but you can return any header shape your backend expects.

TypeScript
1export const api = createMicroApi({2  name: "main",3  baseUrl: "/api",4  tokenProvider,5  authHeader: (token) => ({6    Authorization: `Bearer ${token}`,7  }),8});

Endpoint auth modes

Each endpoint can choose how it uses auth.

optional is the default. It uses a token when one exists, but still allows the request without one.

Use none for endpoints like login and refresh.

Use required when the request must have a token before fetch runs.

Key points

  • optional uses a token when one exists.
  • required throws MicroAuthRequiredError before fetch when no token exists.
  • none skips token lookup, auth headers, and refresh-on-401.
TypeScript
1export const auth = api.resource("auth", {2  login: api.post<LoginResponse, LoginDto>("/auth/login", {3    authMode: "none",4  }),5  profile: api.get<AuthUser>("/auth/profile", {6    // Optional is the default value; this line can be omitted.7    authMode: "optional",8  }),9  me: api.get<AuthUser>("/auth/me", {10    authMode: "required",11  }),12});