Strapi Authentication — JWT, Login, Register, Providers, and Token Refresh
In this tutorial, you will learn how Strapi handles authentication using JWT tokens, how to implement login and registration in your frontend, how to configure authentication providers, and how to manage token refresh for long-lived sessions.
What You'll Learn
- How JWT-based authentication works in Strapi
- How to implement login and register in a frontend application
- How authentication providers extend login options
- How token refresh extends session duration
- How to store and send JWT tokens securely
- How to handle authentication errors
Why It Matters
Authentication is the gateway to protected content. Every frontend that needs user-specific data must handle login, token storage, and authenticated requests. Understanding Strapi's authentication system lets you build secure, user-aware applications where users can create content, access private data, and maintain persistent sessions.
Real-World Use
A recipe sharing app needs users to register, log in, and submit their own recipes. When a logged-in user opens the app, their recipes appear with edit buttons. Public users see only the published recipes. The authentication system handles the JWT creation, token verification on every API request, and session management so the user stays logged in across app sessions.
Learning Path
flowchart LR A["Permissions"] --> B["Authentication
-- You are here"]:::current B --> C["SSO & OAuth"] C --> D["Advanced Auth"] classDef current fill:#4945ff,color:#fff,stroke-width:2px
How JWT Authentication Works
JWT (JSON Web Token) is a token-based authentication mechanism. When a user logs in, Strapi creates a signed JWT that encodes the user's identity and role.
Authentication flow:
1. User sends credentials (email + password) to Strapi
2. Strapi validates credentials against the database
3. Strapi creates a JWT containing user ID, role, and expiration
4. JWT is signed with a secret key (stored in environment variables)
5. Client receives the JWT and stores it (localStorage, cookie, etc.)
6. Client includes the JWT in every API request (Authorization header)
7. Strapi verifies the JWT on each request and identifies the user
The JWT is not encrypted — it is base64-encoded and signed. Anyone can read the contents (do not put secrets in the JWT), but only someone with the secret key can create a valid signature.
Login Endpoint
The login endpoint authenticates a user and returns a JWT.
// POST /api/auth/local
// Request body:
{
"identifier": "alice@example.com",
"password": "securepassword123"
}
// Successful response (200):
{
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"username": "alice",
"email": "alice@example.com",
"provider": "local",
"confirmed": true,
"blocked": false,
"role": {
"id": 1,
"name": "Authenticated"
}
}
}
// Error response (400):
{
"data": null,
"error": {
"status": 400,
"name": "ValidationError",
"message": "Invalid identifier or password"
}
}
The identifier field accepts either an email or username. The response includes both the JWT and user data. Store the JWT securely on the client.
Register Endpoint
The register endpoint creates a new user account.
// POST /api/auth/local/register
// Request body:
{
"username": "bob",
"email": "bob@example.com",
"password": "SecurePass123!"
}
// Successful response (201):
{
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 2,
"username": "bob",
"email": "bob@example.com",
"provider": "local",
"confirmed": false, // If email confirmation is enabled
"blocked": false,
"role": {
"id": 1,
"name": "Authenticated"
}
}
}
If email confirmation is enabled, the user has confirmed: false until they click the confirmation link. Unconfirmed users cannot log in. The registration behavior is configurable in Settings > Users & Permissions > Advanced Settings.
Sending Authenticated Requests
Once you have the JWT, include it in every API request using the Authorization header.
// Fetch with JWT authentication
async function fetchArticles() {
const jwt = localStorage.getItem("strapi_jwt");
const response = await fetch("http://localhost:1337/api/articles", {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${jwt}`,
},
});
if (response.status === 401) {
// Token expired or invalid — redirect to login
window.location.href = "/login";
return;
}
return response.json();
}
// Creating content with authentication
async function createArticle(title, content) {
const jwt = localStorage.getItem("strapi_jwt");
const response = await fetch("http://localhost:1337/api/articles", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${jwt}`,
},
body: JSON.stringify({
data: { title, content },
}),
});
return response.json();
}
Without the JWT, authenticated requests return 401 Unauthorized. With an invalid or expired JWT, they also return 401.
Token Refresh
JWT tokens have a limited lifespan (default is 30 days in Strapi). When a token expires, the user needs to log in again. Token refresh extends the session without requiring the user to re-enter credentials.
// Strapi does not have a built-in refresh token endpoint.
// Strategies for token refresh:
// Strategy 1: Long-lived tokens
// Set JWT expiration to a long period (e.g., 30 days)
// config/plugins.js:
module.exports = {
"users-permissions": {
config: {
jwt: {
expiresIn: "30d", // 30 days
},
},
},
};
// Strategy 2: Silent re-login
// When the API returns 401, prompt the user to log in again
// Store refresh credentials securely (not recommended)
// Strategy 3: Short-lived access + refresh tokens (custom)
// Implement a custom refresh token system
// Not natively supported in Strapi
For most applications, the default 30-day JWT lifespan is sufficient. Users rarely stay logged in for 30 consecutive days. If they return after the token expires, they log in again.
JWT Configuration
Configure JWT behavior in the Users & Permissions plugin:
// config/plugins.js
module.exports = {
"users-permissions": {
config: {
jwt: {
expiresIn: "7d", // Token expires in 7 days
secret: process.env.JWT_SECRET, // Use env variable
},
register: {
allowedFields: ["full_name", "bio"], // Extra fields on registration
},
},
},
};
The JWT secret should be a long, random string stored in an environment variable. Never hardcode it in configuration files.
Email Confirmation
Strapi can require email confirmation before a user can log in.
// Enable email confirmation:
// Settings > Users & Permissions > Advanced Settings
// Check: "Enable email confirmation"
// Configure the email template:
// Settings > Users & Permissions > Email Templates
// Customize the "Email confirmation" template
// The confirmation flow:
// 1. User registers
// 2. Strapi sends confirmation email with a link
// 3. User clicks the link
// 4. Strapi confirms the user account
// 5. User can now log in
// API endpoint for confirmation:
// GET /api/auth/email-confirmation?confirmation=<token>
Email confirmation requires an email provider to be configured. Without email configuration, the confirmation emails will not be sent.
Password Reset
Strapi provides endpoints for password reset:
// Step 1: Request password reset email
// POST /api/auth/forgot-password
// Body: { "email": "alice@example.com" }
// Step 2: Strapi sends email with reset code
// Step 3: Reset password with the code
// POST /api/auth/reset-password
// Body: {
// "code": "reset-code-from-email",
// "password": "newpassword",
// "passwordConfirmation": "newpassword"
// }
The password reset flow is configurable through email templates in the admin panel.
Common Mistakes
Storing JWT in localStorage without security considerations. localStorage is accessible to JavaScript, making it vulnerable to XSS Attacks. For higher security, use HTTP-only cookies. For most applications, localStorage with proper XSS protection is acceptable.
Not handling 401 responses gracefully. When a token expires, the API returns 401. Your frontend should catch this and redirect the user to login, not show a broken page or infinite loading spinner.
Hardcoding JWT secret in configuration files. The JWT secret should be an environment variable, never committed to version control. If the secret is compromised, attackers can forge tokens.
Sending passwords in URL parameters. Always use POST requests with JSON body for login and registration. Never send credentials in GET request URLs or query parameters.
Not validating email confirmation status. If email confirmation is enabled, unconfirmed users cannot log in. Check the user's
confirmedstatus on the frontend and show appropriate messages.
Practice Questions
What authentication mechanism does Strapi use? Answer: JWT (JSON Web Token). Strapi signs a token with the user's identity and the client sends this token with each API request in the Authorization header.
What is the endpoint for user login? Answer:
POST /api/auth/localwithidentifier(email or username) andpasswordin the request body.How do you include the JWT in API requests? Answer: By adding an
Authorization: Bearer <jwt>header to every request. The server verifies the token and identifies the user.Challenge: Build a complete authentication UI in a frontend framework of your choice: (1) Login form that calls the Strapi login endpoint, (2) Registration form with validation, (3) Protected dashboard that only shows when authenticated, (4) Token storage and automatic inclusion in API requests, (5) Logout functionality that clears the token, (6) Handling token expiration with redirect to login. Add error messages for wrong credentials, unconfirmed accounts, and network failures.
FAQ
Mini Project
Your task: Build a complete authentication system for a frontend application.
- Configure Strapi's authentication settings:
- Set JWT expiration to 1 hour for testing
- Enable email confirmation (use a test email provider like Mailtrap)
- Customize the email confirmation template
- Build a frontend (HTML/JS, React, or Vue) with:
- Registration form
- Login form
- Password reset flow
- Protected page that requires authentication
- Automatic token refresh on page load (check if token exists and is valid)
- Test the complete flow: register, confirm email, log in, access protected content, log out, attempt to access protected content without token.
What's Next
Now that you understand authentication, proceed to SSO & OAuth to learn how to integrate social login providers like Google, GitHub, and Facebook. After that, explore Advanced Auth for API tokens, Webhooks, and server-to-server authentication.
Related lessons:
- REST API Security — Securing authenticated endpoints
- Node.js JWT — How JWT signing works
- WordPress User Authentication — Compare authentication systems
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro