Authentication
strapi-typed-client supports Bearer token authentication for both the runtime client and the CLI code generator. This page covers all the ways to configure authentication.
Client Authentication
Token via Constructor
The most common approach is to pass the API token when creating the client:
import { StrapiClient } from '@/strapi'
const strapi = new StrapiClient({
baseURL: 'http://localhost:1337',
token: 'your-strapi-api-token',
})The token is sent as a Bearer token in the Authorization header with every request.
Setting Token Dynamically
You can set or update the token after the client has been created using the setToken method:
const strapi = new StrapiClient({
baseURL: 'http://localhost:1337',
})
// Set token later (e.g., after user login)
strapi.setToken('your-strapi-api-token')
// Now all subsequent requests include the token
const data = await strapi.posts.find()This is useful in scenarios where the token is not available at initialization time, such as after a user authenticates.
Session Auth (Refresh Tokens, Strapi 5.43+)
Strapi 5.43 introduced a session-based JWT mode for users-permissions: a short-lived access JWT (~10 minutes) plus a rotating refresh token. Enable it on the backend:
// config/plugins.ts
export default {
'users-permissions': {
config: {
jwtManagement: 'refresh',
},
},
}strapi-typed-client detects this mode automatically at generation time — the plugin reads jwtManagement from the backend config and bakes the resulting mode into the generated client as the exported AUTH_MODE constant. Toggling the mode on the backend changes the schema hash, so the next generate (or Next.js dev/build) picks it up. No client configuration is required.
What the client does in refresh mode
- Access token in memory. Auth responses that carry a
jwt(login,register, OAuthcallback,resetPassword,changePassword,confirmEmail) store it on the client instance automatically. It is sent asAuthorization: Beareron every request and never toucheslocalStorage. - Transparent refresh on 401/403. When a request sent with the session's own token fails with 401 (expired access token), the client performs
POST /api/auth/refreshand retries the original request once. The same happens for a 403 on a fully anonymous request — that is what Strapi actually returns when a protected route is hit with no credentials (the anonymous caller authenticates as the public role and getsForbiddenError, never 401), and it is exactly the page-reload bootstrap case: access token gone from memory, refresh cookie still alive. Concurrent failures share a single in-flight refresh (single-flight). If the refresh fails, the session is dropped and the original error propagates — youronErrorhook fires as usual. A 403 under any Bearer is an honest permission error and always passes through untouched. - Dead-session latch. Once the server definitively rejects a refresh (4xx) — or after
logout()/clearToken()— the automatic refresh goes quiet instead of re-hitting the backend on every subsequent 401/403. A successfullogin()or an explicitauth.refresh()lifts the latch (callrefresh()to resume a session started in another tab). Transient failures (5xx, network errors) never latch and never drop the tokens. - Race-safe. A refresh that is already on the wire when you
login()orlogout()cannot override them: its late outcome is discarded, so a sign-out never gets resurrected by a stray refresh success, and a fresh login never gets wiped by a stale refresh rejection. - Browser-only. The entire session flow — token capture, Bearer injection, automatic refresh — is inert in Node. There is no cookie jar for a cookie-based refresh, and silently storing tokens on a client instance that may be shared across requests (e.g. a module-level singleton in Next.js) would leak one user's session into another's requests. An explicit
auth.refresh()still performs the request and reports the result, but stores nothing outside the browser. Server-side code should keep using long-lived API tokens viatoken(or explicitsetToken). - Plays fair with
onRequest. If your hook sets theAuthorizationheader, it owns auth for that request — the session flow will not refresh or retry it. The hook also runs for the flow's ownPOST /api/auth/refresh, so infrastructure headers (tenant ids, proxy keys) reach it; the flow never attachesAuthorizationto its refresh request itself. - Typed session methods.
auth.refresh(): Promise<boolean>andauth.logout(): Promise<void>are generated with full types.
Setup
The refresh token lives in an httpOnly cookie (strapi_up_refresh) by default, so the client must send credentials:
const strapi = new StrapiClient({
baseURL: process.env.NEXT_PUBLIC_STRAPI_URL!,
credentials: 'include',
})Bootstrap the session once on app load — after a page reload the in-memory access token is gone, but the cookie can mint a new one:
const isLoggedIn = await strapi.auth.refresh()
// true → session restored, requests are authenticated
// false → no live session, show the login screenThe rest is transparent:
await strapi.auth.login({ identifier: 'user@example.com', password: '...' })
await strapi.articles.find() // Bearer attached; auto-refreshed when expired
await strapi.auth.logout() // POST /api/auth/logout + clears local tokensNon-httpOnly setups
With sessions.httpOnly: false on the backend, refresh tokens are returned in the response body instead of a cookie. The client handles this too: AuthResponse.refreshToken is captured in memory and sent in the refresh request body. No extra configuration is needed (and credentials is not required for this variant).
Overriding the detected mode
StrapiClientConfig.authMode overrides the baked-in default per instance:
// A refresh-mode client that should behave like a legacy one (e.g. a
// server-side script using a long-lived API token):
const strapi = new StrapiClient({
baseURL: '...',
token: process.env.STRAPI_TOKEN,
authMode: 'legacy',
})INFO
Legacy backends are entirely unaffected: a client generated against a backend without jwtManagement: 'refresh' sends the same requests and headers as before — no refresh calls, no behavior change.
Using Environment Variables
A recommended pattern is to read the token from an environment variable:
const strapi = new StrapiClient({
baseURL: process.env.NEXT_PUBLIC_STRAPI_URL!,
token: process.env.STRAPI_TOKEN,
})TIP
In Next.js, environment variables without the NEXT_PUBLIC_ prefix are only available on the server side. Since API tokens should remain secret, use STRAPI_TOKEN (without the prefix) and only create the client in server components or API routes.
Example .env.local file:
NEXT_PUBLIC_STRAPI_URL=http://localhost:1337
STRAPI_TOKEN=your-strapi-api-token-hereCreating API Tokens in Strapi
To generate an API token in the Strapi admin panel:
- Navigate to Settings in the left sidebar.
- Under Global Settings, click API Tokens.
- Click Create new API Token.
- Configure the token:
- Name: A descriptive name (e.g., "Frontend Read-Only").
- Token type: Choose
Read-only,Full access, orCustom. - Token duration: Set an expiration or choose unlimited.
- Click Save and copy the generated token.
WARNING
The token is only displayed once after creation. Store it securely in your environment variables or a secrets manager. If you lose it, you will need to regenerate a new token.
Token Types
| Type | Permissions | Use Case |
|---|---|---|
| Read-only | find and findOne on all content | Public frontend, SSG/ISR |
| Full access | All CRUD operations on all content | Admin dashboards, server-side |
| Custom | Fine-grained per-content-type | Specific use cases |
CLI Authentication
Plugin requireAuth Option
The strapi-typed-client plugin can be configured to require authentication for its schema endpoint. When enabled, the CLI must provide a valid token to fetch the schema.
In your Strapi project's config/plugins.ts:
// config/plugins.ts
export default {
'strapi-typed-client': {
enabled: true,
config: {
requireAuth: true, // Require Bearer token for schema endpoint
},
},
}By default, requireAuth is false in development and true in production (NODE_ENV === 'production'). In development, the schema endpoint is publicly accessible for convenience.
Passing a Token to the CLI
When the plugin has requireAuth: true, pass the token using the --token flag:
npx strapi-types generate --token YOUR_API_TOKENOr using an environment variable:
STRAPI_TOKEN=your-token npx strapi-types generateINFO
The CLI uses the same Bearer token mechanism as the runtime client. Any API token with at least read access will work for schema fetching.
Full CLI Example
# Without auth (requireAuth: false, the default)
npx strapi-types generate --url http://localhost:1337
# With auth (requireAuth: true)
npx strapi-types generate --url http://localhost:1337 --token your-api-token
# Using environment variable
STRAPI_TOKEN=your-api-token npx strapi-types generate --url http://localhost:1337Security Recommendations
Development
During local development, requireAuth defaults to false so you can regenerate types without managing tokens:
// config/plugins.ts
export default {
'strapi-typed-client': {
enabled: true,
config: {
requireAuth: false,
},
},
}Production
In production, requireAuth defaults to true automatically. You can also set it explicitly:
// config/plugins.ts
export default {
'strapi-typed-client': {
enabled: true,
config: {
requireAuth: true,
},
},
}WARNING
The schema endpoint exposes the complete structure of your content types, including field names, relation targets, and component structures. In production environments, enable requireAuth to prevent this information from being publicly accessible.
CI/CD Pipelines
When generating types in a CI/CD pipeline, store the token as a secret and pass it to the CLI:
# GitHub Actions example
- name: Generate Strapi types
run: npx strapi-types generate --url ${{ secrets.STRAPI_URL }} --token ${{ secrets.STRAPI_TOKEN }}Authentication Flow Summary
┌─────────────────────────────────────────────────┐
│ Strapi Server │
│ │
│ config/plugins.ts │
│ ┌─────────────────────────────────────────┐ │
│ │ requireAuth: true/false │ │
│ └─────────────────────────────────────────┘ │
│ │
│ GET /api/strapi-typed-client/schema │
│ ← Authorization: Bearer <token> │
│ │
│ GET /api/strapi-typed-client/schema-hash │
│ ← Authorization: Bearer <token> │
│ │
└────────────────────┬────────────────────────────┘
│
┌────────────┼────────────────┐
│ │ │
CLI Generate StrapiClient CI/CD Pipeline
--token flag { token: '...' } env secret