From cda7082ff7436c9cbcc7520073cf8e062cecb8ab Mon Sep 17 00:00:00 2001 From: Pierre Wizla <4233866+pwizla@users.noreply.github.com> Date: Tue, 12 May 2026 12:13:16 +0200 Subject: [PATCH 01/29] Update Users & Permissions main page: fix incorrect values, restructure API section --- .../docs/cms/features/users-permissions.md | 419 ++---------------- 1 file changed, 28 insertions(+), 391 deletions(-) diff --git a/docusaurus/docs/cms/features/users-permissions.md b/docusaurus/docs/cms/features/users-permissions.md index 11fa3c7017..292df8f61e 100644 --- a/docusaurus/docs/cms/features/users-permissions.md +++ b/docusaurus/docs/cms/features/users-permissions.md @@ -2,7 +2,7 @@ title: Users & Permissions description: Learn to use the Users & Permissions feature to manage end-user accounts, authentication, and role-based access. displayed_sidebar: cmsSidebar -toc_max_heading_level: 5 +toc_max_heading_level: 4 tags: - admin panel - users & permissions @@ -95,7 +95,7 @@ When ticking an action or permission box, related bound routes of the API are di **Path:** *Users & Permissions plugin > Roles* -Although the 2 default end-user roles cannot be deleted, the other ones can, as long as no end user still has this role attributed to their account. +Although the Public role cannot be deleted, other roles can be deleted as long as no end user still has the role attributed to their account. 1. Click on the delete button on the right side of the role's record. 2. In the deletion window, click on the **Confirm** button to confirm the deletion. @@ -226,7 +226,7 @@ module.exports = ({ env }) => ({ config: { jwtManagement: 'legacy-support', jwt: { - expiresIn: '7d', // Traditional JWT expiry + expiresIn: '30d', // Traditional JWT expiry }, }, }, @@ -257,9 +257,11 @@ module.exports = ({ env }) => ({ config: { jwtManagement: 'refresh', sessions: { - accessTokenLifespan: 604800, // 1 week (default) + accessTokenLifespan: 600, // 10 minutes (default) maxRefreshTokenLifespan: 2592000, // 30 days - idleRefreshTokenLifespan: 604800, // 7 days + idleRefreshTokenLifespan: 1209600, // 14 days + maxSessionLifespan: 86400, // 1 day (default) + idleSessionLifespan: 7200, // 2 hours (default) httpOnly: false, // Set to true for HTTP-only cookies cookie: { name: 'strapi_up_refresh', @@ -286,9 +288,11 @@ export default ({ env }) => ({ config: { jwtManagement: 'refresh', sessions: { - accessTokenLifespan: 604800, // 1 week (default) + accessTokenLifespan: 600, // 10 minutes (default) maxRefreshTokenLifespan: 2592000, // 30 days - idleRefreshTokenLifespan: 604800, // 7 days + idleRefreshTokenLifespan: 1209600, // 14 days + maxSessionLifespan: 86400, // 1 day (default) + idleSessionLifespan: 7200, // 2 hours (default) httpOnly: false, // Set to true for HTTP-only cookies cookie: { name: 'strapi_up_refresh', @@ -364,8 +368,8 @@ The following options are available in [the `/config/plugins` file](/cms/configu | --------- | ----------- | ---- | ------- | | `ratelimit` | Settings to customize the rate limiting of the authentications and registration endpoints | object | `{}` | | `ratelimit.enabled` | Enable or disable the rate limiter | boolean | `true` | -| `ratelimit.interval` | Time window for requests to be considered as part of the same rate limiting bucket | object | `{ min: 5 }` | -| `ratelimit.max` | Maximum number of requests allowed in the time window | integer | `5` | +| `ratelimit.interval` | Time window for requests to be considered as part of the same rate limiting bucket (in milliseconds) | integer | `60000` (1 minute) | +| `ratelimit.max` | Maximum number of requests allowed in the time window | integer | `10` | | `ratelimit.prefixKey` | Prefix for the rate limiting key | string | `${userIdentifier}:${requestPath}:${ctx.request.ip}` | @@ -381,8 +385,8 @@ module.exports = ({ env }) => ({ config: { ratelimit: { enabled: true, - interval: { min: 5 }, - max: 5, + interval: 60000, // 1 minute + max: 10, }, }, }, @@ -402,8 +406,8 @@ export default ({ env }) => ({ config: { ratelimit: { enabled: true, - interval: { min: 5 }, - max: 5, + interval: 60000, // 1 minute + max: 10, }, }, }, @@ -452,11 +456,9 @@ The following variables can be used: ### Security configuration -JWTs can be verified and trusted because the information is digitally signed. To sign a token a _secret_ is required. By default Strapi generates and stores it in `/src/extensions/users-permissions/config/jwt.js`. +JWTs can be verified and trusted because the information is digitally signed. To sign a token a _secret_ is required. By default Strapi stores it as the `JWT_SECRET` environment variable in the `.env` file. -This is useful during development but for security reasons it is recommended to set a custom token via an environment variable `JWT_SECRET` when deploying to production. - -By default you can set a `JWT_SECRET` environment variable and it will be used as secret. If you want to use another variable you can update the configuration file. +If you want to use a different environment variable, you can update the configuration file. @@ -573,362 +575,22 @@ If end users can register themselves on your front-end application (see "Enable ### API usage - Each time an API request is sent the server checks if an `Authorization` header is present and verifies if the user making the request has access to the resource. :::note When you create a user without a role, or if you use the `/api/auth/local/register` route, the `authenticated` role is given to the user. ::: -#### Authentication endpoints {#authentication-endpoints} - -The Users & Permissions feature provides the following authentication endpoints for user management and [Content API](/cms/api/rest) access: - -| Method | URL | Description | -| ------ | --- | ----------- | -| `POST` | `/api/auth/local` | User login with email/username and password
(see [`identifier` parameter](#identifier)) | -| `POST` | `/api/auth/local/register` | [User registration](#user-registration) | -| `POST` | `/api/auth/forgot-password` | Request password reset | -| `POST` | `/api/auth/reset-password` | Reset password using token | -| `GET` | `/api/auth/email-confirmation` | Confirm user email address | -| `POST` | `/api/auth/send-email-confirmation` | Resend confirmation email | -| `POST` | `/api/auth/change-password` | Change password (requires authentication) | - -##### Session management endpoints - -When [session management](#jwt-management-modes) is enabled (`jwtManagement: 'refresh'`), additional endpoints are available: - -| Method | URL | Description | -| ------ | --- | ----------- | -| `POST` | `/api/auth/refresh` | Refresh access token using refresh token | -| `POST` | `/api/auth/logout` | Revoke user sessions (supports device-specific logout) | - -To refresh your authentication token, send the following request: - - - -``` -curl -X POST http://localhost:1337/api/auth/refresh \ - -H "Content-Type: application/json" \ - -d '{ - "refreshToken": "your-refresh-token" - }' -``` - - - -```json -{ - "jwt": "your-new-access-token" -} -``` - - - -To log out of all sessions, send the following request: - - - - -```bash -curl -X POST http://localhost:1337/api/auth/logout \ - -H "Authorization: Bearer your-access-token" -``` - - - - -#### User CRUD endpoints - -The Users & Permissions feature also exposes a set of endpoints for managing user records directly. These endpoints are separate from the authentication endpoints and allow you to create, read, update, and delete user entries: - -| Method | URL | Description | -| ------ | --- | ----------- | -| `GET` | `/api/users` | Find all users | -| `GET` | `/api/users/me` | Get the currently authenticated user | -| `GET` | `/api/users/:id` | Find a specific user by ID | -| `GET` | `/api/users/count` | Get the total number of users | -| `POST` | `/api/users` | Create a new user | -| `PUT` | `/api/users/:id` | Update a user by ID | -| `DELETE` | `/api/users/:id` | Delete a user by ID | - -:::note -These endpoints are protected by the role-based permission system. To access them, enable the corresponding action (e.g., `find`, `findOne`, `create`, `update`, `destroy`, `me`, `count`) for the desired role in *Users & Permissions plugin > Roles*. -::: - -##### Get the authenticated user - -The `GET /api/users/me` endpoint returns the user associated with the current JWT. The endpoint is useful for front-end applications that need to display user profile information after login. - - - - -```bash -curl -X GET http://localhost:1337/api/users/me \ - -H "Authorization: Bearer your-access-token" -``` - - - - - -```json -{ - "id": 1, - "documentId": "abc123", - "username": "kai", - "email": "kai@strapi.io", - "provider": "local", - "confirmed": true, - "blocked": false, - "createdAt": "2024-01-15T09:00:00.000Z", - "updatedAt": "2024-01-15T09:00:00.000Z", - "publishedAt": "2024-01-15T09:00:00.000Z" -} -``` - - - - -##### Find all users - -The `GET /api/users` endpoint returns a list of all users. [`populate` and `filters` parameters](/cms/api/rest/parameters) can be passed as query strings. - - - - -```bash -curl -X GET "http://localhost:1337/api/users?populate=role" \ - -H "Authorization: Bearer your-access-token" -``` - - - - - -```json -[ - { - "id": 1, - "documentId": "abc123", - "username": "kai", - "email": "kai@strapi.io", - "provider": "local", - "confirmed": true, - "blocked": false, - "role": { - "id": 1, - "name": "Authenticated", - "description": "Default role given to authenticated user.", - "type": "authenticated", - "createdAt": "2024-01-01T00:00:00.000Z", - "updatedAt": "2024-01-01T00:00:00.000Z" - }, - "createdAt": "2024-01-15T09:00:00.000Z", - "updatedAt": "2024-01-15T09:00:00.000Z", - "publishedAt": "2024-01-15T09:00:00.000Z" - } -] -``` - - - - -##### Create a user - -The `POST /api/users` endpoint creates a new user. Unlike `/api/auth/local/register`, this endpoint requires `create` permission for the Users & Permissions plugin. - - - - -```bash -curl -X POST http://localhost:1337/api/users \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer your-access-token" \ - -d '{ - "username": "newuser", - "email": "newuser@strapi.io", - "password": "Password123", - "role": 1, - "confirmed": true - }' -``` - - - - - -```json -{ - "id": 2, - "documentId": "def456", - "username": "newuser", - "email": "newuser@strapi.io", - "provider": "local", - "confirmed": true, - "blocked": false, - "createdAt": "2024-01-16T10:00:00.000Z", - "updatedAt": "2024-01-16T10:00:00.000Z", - "publishedAt": "2024-01-16T10:00:00.000Z" -} -``` - - - - -##### Update a user - -The `PUT /api/users/:id` endpoint updates an existing user by ID. - - - - -```bash -curl -X PUT http://localhost:1337/api/users/2 \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer your-access-token" \ - -d '{ - "username": "updateduser" - }' -``` - - - - - -```json -{ - "id": 2, - "documentId": "def456", - "username": "updateduser", - "email": "newuser@strapi.io", - "provider": "local", - "confirmed": true, - "blocked": false, - "createdAt": "2024-01-16T10:00:00.000Z", - "updatedAt": "2024-01-17T11:00:00.000Z", - "publishedAt": "2024-01-16T10:00:00.000Z" -} -``` - - - - -##### Delete a user - -The `DELETE /api/users/:id` endpoint deletes a user by ID. +The Users & Permissions feature exposes authentication, user management, and role/permission endpoints through both the REST API and the GraphQL API. Full endpoint references with request and response examples are available on the dedicated sub-pages: - - - -```bash -curl -X DELETE http://localhost:1337/api/users/2 \ - -H "Authorization: Bearer your-access-token" -``` - - - - - -```json -{ - "id": 2, - "documentId": "def456", - "username": "updateduser", - "email": "newuser@strapi.io", - "provider": "local", - "confirmed": true, - "blocked": false, - "createdAt": "2024-01-16T10:00:00.000Z", - "updatedAt": "2024-01-17T11:00:00.000Z", - "publishedAt": "2024-01-16T10:00:00.000Z" -} -``` - - - - -#### Identifier - -The `identifier` parameter sent with requests can be an email or username, as in the following examples: - - - - - -```js -import axios from 'axios'; - -// Request API. -axios - .post('http://localhost:1337/api/auth/local', { - identifier: 'user@strapi.io', - password: 'strapiPassword', - }) - .then(response => { - // Handle success. - console.log('Well done!'); - console.log('User profile', response.data.user); - console.log('User token', response.data.jwt); - }) - .catch(error => { - // Handle error. - console.log('An error occurred:', error.response); - }); -``` - - - - - -If you use **Postman**, set the **body** to **raw** and select **JSON** as your data format: - -```json -{ - "identifier": "user@strapi.io", - "password": "strapiPassword" -} -``` - -If the request is successful you will receive the **user's JWT** in the `jwt` key: - -**Legacy mode response:** -```json -{ - "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNTc2OTM4MTUwLCJleHAiOjE1Nzk1MzAxNTB9.UgsjjXkAZ-anD257BF7y1hbjuY3ogNceKfTAQtzDEsU", - "user": { - "id": 1, - "username": "user", - ... - } -} -``` - -**Session management mode response:** -```json -{ - "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", // Short-lived access token - "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", // Long-lived refresh token - "user": { - "id": 1, - "username": "user", - ... - } -} -``` - - - + + + + #### Token usage -The `jwt` may then be used for making permission-restricted API requests. To make an API request as a user place the JWT into an `Authorization` header of the `GET` request. - -Any request without a token will assume the `public` role permissions by default. Modify the permissions of each user's role in the admin panel. - -Authentication failures return a `401 (unauthorized)` error. - -The `token` variable is the `data.jwt` received when logging in or registering. +The `jwt` received when logging in or registering may then be used for making permission-restricted API requests. To make an API request as a user, place the JWT into an `Authorization` header of the request using the Bearer token pattern: ```js import axios from 'axios'; @@ -937,7 +599,7 @@ const token = 'YOUR_TOKEN_HERE'; // Request API. axios - .get('http://localhost:1337/posts', { + .get('http://localhost:1337/api/posts', { headers: { Authorization: `Bearer ${token}`, }, @@ -952,38 +614,13 @@ axios }); ``` -#### User registration - -Creating a new user in the database with a default role as 'authenticated' can be done as in the following example: - -```js -import axios from 'axios'; - -// Request API. -// Add your own code here to customize or restrict how the public can register new users. -axios - .post('http://localhost:1337/api/auth/local/register', { - username: 'Strapi user', - email: 'user@strapi.io', - password: 'strapiPassword', - }) - .then(response => { - // Handle success. - console.log('Well done!'); - console.log('User profile', response.data.user); - console.log('User token', response.data.jwt); - }) - .catch(error => { - // Handle error. - console.log('An error occurred:', error.response); - }); -``` +Any request without a token will assume the `public` role permissions by default. Modify the permissions of each user's role in the admin panel. Authentication failures return a `401 (unauthorized)` error. #### User object in Strapi context The `user` object is available to successfully authenticated requests. -The authenticated `user` object is a property of `ctx.state`. +The authenticated `user` object is a property of `ctx.state`, as shown in the following example: ```js create: async ctx => { From 2da7cac10625a159ccd0ab94c2c3faebc883257f Mon Sep 17 00:00:00 2001 From: Pierre Wizla <4233866+pwizla@users.noreply.github.com> Date: Tue, 12 May 2026 12:18:58 +0200 Subject: [PATCH 02/29] Add Users & Permissions REST and GraphQL API sub-pages to sidebar Co-Authored-By: Claude Opus 4.6 (1M context) --- docusaurus/sidebars.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/docusaurus/sidebars.js b/docusaurus/sidebars.js index 431e089361..0016140e9e 100644 --- a/docusaurus/sidebars.js +++ b/docusaurus/sidebars.js @@ -121,9 +121,22 @@ const sidebars = { id: 'cms/features/sso', }, { - type: 'doc', + type: 'category', label: 'Users & Permissions', - id: 'cms/features/users-permissions', + link: { type: 'doc', id: 'cms/features/users-permissions' }, + collapsed: true, + items: [ + { + type: 'doc', + label: 'REST API', + id: 'cms/features/users-permissions/rest-api', + }, + { + type: 'doc', + label: 'GraphQL API', + id: 'cms/features/users-permissions/graphql-api', + }, + ], }, { type: 'category', From 1940f2931870ddf4908aecbaff1193dee967238c Mon Sep 17 00:00:00 2001 From: Pierre Wizla <4233866+pwizla@users.noreply.github.com> Date: Tue, 12 May 2026 12:20:34 +0200 Subject: [PATCH 03/29] Add Users & Permissions REST API reference page Co-Authored-By: Claude Opus 4.6 (1M context) --- .../features/users-permissions/rest-api.md | 675 ++++++++++++++++++ 1 file changed, 675 insertions(+) create mode 100644 docusaurus/docs/cms/features/users-permissions/rest-api.md diff --git a/docusaurus/docs/cms/features/users-permissions/rest-api.md b/docusaurus/docs/cms/features/users-permissions/rest-api.md new file mode 100644 index 0000000000..1b5f58c40b --- /dev/null +++ b/docusaurus/docs/cms/features/users-permissions/rest-api.md @@ -0,0 +1,675 @@ +--- +title: Users & Permissions REST API +description: REST API reference for authentication, user management, roles, and permissions with the Users & Permissions plugin. +displayed_sidebar: cmsSidebar +tags: + - users & permissions + - REST API + - authentication + - API +--- + +# Users & Permissions REST API + +The Users & Permissions plugin exposes a set of REST API endpoints for authentication, user management, and role/permission management. These endpoints are separate from the standard content-type CRUD endpoints and have their own response shapes. For a general overview of the plugin's features and configuration, see the [Users & Permissions introduction](/cms/features/users-permissions). + +All endpoints use the `/api` prefix. For example, if your Strapi server runs at `http://localhost:1337`, the login endpoint is `http://localhost:1337/api/auth/local`. + +## Authentication + +Authentication endpoints handle login, registration, and password management. Most of these endpoints are public by default and do not require a Bearer token. + +### Login + +`POST /api/auth/local` + +Authenticates a user with their identifier (email or username) and password, returning a JWT and the user object. + +Request body: + +```json +{ + "identifier": "user@example.com", + "password": "yourPassword" +} +``` + +Example request: + +```bash +curl -X POST http://localhost:1337/api/auth/local \ + -H "Content-Type: application/json" \ + -d '{"identifier": "user@example.com", "password": "yourPassword"}' +``` + +Example response (200): + +```json +{ + "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "user": { + "id": 1, + "documentId": "x74detpqybxw0bn6ormua5g2", + "username": "testuser1", + "email": "user@example.com", + "provider": "local", + "confirmed": true, + "blocked": false, + "createdAt": "2024-03-15T10:00:00.000Z", + "updatedAt": "2024-03-15T10:00:00.000Z", + "publishedAt": "2024-03-15T10:00:00.000Z" + } +} +``` + +Possible errors: + +| Status | Message | Cause | +|--------|---------|-------| +| 400 | `"Invalid identifier or password"` | Wrong credentials | +| 400 | `"Your account email is not confirmed"` | Email confirmation required but not completed | +| 400 | `"Your account has been blocked by an administrator"` | Account blocked | + +This endpoint is rate limited (default: 10 requests per 60 seconds). + +### Register + +`POST /api/auth/local/register` + +Creates a new user account and returns a JWT along with the user object. + +Request body: + +```json +{ + "username": "newuser", + "email": "newuser@example.com", + "password": "Password123!" +} +``` + +Example request: + +```bash +curl -X POST http://localhost:1337/api/auth/local/register \ + -H "Content-Type: application/json" \ + -d '{"username": "newuser", "email": "newuser@example.com", "password": "Password123!"}' +``` + +Example response (200): + +```json +{ + "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "user": { + "id": 2, + "documentId": "xjpaytstw1gm7wdfoc6c2k13", + "username": "newuser", + "email": "newuser@example.com", + "provider": "local", + "confirmed": true, + "blocked": false, + "createdAt": "2024-03-15T10:00:00.000Z", + "updatedAt": "2024-03-15T10:00:00.000Z", + "publishedAt": "2024-03-15T10:00:00.000Z" + } +} +``` + +:::note +Only `username`, `email`, and `password` are accepted in the request body by default. To allow additional fields (e.g., a `fullName` field you added to the User content-type), you must explicitly list them in the `register.allowedFields` configuration. See [Registration configuration](/cms/features/users-permissions#registration-configuration) for details. + +When email confirmation is enabled, the response contains only the user object without a JWT -- the user must confirm their email before they can log in. + +The newly registered user is assigned the default role (typically "Authenticated"). +::: + +Possible errors: + +| Status | Message | Cause | +|--------|---------|-------| +| 400 | `"Email or Username are already taken"` | Duplicate email or username | +| 400 | `"Invalid parameters: fieldName"` | Extra fields not listed in `allowedFields` | +| 400 | `"Register action is currently disabled"` | Registration disabled in admin settings | + +This endpoint is rate limited. + +### Forgot password + +`POST /api/auth/forgot-password` + +Sends a password reset email to the specified address. Requires an email provider to be configured. + +Request body: + +```json +{ + "email": "user@example.com" +} +``` + +Example request: + +```bash +curl -X POST http://localhost:1337/api/auth/forgot-password \ + -H "Content-Type: application/json" \ + -d '{"email": "user@example.com"}' +``` + +Example response (200): + +```json +{ + "ok": true +} +``` + +:::note +This endpoint always returns `{ "ok": true }` regardless of whether the email address exists in the system. This is intentional to prevent user enumeration attacks. +::: + +This endpoint is rate limited. + +### Reset password + +`POST /api/auth/reset-password` + +Resets a user's password using a token received by email. All three fields are required. + +Request body: + +```json +{ + "code": "resetTokenFromEmail", + "password": "NewPassword123!", + "passwordConfirmation": "NewPassword123!" +} +``` + +Example request: + +```bash +curl -X POST http://localhost:1337/api/auth/reset-password \ + -H "Content-Type: application/json" \ + -d '{"code": "resetTokenFromEmail", "password": "NewPassword123!", "passwordConfirmation": "NewPassword123!"}' +``` + +Example response (200): Same shape as the [login](#login) response (jwt + user). + +Possible errors: + +| Status | Message | Cause | +|--------|---------|-------| +| 400 | `"passwordConfirmation is a required field"` | Missing `passwordConfirmation` | +| 400 | `"Incorrect code provided"` | Invalid or expired reset token | + +This endpoint is rate limited. + +### Change password + +`POST /api/auth/change-password` + +Changes the password for the currently authenticated user. Requires a valid Bearer token. All three fields are required. + +Request body: + +```json +{ + "currentPassword": "OldPassword123!", + "password": "NewPassword456!", + "passwordConfirmation": "NewPassword456!" +} +``` + +Example request: + +```bash +curl -X POST http://localhost:1337/api/auth/change-password \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" \ + -d '{"currentPassword": "OldPassword123!", "password": "NewPassword456!", "passwordConfirmation": "NewPassword456!"}' +``` + +Example response (200): Same shape as the [login](#login) response (jwt + user). + +Possible errors: + +| Status | Message | Cause | +|--------|---------|-------| +| 400 | `"passwordConfirmation is a required field"` | Missing `passwordConfirmation` | +| 400 | `"The provided current password is invalid"` | Wrong current password | +| 400 | New password must differ from current password | Same password reused | + +This endpoint is rate limited. + +### Email confirmation + +`GET /api/auth/email-confirmation` + +Confirms a user's email address using a token from the confirmation email. + +Query parameter: `confirmation` -- the token received in the confirmation email. + +Example request: + +```bash +curl -G http://localhost:1337/api/auth/email-confirmation \ + --data-urlencode "confirmation=confirmationTokenHere" +``` + +After confirming the email, Strapi redirects the user to the URL configured in the admin panel under Settings > Users & Permissions > Advanced Settings > "Redirection url". + +### Send email confirmation + +`POST /api/auth/send-email-confirmation` + +Resends the confirmation email to a user who has not yet confirmed their email address. + +Request body: + +```json +{ + "email": "user@example.com" +} +``` + +Example request: + +```bash +curl -X POST http://localhost:1337/api/auth/send-email-confirmation \ + -H "Content-Type: application/json" \ + -d '{"email": "user@example.com"}' +``` + +Example response (200): + +```json +{ + "email": "user@example.com", + "sent": true +} +``` + +Possible errors: + +| Status | Message | Cause | +|--------|---------|-------| +| 400 | `"Already confirmed"` | User already confirmed their email | +| 400 | `"Your account has been blocked by an administrator"` | Account blocked | + +:::caution +This endpoint is **not** rate limited. +::: + +### Provider authentication + +`GET /api/connect/:provider` + +Redirects the user to a third-party provider's login page (e.g., Google, GitHub, Discord). Replace `:provider` with the provider name. See [Setting up providers](/cms/configurations/users-and-permissions-providers) for configuration instructions. + +### Provider callback + +`GET /api/auth/:provider/callback` + +Handles the OAuth callback after the user authenticates with a third-party provider. On success, returns the same response shape as [login](#login) (jwt + user). + +## Session management endpoints + +When session management is enabled (`jwtManagement: 'refresh'` in the plugin configuration), these additional endpoints become available. They return 404 when the default legacy JWT mode is active. + +### Refresh token + +`POST /api/auth/refresh` + +Exchanges a refresh token for a new access token. The old refresh token is invalidated and a new one is returned (token rotation). + +Request body: + +```json +{ + "refreshToken": "yourRefreshToken" +} +``` + +Example request: + +```bash +curl -X POST http://localhost:1337/api/auth/refresh \ + -H "Content-Type: application/json" \ + -d '{"refreshToken": "yourRefreshToken"}' +``` + +Example response (200): + +```json +{ + "jwt": "newAccessToken" +} +``` + +### Logout + +`POST /api/auth/logout` + +Revokes all sessions for the authenticated user. Requires a valid Bearer token. + +Example request: + +```bash +curl -X POST http://localhost:1337/api/auth/logout \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +Example response (200): + +```json +{ + "ok": true +} +``` + +## Users + +User management endpoints handle CRUD operations on end-user records. Each endpoint requires the corresponding permission to be enabled for the user's role in the admin panel (Settings > Users & Permissions > Roles). + +:::caution Response format +User endpoints return bare JSON objects (not wrapped in a `data` key), unlike standard Strapi content-type endpoints. +::: + +### Get current user + +`GET /api/users/me` + +Returns the user associated with the provided JWT. Requires authentication. + +Example request: + +```bash +curl http://localhost:1337/api/users/me \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +Example response (200): + +```json +{ + "id": 1, + "documentId": "x74detpqybxw0bn6ormua5g2", + "username": "testuser1", + "email": "user@example.com", + "provider": "local", + "confirmed": true, + "blocked": false, + "createdAt": "2024-03-15T10:00:00.000Z", + "updatedAt": "2024-03-15T10:00:00.000Z", + "publishedAt": "2024-03-15T10:00:00.000Z" +} +``` + +### List users + +`GET /api/users` + +Returns a list of users. Supports `filters`, `sort`, and `pagination` query parameters. + +Example request: + +```bash +curl http://localhost:1337/api/users \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +Example response (200): An array of user objects. + +### Get a user + +`GET /api/users/:id` + +Returns a single user by their integer `id`. + +Example request: + +```bash +curl http://localhost:1337/api/users/1 \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +Example response (200): A user object. + +### Count users + +`GET /api/users/count` + +Returns the total number of users. Supports the `filters` query parameter. + +Example request: + +```bash +curl http://localhost:1337/api/users/count \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +Example response (200): A bare integer, for example: + +```json +42 +``` + +### Create a user + +`POST /api/users` + +Creates a new user. If `role` is omitted, the default role is assigned. + +Request body: + +```json +{ + "username": "newuser", + "email": "newuser@example.com", + "password": "Password123!", + "role": 1, + "confirmed": true +} +``` + +Example request: + +```bash +curl -X POST http://localhost:1337/api/users \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" \ + -d '{"username": "newuser", "email": "newuser@example.com", "password": "Password123!", "role": 1, "confirmed": true}' +``` + +Example response (201): A user object with the populated role. + +### Update a user + +`PUT /api/users/:id` + +Updates an existing user. Only include the fields you want to change. + +Example request: + +```bash +curl -X PUT http://localhost:1337/api/users/1 \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" \ + -d '{"username": "updateduser"}' +``` + +Example response (200): The updated user object. + +Possible errors: + +| Status | Message | Cause | +|--------|---------|-------| +| 404 | `"User not found"` | No user with the given `id` | +| 400 | `"Username already taken"` or `"Email already taken"` | Duplicate value | + +### Delete a user + +`DELETE /api/users/:id` + +Deletes a user by their integer `id`. + +Example request: + +```bash +curl -X DELETE http://localhost:1337/api/users/1 \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +Example response (200): The deleted user object. + +## Roles + +Role endpoints manage end-user roles and their associated permissions. These endpoints use the `/api/users-permissions/` prefix, not the standard `/api/` prefix used by content-type endpoints. + +### List roles + +`GET /api/users-permissions/roles` + +Returns all available roles with user counts. + +Example request: + +```bash +curl http://localhost:1337/api/users-permissions/roles \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +Example response (200): + +```json +{ + "roles": [ + { + "id": 1, + "name": "Authenticated", + "description": "Default role given to authenticated user.", + "type": "authenticated", + "nb_users": 2 + }, + { + "id": 2, + "name": "Public", + "description": "Default role given to unauthenticated user.", + "type": "public", + "nb_users": 0 + } + ] +} +``` + +### Get a role + +`GET /api/users-permissions/roles/:id` + +Returns a single role by `id`, including its full permissions tree. + +Example request: + +```bash +curl http://localhost:1337/api/users-permissions/roles/1 \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +Example response (200): A role object with a nested `permissions` structure mapping plugins to controllers to actions. + +### Create a role + +`POST /api/users-permissions/roles` + +Creates a new role. + +Request body: + +```json +{ + "name": "Editor", + "description": "Can edit content", + "type": "editor" +} +``` + +Example request: + +```bash +curl -X POST http://localhost:1337/api/users-permissions/roles \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" \ + -d '{"name": "Editor", "description": "Can edit content", "type": "editor"}' +``` + +Example response (200): + +```json +{ + "ok": true +} +``` + +### Update a role + +`PUT /api/users-permissions/roles/:id` + +Updates an existing role. + +Example request: + +```bash +curl -X PUT http://localhost:1337/api/users-permissions/roles/1 \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" \ + -d '{"description": "Updated description"}' +``` + +Example response (200): + +```json +{ + "ok": true +} +``` + +### Delete a role + +`DELETE /api/users-permissions/roles/:id` + +Deletes a role by `id`. The Public role cannot be deleted. When a role is deleted, users assigned to it are reassigned to the Public role. + +Example request: + +```bash +curl -X DELETE http://localhost:1337/api/users-permissions/roles/3 \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +Example response (200): + +```json +{ + "ok": true +} +``` + +## Permissions + +The permissions endpoint returns the complete permission tree, showing which actions are available across all plugins and content-types. + +### List permissions + +`GET /api/users-permissions/permissions` + +Returns a nested object mapping plugins to controllers to actions. + +Example request: + +```bash +curl http://localhost:1337/api/users-permissions/permissions \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +Example response (200): A nested `permissions` object where keys are plugin names, sub-keys are controller names, and values contain the available actions with their `enabled` status and `policy` information. From cca9cde55359f4040b27287abdbae2943a8035b7 Mon Sep 17 00:00:00 2001 From: Pierre Wizla <4233866+pwizla@users.noreply.github.com> Date: Tue, 12 May 2026 12:21:11 +0200 Subject: [PATCH 04/29] Add Users & Permissions GraphQL API reference page Co-Authored-By: Claude Opus 4.6 (1M context) --- .../features/users-permissions/graphql-api.md | 336 ++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 docusaurus/docs/cms/features/users-permissions/graphql-api.md diff --git a/docusaurus/docs/cms/features/users-permissions/graphql-api.md b/docusaurus/docs/cms/features/users-permissions/graphql-api.md new file mode 100644 index 0000000000..f13409d034 --- /dev/null +++ b/docusaurus/docs/cms/features/users-permissions/graphql-api.md @@ -0,0 +1,336 @@ +--- +title: Users & Permissions GraphQL API +description: GraphQL queries and mutations for authentication, user management, and role-based access with the Users & Permissions plugin. +displayed_sidebar: cmsSidebar +tags: + - users & permissions + - GraphQL API + - authentication + - API +--- + +# Users & Permissions GraphQL API + +This page documents all GraphQL queries and mutations provided by the Users & Permissions plugin. The [GraphQL plugin](/cms/plugins/graphql) must be installed. For configuration details, see the main [Users & Permissions page](/cms/features/users-permissions). + +## Authentication + +Authentication mutations handle login, registration, and password management. Public mutations do not require an Authorization header. + +### Login + +The `login` mutation authenticates a user and returns a JWT token: + +```graphql +mutation { + login(input: { identifier: "user@example.com", password: "yourPassword" }) { + jwt + user { + id + documentId + username + email + confirmed + blocked + } + } +} +``` + +Input type `UsersPermissionsLoginInput`: + +- `identifier` (String!) -- email or username +- `password` (String!) +- `provider` (String, defaults to "local") + +Returns `UsersPermissionsLoginPayload`: `jwt` (String), `user` (UsersPermissionsMe) + +Auth: Public + +### Register + +The `register` mutation creates a new user account and returns a JWT token: + +```graphql +mutation { + register(input: { username: "newuser", email: "new@example.com", password: "Password123!" }) { + jwt + user { + id + documentId + username + email + } + } +} +``` + +Input type `UsersPermissionsRegisterInput`: + +- `username` (String!) +- `email` (String!) +- `password` (String!) + +Returns `UsersPermissionsLoginPayload` + +Auth: Public + +:::note +Only `username`, `email`, and `password` are accepted by default. Additional fields require `register.allowedFields` configuration. +::: + +### Forgot password + +The `forgotPassword` mutation sends a password reset email to the user: + +```graphql +mutation { + forgotPassword(email: "user@example.com") { + ok + } +} +``` + +Input: `email` (String!) -- direct argument, not wrapped in an input type. + +Returns `UsersPermissionsPasswordPayload`: `ok` (Boolean!) + +Auth: Public + +### Reset password + +The `resetPassword` mutation sets a new password using the token received by email: + +```graphql +mutation { + resetPassword(code: "resetTokenFromEmail", password: "NewPassword123!", passwordConfirmation: "NewPassword123!") { + jwt + user { + id + username + email + } + } +} +``` + +Input (direct arguments): + +- `code` (String!) -- reset token from email +- `password` (String!) +- `passwordConfirmation` (String!) + +Returns `UsersPermissionsLoginPayload` + +Auth: Public + +### Change password + +The `changePassword` mutation updates the password for the currently authenticated user: + +```graphql +mutation { + changePassword(currentPassword: "OldPassword123!", password: "NewPassword456!", passwordConfirmation: "NewPassword456!") { + jwt + user { + id + username + email + } + } +} +``` + +Requires an Authorization header with a Bearer token. + +Input (direct arguments): + +- `currentPassword` (String!) +- `password` (String!) +- `passwordConfirmation` (String!) + +Returns `UsersPermissionsLoginPayload` + +Auth: Authenticated + +### Email confirmation + +The `emailConfirmation` mutation confirms a user's email address using the token received by email: + +```graphql +mutation { + emailConfirmation(confirmation: "confirmationTokenFromEmail") { + jwt + user { + id + username + email + confirmed + } + } +} +``` + +Input: `confirmation` (String!) + +Returns `UsersPermissionsLoginPayload` + +Auth: Public + +:::note +Unlike the REST equivalent (which redirects), the GraphQL mutation returns a JWT and user object directly. +::: + +## User queries and mutations + +These operations retrieve and manage user records. They require the corresponding permission to be enabled for the requesting user's role. + +### Get authenticated user (me query) + +The `me` query returns the profile of the currently authenticated user: + +```graphql +query { + me { + id + documentId + username + email + confirmed + blocked + role { + id + name + description + type + } + } +} +``` + +Requires an Authorization header. Returns `UsersPermissionsMe` type. + +### Create a user + +The `createUsersPermissionsUser` mutation creates a new user record: + +```graphql +mutation { + createUsersPermissionsUser(data: { username: "newuser", email: "new@example.com", password: "Password123!" }) { + data { + documentId + username + email + } + } +} +``` + +Input: `UsersPermissionsUserInput` (auto-generated from User content-type schema, with `password` field added) + +Auth: Requires `plugin::users-permissions.user.create` permission + +### Update a user + +The `updateUsersPermissionsUser` mutation updates an existing user record: + +```graphql +mutation { + updateUsersPermissionsUser(id: "documentId123", data: { username: "updatedname" }) { + data { + documentId + username + email + } + } +} +``` + +Auth: Requires `plugin::users-permissions.user.update` permission + +### Delete a user + +The `deleteUsersPermissionsUser` mutation removes a user record: + +```graphql +mutation { + deleteUsersPermissionsUser(id: "documentId123") { + data { + documentId + username + } + } +} +``` + +Auth: Requires `plugin::users-permissions.user.destroy` permission + +## Role mutations + +Role mutations manage end-user roles. They return `{ ok: true }` on success (not the role data). + +### Create a role + +The `createUsersPermissionsRole` mutation creates a new role: + +```graphql +mutation { + createUsersPermissionsRole(data: { name: "Editor", description: "Can edit content" }) { + ok + } +} +``` + +Auth: Requires `plugin::users-permissions.role.createRole` permission + +### Update a role + +The `updateUsersPermissionsRole` mutation updates an existing role: + +```graphql +mutation { + updateUsersPermissionsRole(id: "1", data: { name: "Senior Editor", description: "Can edit and publish" }) { + ok + } +} +``` + +Auth: Requires `plugin::users-permissions.role.updateRole` permission + +### Delete a role + +The `deleteUsersPermissionsRole` mutation removes a role: + +```graphql +mutation { + deleteUsersPermissionsRole(id: "3") { + ok + } +} +``` + +Auth: Requires `plugin::users-permissions.role.deleteRole` permission + +:::note +The Public role cannot be deleted. +::: + +## Types reference + +This section lists the key GraphQL types used across Users & Permissions operations. + +### Input types + +| Type | Fields | Used by | +|------|--------|---------| +| `UsersPermissionsLoginInput` | `identifier` (String!), `password` (String!), `provider` (String) | `login` mutation | +| `UsersPermissionsRegisterInput` | `username` (String!), `email` (String!), `password` (String!) | `register` mutation | + +### Response types + +| Type | Fields | Used by | +|------|--------|---------| +| `UsersPermissionsLoginPayload` | `jwt` (String), `user` (UsersPermissionsMe!) | `login`, `register`, `resetPassword`, `changePassword`, `emailConfirmation` | +| `UsersPermissionsPasswordPayload` | `ok` (Boolean!) | `forgotPassword` | +| `UsersPermissionsMe` | `id` (ID!), `documentId` (ID!), `username` (String!), `email` (String), `confirmed` (Boolean), `blocked` (Boolean), `role` (UsersPermissionsMeRole) | `me` query, nested in login payload | +| `UsersPermissionsMeRole` | `id` (ID!), `name` (String!), `description` (String), `type` (String) | Nested in `UsersPermissionsMe` | From 9be3d79946cfcf701783a8da3f328b9363ddf6e4 Mon Sep 17 00:00:00 2001 From: Pierre Wizla <4233866+pwizla@users.noreply.github.com> Date: Tue, 12 May 2026 14:03:12 +0200 Subject: [PATCH 05/29] Fix provider pages: copy-paste errors, deprecation warnings Co-Authored-By: Claude Opus 4.6 (1M context) --- .../users-and-permissions-providers/auth-zero.md | 2 +- .../configurations/users-and-permissions-providers/cas.md | 4 ++-- .../users-and-permissions-providers/instagram.md | 4 ++++ .../users-and-permissions-providers/linkedin.md | 6 +++++- .../users-and-permissions-providers/twitter.md | 4 ++++ 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/docusaurus/docs/cms/configurations/users-and-permissions-providers/auth-zero.md b/docusaurus/docs/cms/configurations/users-and-permissions-providers/auth-zero.md index c899dd9a6b..9030201276 100644 --- a/docusaurus/docs/cms/configurations/users-and-permissions-providers/auth-zero.md +++ b/docusaurus/docs/cms/configurations/users-and-permissions-providers/auth-zero.md @@ -22,7 +22,7 @@ You have read the [Users & Permissions providers documentation](/cms/configurati ## Auth0 configuration :::note -AWS Cognito accepts the `localhost` urls.
+Auth0 accepts the `localhost` urls.
The use of `ngrok` is not needed. ::: diff --git a/docusaurus/docs/cms/configurations/users-and-permissions-providers/cas.md b/docusaurus/docs/cms/configurations/users-and-permissions-providers/cas.md index 041bcd0c46..cca9de8a71 100644 --- a/docusaurus/docs/cms/configurations/users-and-permissions-providers/cas.md +++ b/docusaurus/docs/cms/configurations/users-and-permissions-providers/cas.md @@ -13,7 +13,7 @@ import ConfigDone from '/docs/snippets/u-and-p-provider-config-done.md' # CAS provider setup for Users & Permissions -The present page explains how to setup the Auth0 provider for the [Users & Permissions feature](/cms/features/users-permissions). +The present page explains how to setup the CAS provider for the [Users & Permissions feature](/cms/features/users-permissions). :::prerequisites You have read the [Users & Permissions providers documentation](/cms/configurations/users-and-permissions-providers). @@ -58,7 +58,7 @@ The use of `ngrok` is not needed. ## Strapi configuration -1. Visit the User & Permissions provider settings page at +1. Visit the User & Permissions provider settings page at 2. Click on the **CAS** provider 3. Fill the information: - **Enable**: `ON` diff --git a/docusaurus/docs/cms/configurations/users-and-permissions-providers/instagram.md b/docusaurus/docs/cms/configurations/users-and-permissions-providers/instagram.md index 034f6009a4..9e1ed55a1c 100644 --- a/docusaurus/docs/cms/configurations/users-and-permissions-providers/instagram.md +++ b/docusaurus/docs/cms/configurations/users-and-permissions-providers/instagram.md @@ -19,6 +19,10 @@ The present page explains how to setup the Instagram provider for the [Users & P You have read the [Users & Permissions providers documentation](/cms/configurations/users-and-permissions-providers). ::: +:::caution +The Instagram Basic Display API was deprecated and shut down by Meta on December 4, 2024. This provider may no longer work as documented below. +::: + ## Instagram configuration :::note diff --git a/docusaurus/docs/cms/configurations/users-and-permissions-providers/linkedin.md b/docusaurus/docs/cms/configurations/users-and-permissions-providers/linkedin.md index 5bcf35057a..f8668f83e2 100644 --- a/docusaurus/docs/cms/configurations/users-and-permissions-providers/linkedin.md +++ b/docusaurus/docs/cms/configurations/users-and-permissions-providers/linkedin.md @@ -19,6 +19,10 @@ The present page explains how to setup the LinkedIn provider for the [Users & Pe You have read the [Users & Permissions providers documentation](/cms/configurations/users-and-permissions-providers). ::: +:::caution +LinkedIn has migrated from "Sign In with LinkedIn" to "Sign In with LinkedIn using OpenID Connect." The `r_liteprofile` and `r_emailaddress` scopes have been deprecated. The setup steps below may need adjustments to match the new OpenID Connect product. +::: + ## LinkedIn configuration :::note @@ -37,7 +41,7 @@ The use of `ngrok` is not needed. 6. Fill the information: - **Authorized redirect URL**: `http://localhost:1337/api/connect/linkedin/callback` 7. On the app page click on **Products** tab. -8. Select `Sign In with LinkedIn` from the product list to enable it. +8. Select `Sign In with LinkedIn using OpenID Connect` from the product list to enable it. ## Strapi configuration diff --git a/docusaurus/docs/cms/configurations/users-and-permissions-providers/twitter.md b/docusaurus/docs/cms/configurations/users-and-permissions-providers/twitter.md index b21329e45e..88a93544cf 100644 --- a/docusaurus/docs/cms/configurations/users-and-permissions-providers/twitter.md +++ b/docusaurus/docs/cms/configurations/users-and-permissions-providers/twitter.md @@ -19,6 +19,10 @@ The present page explains how to setup the Twitter provider for the [Users & Per You have read the [Users & Permissions providers documentation](/cms/configurations/users-and-permissions-providers). ::: +:::caution +Twitter has rebranded to X. The developer portal has been significantly restructured, and free-tier API access is now limited. The setup steps and URLs below may be outdated. +::: + ## Twitter configuration :::note From bcebacc1c17005604bb759630759854d0ff880d0 Mon Sep 17 00:00:00 2001 From: Pierre Wizla <4233866+pwizla@users.noreply.github.com> Date: Tue, 12 May 2026 14:25:44 +0200 Subject: [PATCH 06/29] Fix secondary docs: wrong defaults, strapi scoping, actionable breaking change Co-Authored-By: Claude Opus 4.6 (1M context) --- .../examples/authentication.md | 12 ++++++----- ...omizing-users-permissions-plugin-routes.md | 8 ++++---- .../register-allowed-fields.md | 20 +++++++++++++++++++ 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/docusaurus/docs/cms/backend-customization/examples/authentication.md b/docusaurus/docs/cms/backend-customization/examples/authentication.md index a7b38c5d60..d9652b72fd 100644 --- a/docusaurus/docs/cms/backend-customization/examples/authentication.md +++ b/docusaurus/docs/cms/backend-customization/examples/authentication.md @@ -99,7 +99,8 @@ const Login = () => { body: JSON.stringify(values), }); /** - * Gets the JWT from the server response + * Gets the JWT from the server response. + * The actual response is { jwt, user }, but we only need the JWT here. */ const { jwt } = await res.json(); /** @@ -159,9 +160,11 @@ module.exports = ({ env }) => ({ config: { jwtManagement: 'refresh', sessions: { - accessTokenLifespan: 604800, // 1 week (default) - maxRefreshTokenLifespan: 2592000, // 30 days - idleRefreshTokenLifespan: 604800, // 7 days + accessTokenLifespan: 600, // 10 minutes (default) + maxRefreshTokenLifespan: 2592000, // 30 days (default) + idleRefreshTokenLifespan: 1209600, // 14 days (default) + maxSessionLifespan: 86400, // 1 day (default) + idleSessionLifespan: 7200, // 2 hours (default) }, }, }, @@ -257,7 +260,6 @@ const EnhancedLogin = () => { export default EnhancedLogin; ``` -```
diff --git a/docusaurus/docs/cms/backend-customization/guides/customizing-users-permissions-plugin-routes.md b/docusaurus/docs/cms/backend-customization/guides/customizing-users-permissions-plugin-routes.md index 402f8a8ef1..894042fe58 100644 --- a/docusaurus/docs/cms/backend-customization/guides/customizing-users-permissions-plugin-routes.md +++ b/docusaurus/docs/cms/backend-customization/guides/customizing-users-permissions-plugin-routes.md @@ -302,7 +302,7 @@ You can add custom routes to the Users & Permissions plugin. For example, add an ```js title="/src/extensions/users-permissions/strapi-server.js" -module.exports = (plugin) => { +module.exports = (plugin, { strapi }) => { // Add a new controller action plugin.controllers.user.deactivate = async (ctx) => { const { id } = ctx.params; @@ -335,7 +335,7 @@ module.exports = (plugin) => { ```ts title="/src/extensions/users-permissions/strapi-server.ts" -export default (plugin) => { +export default (plugin, { strapi }) => { // Add a new controller action plugin.controllers.user.deactivate = async (ctx) => { const { id } = ctx.params; @@ -557,7 +557,7 @@ The following example combines several customizations in a single file: it adds ```js title="/src/extensions/users-permissions/strapi-server.js" -module.exports = (plugin) => { +module.exports = (plugin, { strapi }) => { const routes = plugin.routes['content-api'].routes; // 1. Add 'is-own-user' policy to update and delete @@ -616,7 +616,7 @@ module.exports = (plugin) => { ```ts title="/src/extensions/users-permissions/strapi-server.ts" -export default (plugin) => { +export default (plugin, { strapi }) => { const routes = plugin.routes['content-api'].routes; // 1. Add 'is-own-user' policy to update and delete diff --git a/docusaurus/docs/cms/migration/v4-to-v5/breaking-changes/register-allowed-fields.md b/docusaurus/docs/cms/migration/v4-to-v5/breaking-changes/register-allowed-fields.md index 2cdb374070..817f090372 100644 --- a/docusaurus/docs/cms/migration/v4-to-v5/breaking-changes/register-allowed-fields.md +++ b/docusaurus/docs/cms/migration/v4-to-v5/breaking-changes/register-allowed-fields.md @@ -56,3 +56,23 @@ An undefined `allowedFields` is treated as an empty array, and no fields are acc ### Manual procedure A codemod should handle this migration. If not, please refer to the documentation on how to [register allowed fields for the Users & Permissions plugin](/cms/features/users-permissions#registration-configuration). + +#### How it works in Strapi 5 + +The fields `username`, `email`, and `password` are always accepted by the registration endpoint -- they are hardcoded as `alwaysAllowedKeys` in the Users & Permissions plugin source. Any other field on the User content-type must be explicitly listed in `allowedFields` or the request will be rejected with a `400` status and an error message like `"Invalid parameters: fieldName"`. + +To allow additional fields on registration, update your plugin configuration: + +```js title="config/plugins.js" +module.exports = { + 'users-permissions': { + config: { + register: { + allowedFields: ['firstName', 'lastName'], + }, + }, + }, +}; +``` + +If you were relying on the Strapi v4 behavior where all fields were accepted by default, list every extra field you need in `allowedFields`. Fields not listed will be silently dropped or rejected. From 758e7140dd147c48d2c0069a99dd656634c3c2c1 Mon Sep 17 00:00:00 2001 From: Pierre Wizla <4233866+pwizla@users.noreply.github.com> Date: Tue, 12 May 2026 15:15:33 +0200 Subject: [PATCH 07/29] Regenerate llms files with new U&P pages Co-Authored-By: Claude Opus 4.6 (1M context) --- docusaurus/static/llms-code.txt | 1107 +++++++++++++++++++++++-------- docusaurus/static/llms-full.txt | 1042 +++++++++++++++++++++++++---- docusaurus/static/llms.txt | 3 +- 3 files changed, 1740 insertions(+), 412 deletions(-) diff --git a/docusaurus/static/llms-code.txt b/docusaurus/static/llms-code.txt index 53753d111c..616eb80651 100644 --- a/docusaurus/static/llms-code.txt +++ b/docusaurus/static/llms-code.txt @@ -11439,7 +11439,8 @@ const Login = () => { body: JSON.stringify(values), }); /** - * Gets the JWT from the server response + * Gets the JWT from the server response. + * The actual response is { jwt, user }, but we only need the JWT here. */ const { jwt } = await res.json(); /** @@ -11499,9 +11500,11 @@ module.exports = ({ env }) => ({ config: { jwtManagement: 'refresh', sessions: { - accessTokenLifespan: 604800, // 1 week (default) - maxRefreshTokenLifespan: 2592000, // 30 days - idleRefreshTokenLifespan: 604800, // 7 days + accessTokenLifespan: 600, // 10 minutes (default) + maxRefreshTokenLifespan: 2592000, // 30 days (default) + idleRefreshTokenLifespan: 1209600, // 14 days (default) + maxSessionLifespan: 86400, // 1 day (default) + idleSessionLifespan: 7200, // 2 hours (default) }, }, }, @@ -12519,7 +12522,7 @@ Language: JavaScript File path: /src/extensions/users-permissions/strapi-server.js ```js -module.exports = (plugin) => { +module.exports = (plugin, { strapi }) => { // Add a new controller action plugin.controllers.user.deactivate = async (ctx) => { const { id } = ctx.params; @@ -12552,7 +12555,7 @@ Language: TypeScript File path: /src/extensions/users-permissions/strapi-server.ts ```ts -export default (plugin) => { +export default (plugin, { strapi }) => { // Add a new controller action plugin.controllers.user.deactivate = async (ctx) => { const { id } = ctx.params; @@ -12737,7 +12740,7 @@ Language: JavaScript File path: /src/extensions/users-permissions/strapi-server.js ```js -module.exports = (plugin) => { +module.exports = (plugin, { strapi }) => { const routes = plugin.routes['content-api'].routes; // 1. Add 'is-own-user' policy to update and delete @@ -12796,7 +12799,7 @@ Language: TypeScript File path: /src/extensions/users-permissions/strapi-server.ts ```ts -export default (plugin) => { +export default (plugin, { strapi }) => { const routes = plugin.routes['content-api'].routes; // 1. Add 'is-own-user' policy to update and delete @@ -24015,9 +24018,11 @@ module.exports = ({ env }) => ({ config: { jwtManagement: 'refresh', sessions: { - accessTokenLifespan: 604800, // 1 week (default) + accessTokenLifespan: 600, // 10 minutes (default) maxRefreshTokenLifespan: 2592000, // 30 days - idleRefreshTokenLifespan: 604800, // 7 days + idleRefreshTokenLifespan: 1209600, // 14 days + maxSessionLifespan: 86400, // 1 day (default) + idleSessionLifespan: 7200, // 2 hours (default) httpOnly: false, // Set to true for HTTP-only cookies cookie: { name: 'strapi_up_refresh', @@ -24044,9 +24049,11 @@ export default ({ env }) => ({ config: { jwtManagement: 'refresh', sessions: { - accessTokenLifespan: 604800, // 1 week (default) + accessTokenLifespan: 600, // 10 minutes (default) maxRefreshTokenLifespan: 2592000, // 30 days - idleRefreshTokenLifespan: 604800, // 7 days + idleRefreshTokenLifespan: 1209600, // 14 days + maxSessionLifespan: 86400, // 1 day (default) + idleSessionLifespan: 7200, // 2 hours (default) httpOnly: false, // Set to true for HTTP-only cookies cookie: { name: 'strapi_up_refresh', @@ -24070,7 +24077,7 @@ module.exports = ({ env }) => ({ config: { jwtManagement: 'legacy-support', jwt: { - expiresIn: '7d', // Traditional JWT expiry + expiresIn: '30d', // Traditional JWT expiry }, }, }, @@ -24119,7 +24126,7 @@ export default ({ env }) => ({ ## Rate limiting configuration -Description: | ratelimit | Settings to customize the rate limiting of the authentications and registration endpoints | object | {} | | ratelimit.enabled | Enable or disable the rate limiter | boolean | true | | ratelimit.interval | Time window for requests to be considered as part of the same rate limiting bucket | object | { min: 5 } | | ratelimit.max | Maximum number of requests allowed in the time window | integer | 5 | | ratelimit.prefixKey | Prefix for the rate limiting key | string | ${userIdentifier}:${requestPath}:${ctx.request.ip} | +Description: | ratelimit | Settings to customize the rate limiting of the authentications and registration endpoints | object | {} | | ratelimit.enabled | Enable or disable the rate limiter | boolean | true | | ratelimit.interval | Time window for requests to be considered as part of the same rate limiting bucket (in milliseconds) | integer | 60000 (1 minute) | | ratelimit.max | Maximum number of requests allowed in the time window | integer | 10 | | ratelimit.prefixKey | Prefix for the rate limiting key | string | ${userIdentifier}:${requestPath}:${ctx.request.ip} | (Source: https://docs.strapi.io/cms/features/users-permissions#rate-limiting-configuration) Language: JavaScript @@ -24133,8 +24140,8 @@ module.exports = ({ env }) => ({ config: { ratelimit: { enabled: true, - interval: { min: 5 }, - max: 5, + interval: 60000, // 1 minute + max: 10, }, }, }, @@ -24154,8 +24161,8 @@ export default ({ env }) => ({ config: { ratelimit: { enabled: true, - interval: { min: 5 }, - max: 5, + interval: 60000, // 1 minute + max: 10, }, }, }, @@ -24165,7 +24172,7 @@ export default ({ env }) => ({ ## Security configuration -Description: By default you can set a JWT_SECRET environment variable and it will be used as secret. +Description: If you want to use a different environment variable, you can update the configuration file. (Source: https://docs.strapi.io/cms/features/users-permissions#security-configuration) Language: JavaScript @@ -24226,376 +24233,876 @@ File path: /config/plugins.js|ts ``` -## Session management endpoints -Description: Code example from "Session management endpoints" -(Source: https://docs.strapi.io/cms/features/users-permissions#session-management-endpoints) +## Token usage +Description: The jwt received when logging in or registering may then be used for making permission-restricted API requests. +(Source: https://docs.strapi.io/cms/features/users-permissions#token-usage) Language: JavaScript File path: N/A -``` -curl -X POST http://localhost:1337/api/auth/refresh \ - -H "Content-Type: application/json" \ - -d '{ - "refreshToken": "your-refresh-token" - }' -``` - ---- -Language: JSON -File path: N/A - -```json -{ - "jwt": "your-new-access-token" -} -``` +```js +import axios from 'axios'; ---- -Language: Bash -File path: N/A +const token = 'YOUR_TOKEN_HERE'; -```bash -curl -X POST http://localhost:1337/api/auth/logout \ - -H "Authorization: Bearer your-access-token" +// Request API. +axios + .get('http://localhost:1337/api/posts', { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then(response => { + // Handle success. + console.log('Data: ', response.data); + }) + .catch(error => { + // Handle error. + console.log('An error occurred:', error.response); + }); ``` -## Get the authenticated user -Description: Code example from "Get the authenticated user" -(Source: https://docs.strapi.io/cms/features/users-permissions#get-the-authenticated-user) +## User object in Strapi context +Description: The authenticated user object is a property of ctx.state, as shown in the following example: +(Source: https://docs.strapi.io/cms/features/users-permissions#user-object-in-strapi-context) -Language: Bash +Language: JavaScript File path: N/A -```bash -curl -X GET http://localhost:1337/api/users/me \ - -H "Authorization: Bearer your-access-token" -``` +```js +create: async ctx => { + const { id } = ctx.state.user; ---- -Language: JSON -File path: N/A + const depositObj = { + ...ctx.request.body, + depositor: id, + }; -```json -{ - "id": 1, - "documentId": "abc123", - "username": "kai", - "email": "kai@strapi.io", - "provider": "local", - "confirmed": true, - "blocked": false, - "createdAt": "2024-01-15T09:00:00.000Z", - "updatedAt": "2024-01-15T09:00:00.000Z", - "publishedAt": "2024-01-15T09:00:00.000Z" -} + const data = await strapi.services.deposit.add(depositObj); + + // Send 201 `created` + ctx.created(data); +}; ``` -## Find all users -Description: Code example from "Find all users" -(Source: https://docs.strapi.io/cms/features/users-permissions#find-all-users) -Language: Bash -File path: N/A +# Users & Permissions GraphQL API +Source: https://docs.strapi.io/cms/features/users-permissions/graphql-api -```bash -curl -X GET "http://localhost:1337/api/users?populate=role" \ - -H "Authorization: Bearer your-access-token" -``` +## Login +Description: The login mutation authenticates a user and returns a JWT token: +(Source: https://docs.strapi.io/cms/features/users-permissions/graphql-api#login) ---- -Language: JSON +Language: DOCKERFILE File path: N/A -```json -[ - { - "id": 1, - "documentId": "abc123", - "username": "kai", - "email": "kai@strapi.io", - "provider": "local", - "confirmed": true, - "blocked": false, - "role": { - "id": 1, - "name": "Authenticated", - "description": "Default role given to authenticated user.", - "type": "authenticated", - "createdAt": "2024-01-01T00:00:00.000Z", - "updatedAt": "2024-01-01T00:00:00.000Z" - }, - "createdAt": "2024-01-15T09:00:00.000Z", - "updatedAt": "2024-01-15T09:00:00.000Z", - "publishedAt": "2024-01-15T09:00:00.000Z" +```dockerfile +mutation { + login(input: { identifier: "user@example.com", password: "yourPassword" }) { + jwt + user { + id + documentId + username + email + confirmed + blocked + } } -] +} ``` -## Create a user -Description: Code example from "Create a user" -(Source: https://docs.strapi.io/cms/features/users-permissions#create-a-user) +## Register +Description: The register mutation creates a new user account and returns a JWT token: +(Source: https://docs.strapi.io/cms/features/users-permissions/graphql-api#register) -Language: Bash +Language: DOCKERFILE File path: N/A -```bash -curl -X POST http://localhost:1337/api/users \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer your-access-token" \ - -d '{ - "username": "newuser", - "email": "newuser@strapi.io", - "password": "Password123", - "role": 1, - "confirmed": true - }' +```dockerfile +mutation { + register(input: { username: "newuser", email: "new@example.com", password: "Password123!" }) { + jwt + user { + id + documentId + username + email + } + } +} ``` ---- -Language: JSON + +## Forgot password +Description: The forgotPassword mutation sends a password reset email to the user: +(Source: https://docs.strapi.io/cms/features/users-permissions/graphql-api#forgot-password) + +Language: GRAPHQL File path: N/A -```json -{ - "id": 2, - "documentId": "def456", - "username": "newuser", - "email": "newuser@strapi.io", - "provider": "local", - "confirmed": true, - "blocked": false, - "createdAt": "2024-01-16T10:00:00.000Z", - "updatedAt": "2024-01-16T10:00:00.000Z", - "publishedAt": "2024-01-16T10:00:00.000Z" +```graphql +mutation { + forgotPassword(email: "user@example.com") { + ok + } } ``` -## Update a user -Description: Code example from "Update a user" -(Source: https://docs.strapi.io/cms/features/users-permissions#update-a-user) +## Reset password +Description: The resetPassword mutation sets a new password using the token received by email: +(Source: https://docs.strapi.io/cms/features/users-permissions/graphql-api#reset-password) -Language: Bash +Language: DOCKERFILE File path: N/A -```bash -curl -X PUT http://localhost:1337/api/users/2 \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer your-access-token" \ - -d '{ - "username": "updateduser" - }' +```dockerfile +mutation { + resetPassword(code: "resetTokenFromEmail", password: "NewPassword123!", passwordConfirmation: "NewPassword123!") { + jwt + user { + id + username + email + } + } +} ``` ---- -Language: JSON + +## Change password +Description: The changePassword mutation updates the password for the currently authenticated user: +(Source: https://docs.strapi.io/cms/features/users-permissions/graphql-api#change-password) + +Language: DOCKERFILE File path: N/A -```json -{ - "id": 2, - "documentId": "def456", - "username": "updateduser", - "email": "newuser@strapi.io", - "provider": "local", - "confirmed": true, - "blocked": false, - "createdAt": "2024-01-16T10:00:00.000Z", - "updatedAt": "2024-01-17T11:00:00.000Z", - "publishedAt": "2024-01-16T10:00:00.000Z" +```dockerfile +mutation { + changePassword(currentPassword: "OldPassword123!", password: "NewPassword456!", passwordConfirmation: "NewPassword456!") { + jwt + user { + id + username + email + } + } } ``` -## Delete a user -Description: Code example from "Delete a user" -(Source: https://docs.strapi.io/cms/features/users-permissions#delete-a-user) +## Email confirmation +Description: The emailConfirmation mutation confirms a user's email address using the token received by email: +(Source: https://docs.strapi.io/cms/features/users-permissions/graphql-api#email-confirmation) -Language: Bash +Language: DOCKERFILE File path: N/A -```bash -curl -X DELETE http://localhost:1337/api/users/2 \ - -H "Authorization: Bearer your-access-token" +```dockerfile +mutation { + emailConfirmation(confirmation: "confirmationTokenFromEmail") { + jwt + user { + id + username + email + confirmed + } + } +} ``` ---- -Language: JSON + +## Get authenticated user (me query) +Description: The me query returns the profile of the currently authenticated user: +(Source: https://docs.strapi.io/cms/features/users-permissions/graphql-api#get-authenticated-user-me-query) + +Language: GRAPHQL File path: N/A -```json -{ - "id": 2, - "documentId": "def456", - "username": "updateduser", - "email": "newuser@strapi.io", - "provider": "local", - "confirmed": true, - "blocked": false, - "createdAt": "2024-01-16T10:00:00.000Z", - "updatedAt": "2024-01-17T11:00:00.000Z", - "publishedAt": "2024-01-16T10:00:00.000Z" +```graphql +query { + me { + id + documentId + username + email + confirmed + blocked + role { + id + name + description + type + } + } } ``` -## Identifier -Description: The identifier parameter sent with requests can be an email or username, as in the following examples: -(Source: https://docs.strapi.io/cms/features/users-permissions#identifier) +## Create a user +Description: The createUsersPermissionsUser mutation creates a new user record: +(Source: https://docs.strapi.io/cms/features/users-permissions/graphql-api#create-a-user) -Language: JavaScript +Language: GRAPHQL File path: N/A -```js -import axios from 'axios'; - -// Request API. -axios - .post('http://localhost:1337/api/auth/local', { - identifier: 'user@strapi.io', - password: 'strapiPassword', - }) - .then(response => { - // Handle success. - console.log('Well done!'); - console.log('User profile', response.data.user); - console.log('User token', response.data.jwt); - }) - .catch(error => { - // Handle error. - console.log('An error occurred:', error.response); - }); +```graphql +mutation { + createUsersPermissionsUser(data: { username: "newuser", email: "new@example.com", password: "Password123!" }) { + data { + documentId + username + email + } + } +} ``` ---- -Language: JSON -File path: N/A -```json -{ - "identifier": "user@strapi.io", - "password": "strapiPassword" -} -``` +## Update a user +Description: The updateUsersPermissionsUser mutation updates an existing user record: +(Source: https://docs.strapi.io/cms/features/users-permissions/graphql-api#update-a-user) ---- -Language: JSON +Language: GRAPHQL File path: N/A -```json -{ - "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNTc2OTM4MTUwLCJleHAiOjE1Nzk1MzAxNTB9.UgsjjXkAZ-anD257BF7y1hbjuY3ogNceKfTAQtzDEsU", - "user": { - "id": 1, - "username": "user", - ... +```graphql +mutation { + updateUsersPermissionsUser(id: "documentId123", data: { username: "updatedname" }) { + data { + documentId + username + email } + } } ``` ---- -Language: JSON + +## Delete a user +Description: The deleteUsersPermissionsUser mutation removes a user record: +(Source: https://docs.strapi.io/cms/features/users-permissions/graphql-api#delete-a-user) + +Language: GRAPHQL File path: N/A -```json -{ - "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", // Short-lived access token - "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", // Long-lived refresh token - "user": { - "id": 1, - "username": "user", - ... +```graphql +mutation { + deleteUsersPermissionsUser(id: "documentId123") { + data { + documentId + username } + } } ``` -## Token usage -Description: The token variable is the data.jwt received when logging in or registering. -(Source: https://docs.strapi.io/cms/features/users-permissions#token-usage) +## Create a role +Description: The createUsersPermissionsRole mutation creates a new role: +(Source: https://docs.strapi.io/cms/features/users-permissions/graphql-api#create-a-role) -Language: JavaScript +Language: GRAPHQL File path: N/A -```js -import axios from 'axios'; +```graphql +mutation { + createUsersPermissionsRole(data: { name: "Editor", description: "Can edit content" }) { + ok + } +} +``` -const token = 'YOUR_TOKEN_HERE'; -// Request API. -axios - .get('http://localhost:1337/posts', { - headers: { - Authorization: `Bearer ${token}`, +## Update a role +Description: The updateUsersPermissionsRole mutation updates an existing role: +(Source: https://docs.strapi.io/cms/features/users-permissions/graphql-api#update-a-role) + +Language: GRAPHQL +File path: N/A + +```graphql +mutation { + updateUsersPermissionsRole(id: "1", data: { name: "Senior Editor", description: "Can edit and publish" }) { + ok + } +} +``` + + +## Delete a role +Description: The deleteUsersPermissionsRole mutation removes a role: +(Source: https://docs.strapi.io/cms/features/users-permissions/graphql-api#delete-a-role) + +Language: GRAPHQL +File path: N/A + +```graphql +mutation { + deleteUsersPermissionsRole(id: "3") { + ok + } +} +``` + + + +# Users & Permissions REST API +Source: https://docs.strapi.io/cms/features/users-permissions/rest-api + +## Login +Description: Request body: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#login) + +Language: JSON +File path: N/A + +```json +{ + "identifier": "user@example.com", + "password": "yourPassword" +} +``` + +Language: Bash +File path: N/A + +```bash +curl -X POST http://localhost:1337/api/auth/local \ + -H "Content-Type: application/json" \ + -d '{"identifier": "user@example.com", "password": "yourPassword"}' +``` + +Language: JSON +File path: N/A + +```json +{ + "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "user": { + "id": 1, + "documentId": "x74detpqybxw0bn6ormua5g2", + "username": "testuser1", + "email": "user@example.com", + "provider": "local", + "confirmed": true, + "blocked": false, + "createdAt": "2024-03-15T10:00:00.000Z", + "updatedAt": "2024-03-15T10:00:00.000Z", + "publishedAt": "2024-03-15T10:00:00.000Z" + } +} +``` + + +## Register +Description: Request body: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#register) + +Language: JSON +File path: N/A + +```json +{ + "username": "newuser", + "email": "newuser@example.com", + "password": "Password123!" +} +``` + +Language: Bash +File path: N/A + +```bash +curl -X POST http://localhost:1337/api/auth/local/register \ + -H "Content-Type: application/json" \ + -d '{"username": "newuser", "email": "newuser@example.com", "password": "Password123!"}' +``` + +Language: JSON +File path: N/A + +```json +{ + "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "user": { + "id": 2, + "documentId": "xjpaytstw1gm7wdfoc6c2k13", + "username": "newuser", + "email": "newuser@example.com", + "provider": "local", + "confirmed": true, + "blocked": false, + "createdAt": "2024-03-15T10:00:00.000Z", + "updatedAt": "2024-03-15T10:00:00.000Z", + "publishedAt": "2024-03-15T10:00:00.000Z" + } +} +``` + + +## Forgot password +Description: Request body: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#forgot-password) + +Language: JSON +File path: N/A + +```json +{ + "email": "user@example.com" +} +``` + +Language: Bash +File path: N/A + +```bash +curl -X POST http://localhost:1337/api/auth/forgot-password \ + -H "Content-Type: application/json" \ + -d '{"email": "user@example.com"}' +``` + +Language: JSON +File path: N/A + +```json +{ + "ok": true +} +``` + + +## Reset password +Description: Request body: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#reset-password) + +Language: JSON +File path: N/A + +```json +{ + "code": "resetTokenFromEmail", + "password": "NewPassword123!", + "passwordConfirmation": "NewPassword123!" +} +``` + +Language: Bash +File path: N/A + +```bash +curl -X POST http://localhost:1337/api/auth/reset-password \ + -H "Content-Type: application/json" \ + -d '{"code": "resetTokenFromEmail", "password": "NewPassword123!", "passwordConfirmation": "NewPassword123!"}' +``` + + +## Change password +Description: Request body: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#change-password) + +Language: JSON +File path: N/A + +```json +{ + "currentPassword": "OldPassword123!", + "password": "NewPassword456!", + "passwordConfirmation": "NewPassword456!" +} +``` + +Language: Bash +File path: N/A + +```bash +curl -X POST http://localhost:1337/api/auth/change-password \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" \ + -d '{"currentPassword": "OldPassword123!", "password": "NewPassword456!", "passwordConfirmation": "NewPassword456!"}' +``` + + +## Email confirmation +Description: Example request: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#email-confirmation) + +Language: Bash +File path: N/A + +```bash +curl -G http://localhost:1337/api/auth/email-confirmation \ + --data-urlencode "confirmation=confirmationTokenHere" +``` + + +## Send email confirmation +Description: Request body: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#send-email-confirmation) + +Language: JSON +File path: N/A + +```json +{ + "email": "user@example.com" +} +``` + +Language: Bash +File path: N/A + +```bash +curl -X POST http://localhost:1337/api/auth/send-email-confirmation \ + -H "Content-Type: application/json" \ + -d '{"email": "user@example.com"}' +``` + +Language: JSON +File path: N/A + +```json +{ + "email": "user@example.com", + "sent": true +} +``` + + +## Refresh token +Description: Request body: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#refresh-token) + +Language: JSON +File path: N/A + +```json +{ + "refreshToken": "yourRefreshToken" +} +``` + +Language: Bash +File path: N/A + +```bash +curl -X POST http://localhost:1337/api/auth/refresh \ + -H "Content-Type: application/json" \ + -d '{"refreshToken": "yourRefreshToken"}' +``` + +Language: JSON +File path: N/A + +```json +{ + "jwt": "newAccessToken" +} +``` + + +## Logout +Description: Example request: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#logout) + +Language: Bash +File path: N/A + +```bash +curl -X POST http://localhost:1337/api/auth/logout \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +Language: JSON +File path: N/A + +```json +{ + "ok": true +} +``` + + +## Get current user +Description: Example request: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#get-current-user) + +Language: Bash +File path: N/A + +```bash +curl http://localhost:1337/api/users/me \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +Language: JSON +File path: N/A + +```json +{ + "id": 1, + "documentId": "x74detpqybxw0bn6ormua5g2", + "username": "testuser1", + "email": "user@example.com", + "provider": "local", + "confirmed": true, + "blocked": false, + "createdAt": "2024-03-15T10:00:00.000Z", + "updatedAt": "2024-03-15T10:00:00.000Z", + "publishedAt": "2024-03-15T10:00:00.000Z" +} +``` + + +## List users +Description: Example request: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#list-users) + +Language: Bash +File path: N/A + +```bash +curl http://localhost:1337/api/users \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + + +## Get a user +Description: Example request: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#get-a-user) + +Language: Bash +File path: N/A + +```bash +curl http://localhost:1337/api/users/1 \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + + +## Count users +Description: Example request: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#count-users) + +Language: Bash +File path: N/A + +```bash +curl http://localhost:1337/api/users/count \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +Language: JSON +File path: N/A + +```json +42 +``` + + +## Create a user +Description: Request body: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#create-a-user) + +Language: JSON +File path: N/A + +```json +{ + "username": "newuser", + "email": "newuser@example.com", + "password": "Password123!", + "role": 1, + "confirmed": true +} +``` + +Language: Bash +File path: N/A + +```bash +curl -X POST http://localhost:1337/api/users \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" \ + -d '{"username": "newuser", "email": "newuser@example.com", "password": "Password123!", "role": 1, "confirmed": true}' +``` + + +## Update a user +Description: Example request: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#update-a-user) + +Language: Bash +File path: N/A + +```bash +curl -X PUT http://localhost:1337/api/users/1 \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" \ + -d '{"username": "updateduser"}' +``` + + +## Delete a user +Description: Example request: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#delete-a-user) + +Language: Bash +File path: N/A + +```bash +curl -X DELETE http://localhost:1337/api/users/1 \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + + +## List roles +Description: Example request: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#list-roles) + +Language: Bash +File path: N/A + +```bash +curl http://localhost:1337/api/users-permissions/roles \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +Language: JSON +File path: N/A + +```json +{ + "roles": [ + { + "id": 1, + "name": "Authenticated", + "description": "Default role given to authenticated user.", + "type": "authenticated", + "nb_users": 2 }, - }) - .then(response => { - // Handle success. - console.log('Data: ', response.data); - }) - .catch(error => { - // Handle error. - console.log('An error occurred:', error.response); - }); + { + "id": 2, + "name": "Public", + "description": "Default role given to unauthenticated user.", + "type": "public", + "nb_users": 0 + } + ] +} ``` -## User registration -Description: Creating a new user in the database with a default role as 'authenticated' can be done as in the following example: -(Source: https://docs.strapi.io/cms/features/users-permissions#user-registration) +## Get a role +Description: Example request: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#get-a-role) -Language: JavaScript +Language: Bash File path: N/A -```js -import axios from 'axios'; +```bash +curl http://localhost:1337/api/users-permissions/roles/1 \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` -// Request API. -// Add your own code here to customize or restrict how the public can register new users. -axios - .post('http://localhost:1337/api/auth/local/register', { - username: 'Strapi user', - email: 'user@strapi.io', - password: 'strapiPassword', - }) - .then(response => { - // Handle success. - console.log('Well done!'); - console.log('User profile', response.data.user); - console.log('User token', response.data.jwt); - }) - .catch(error => { - // Handle error. - console.log('An error occurred:', error.response); - }); + +## Create a role +Description: Request body: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#create-a-role) + +Language: JSON +File path: N/A + +```json +{ + "name": "Editor", + "description": "Can edit content", + "type": "editor" +} ``` +Language: Bash +File path: N/A -## User object in Strapi context -Description: The authenticated user object is a property of ctx.state. -(Source: https://docs.strapi.io/cms/features/users-permissions#user-object-in-strapi-context) +```bash +curl -X POST http://localhost:1337/api/users-permissions/roles \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" \ + -d '{"name": "Editor", "description": "Can edit content", "type": "editor"}' +``` -Language: JavaScript +Language: JSON File path: N/A -```js -create: async ctx => { - const { id } = ctx.state.user; +```json +{ + "ok": true +} +``` - const depositObj = { - ...ctx.request.body, - depositor: id, - }; - const data = await strapi.services.deposit.add(depositObj); +## Update a role +Description: Example request: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#update-a-role) - // Send 201 `created` - ctx.created(data); -}; +Language: Bash +File path: N/A + +```bash +curl -X PUT http://localhost:1337/api/users-permissions/roles/1 \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" \ + -d '{"description": "Updated description"}' +``` + +Language: JSON +File path: N/A + +```json +{ + "ok": true +} +``` + + +## Delete a role +Description: Example request: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#delete-a-role) + +Language: Bash +File path: N/A + +```bash +curl -X DELETE http://localhost:1337/api/users-permissions/roles/3 \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +Language: JSON +File path: N/A + +```json +{ + "ok": true +} +``` + + +## List permissions +Description: Example request: +(Source: https://docs.strapi.io/cms/features/users-permissions/rest-api#list-permissions) + +Language: Bash +File path: N/A + +```bash +curl http://localhost:1337/api/users-permissions/permissions \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" ``` @@ -27153,6 +27660,30 @@ File path: N/A +# The Users & Permissions plugin's register.allowedFields configuration option defaults to [] +Source: https://docs.strapi.io/cms/migration/v4-to-v5/breaking-changes/register-allowed-fields + +## How it works in Strapi 5 +Description: To allow additional fields on registration, update your plugin configuration: +(Source: https://docs.strapi.io/cms/migration/v4-to-v5/breaking-changes/register-allowed-fields#how-it-works-in-strapi-5) + +Language: JavaScript +File path: config/plugins.js + +```js +module.exports = { + 'users-permissions': { + config: { + register: { + allowedFields: ['firstName', 'lastName'], + }, + }, + }, +}; +``` + + + # Some env-only configuration options are handled by the server configuration Source: https://docs.strapi.io/cms/migration/v4-to-v5/breaking-changes/removed-support-for-some-env-options diff --git a/docusaurus/static/llms-full.txt b/docusaurus/static/llms-full.txt index f2ce46c6ac..f16b70d2b1 100644 --- a/docusaurus/static/llms-full.txt +++ b/docusaurus/static/llms-full.txt @@ -9573,209 +9573,1005 @@ To access the admin panel using a specific provider instead of logging in with a -# Users & Permissions -Source: https://docs.strapi.io/cms/features/users-permissions +# Users & Permissions GraphQL API +Source: https://docs.strapi.io/cms/features/users-permissions/graphql-api -# Users & Permissions +# Users & Permissions GraphQL API -The Users & Permissions feature allows the management of the end-users of a Strapi project. It provides a full authentication process based on JSON Web Tokens (JWT) to protect your API, and an access-control list (ACL) strategy that enables you to manage permissions between groups of users. +This page documents all GraphQL queries and mutations provided by the Users & Permissions plugin. The [GraphQL plugin](/cms/plugins/graphql) must be installed. For configuration details, see the main [Users & Permissions page](/cms/features/users-permissions). - +## Authentication -## Admin panel configuration +Authentication mutations handle login, registration, and password management. Public mutations do not require an Authorization header. -The Users & Permissions feature is configured from both the admin panel settings, and via the code base. +### Login -### Roles +The `login` mutation authenticates a user and returns a JWT token: -The Users & Permissions feature allows creating and managing roles for end users, to configure what they can have access to. +```graphql +mutation { + login(input: { identifier: "user@example.com", password: "yourPassword" }) { + jwt + user { + id + documentId + username + email + confirmed + blocked + } + } +} +``` -#### Creating a new role +Input type `UsersPermissionsLoginInput`: -**Path:** +- `identifier` (String!) -- email or username +- `password` (String!) +- `provider` (String, defaults to "local") -`) and inside hyperlink text that references an external page title should NOT be flagged. + - `"the token -- received by email"` → `"the token (received by email)"` +- **Exception:** Em dashes and double hyphens inside HTML comments (``), code fences, and inside hyperlink text that references an external page title should NOT be flagged. ### Consistency - **Detect:** Inconsistent terminology within the same document (e.g., "admin panel" vs "Admin Panel" vs "administration panel"); inconsistent heading capitalization From aaaff3a238c9dc17f7af3556d9abe2d903c23b79 Mon Sep 17 00:00:00 2001 From: Pierre Wizla <4233866+pwizla@users.noreply.github.com> Date: Thu, 21 May 2026 16:51:14 +0200 Subject: [PATCH 25/29] Improve provider deprecation callouts with specific technical details --- .../users-and-permissions-providers/instagram.md | 4 ++-- .../users-and-permissions-providers/linkedin.md | 4 ++-- .../configurations/users-and-permissions-providers/twitter.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docusaurus/docs/cms/configurations/users-and-permissions-providers/instagram.md b/docusaurus/docs/cms/configurations/users-and-permissions-providers/instagram.md index 4a421ac3b1..7307e8d5a8 100644 --- a/docusaurus/docs/cms/configurations/users-and-permissions-providers/instagram.md +++ b/docusaurus/docs/cms/configurations/users-and-permissions-providers/instagram.md @@ -19,8 +19,8 @@ The present page explains how to setup the Instagram provider for the [Users & P You have read the [Users & Permissions providers documentation](/cms/configurations/users-and-permissions-providers). ::: -:::caution -The Instagram Basic Display API was deprecated and shut down by Meta on December 4, 2024. This provider may no longer work as documented below. +:::caution Deprecated provider +The Instagram Basic Display API was shut down by Meta on December 4, 2024. Strapi's built-in Instagram provider relies on this API and **no longer works**. Meta's replacement is the [Instagram Business Login](https://developers.facebook.com/docs/instagram-business-login), which requires a different authentication flow. Until Strapi updates the provider implementation, Instagram authentication is not available out of the box. The setup steps below are kept for reference only. ::: ## Instagram configuration diff --git a/docusaurus/docs/cms/configurations/users-and-permissions-providers/linkedin.md b/docusaurus/docs/cms/configurations/users-and-permissions-providers/linkedin.md index 368d7da0bb..8eb7d32baf 100644 --- a/docusaurus/docs/cms/configurations/users-and-permissions-providers/linkedin.md +++ b/docusaurus/docs/cms/configurations/users-and-permissions-providers/linkedin.md @@ -19,8 +19,8 @@ The present page explains how to setup the LinkedIn provider for the [Users & Pe You have read the [Users & Permissions providers documentation](/cms/configurations/users-and-permissions-providers). ::: -:::caution -LinkedIn has migrated from "Sign In with LinkedIn" to "Sign In with LinkedIn using OpenID Connect." The `r_liteprofile` and `r_emailaddress` scopes have been deprecated. The setup steps below may need adjustments to match the new OpenID Connect product. +:::caution Deprecated scopes +LinkedIn has migrated to "Sign In with LinkedIn using OpenID Connect," deprecating the `r_liteprofile` and `r_emailaddress` scopes. Strapi's built-in LinkedIn provider still uses these deprecated scopes and the legacy Profile API. The provider **may stop working** when LinkedIn fully removes legacy support. The setup steps below reflect the current Strapi implementation but the LinkedIn developer portal steps (product selection, scopes) should follow LinkedIn's [OpenID Connect documentation](https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2). ::: ## LinkedIn configuration diff --git a/docusaurus/docs/cms/configurations/users-and-permissions-providers/twitter.md b/docusaurus/docs/cms/configurations/users-and-permissions-providers/twitter.md index 8b5824017c..09092e76ba 100644 --- a/docusaurus/docs/cms/configurations/users-and-permissions-providers/twitter.md +++ b/docusaurus/docs/cms/configurations/users-and-permissions-providers/twitter.md @@ -19,8 +19,8 @@ The present page explains how to setup the Twitter provider for the [Users & Per You have read the [Users & Permissions providers documentation](/cms/configurations/users-and-permissions-providers). ::: -:::caution -Twitter has rebranded to X. The developer portal has been significantly restructured, and free-tier API access is now limited. The setup steps and URLs below may be outdated. +:::caution Paid API access required +Twitter (now X) restructured its API access in 2023. Strapi's built-in Twitter provider uses the v1.1 API (`account/verify_credentials`), which is **no longer available on the free tier**. You need a [Basic or Pro plan](https://developer.x.com/en/portal/products) on the X Developer Portal to use this provider. The developer portal URLs and setup flow have also changed since the rebranding. ::: ## Twitter configuration From 8c3b69be210ee079e04267ba811b452baca34d6e Mon Sep 17 00:00:00 2001 From: Pierre Wizla <4233866+pwizla@users.noreply.github.com> Date: Thu, 21 May 2026 16:53:51 +0200 Subject: [PATCH 26/29] Remove double-hyphen dash and fix 'plugin' to 'feature' in register-allowed-fields --- .../v4-to-v5/breaking-changes/register-allowed-fields.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docusaurus/docs/cms/migration/v4-to-v5/breaking-changes/register-allowed-fields.md b/docusaurus/docs/cms/migration/v4-to-v5/breaking-changes/register-allowed-fields.md index e5d2ce1375..b0a1c12c4d 100644 --- a/docusaurus/docs/cms/migration/v4-to-v5/breaking-changes/register-allowed-fields.md +++ b/docusaurus/docs/cms/migration/v4-to-v5/breaking-changes/register-allowed-fields.md @@ -59,7 +59,7 @@ A codemod should handle this migration. If not, please refer to the documentatio #### How it works in Strapi 5 -The fields `username`, `email`, and `password` are always accepted by the registration endpoint -- they are hardcoded as `alwaysAllowedKeys` in the Users & Permissions plugin source. Any other field on the User content-type must be explicitly listed in `allowedFields` or the request will be rejected with a `400` status and an error message like `"Invalid parameters: fieldName"`. +The fields `username`, `email`, and `password` are always accepted by the registration endpoint. They are hardcoded as `alwaysAllowedKeys` in the Users & Permissions feature source. Any other field on the User content-type must be explicitly listed in `allowedFields` or the request will be rejected with a `400` status and an error message like `"Invalid parameters: fieldName"`. To allow additional fields on registration, update your plugin configuration: From fcc9c6217819721de2954e426e38039ab3193551 Mon Sep 17 00:00:00 2001 From: Pierre Wizla <4233866+pwizla@users.noreply.github.com> Date: Thu, 21 May 2026 17:51:16 +0200 Subject: [PATCH 27/29] Fix heading case and remove stale NotV5 banner in authentication example --- .../cms/backend-customization/examples/authentication.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/docusaurus/docs/cms/backend-customization/examples/authentication.md b/docusaurus/docs/cms/backend-customization/examples/authentication.md index d9652b72fd..98141eb8d5 100644 --- a/docusaurus/docs/cms/backend-customization/examples/authentication.md +++ b/docusaurus/docs/cms/backend-customization/examples/authentication.md @@ -6,12 +6,8 @@ pagination_prev: cms/backend-customization/examples pagination_next: cms/backend-customization/examples/services-and-controllers --- -import NotV5 from '/docs/snippets/_not-updated-to-v5.md' - # Examples cookbook: Authentication flow with JWT - - :::prerequisites This page is part of the back end customization examples cookbook. Please ensure you've read its [introduction](/cms/backend-customization/examples). ::: @@ -146,7 +142,7 @@ const Login = () => { export default Login; ``` -## Enhanced Authentication with Session Management +## Enhanced authentication with session management The above example uses the traditional JWT approach. For enhanced security, you can enable session management mode in your Users & Permissions configuration, which provides shorter-lived access tokens and refresh token functionality. @@ -171,7 +167,7 @@ module.exports = ({ env }) => ({ }); ``` -### Enhanced Login Component +### Enhanced login component Here's an updated login component that handles both JWT and refresh tokens: From bff9e8b3fdc274ba612bcabce909173e6484273a Mon Sep 17 00:00:00 2001 From: Pierre Wizla <4233866+pwizla@users.noreply.github.com> Date: Thu, 21 May 2026 17:51:27 +0200 Subject: [PATCH 28/29] Fix exact error message and remove 'plugin' from prose in REST API page --- docusaurus/docs/cms/features/users-permissions/rest-api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docusaurus/docs/cms/features/users-permissions/rest-api.md b/docusaurus/docs/cms/features/users-permissions/rest-api.md index 693f593f0d..89eaecf0f7 100644 --- a/docusaurus/docs/cms/features/users-permissions/rest-api.md +++ b/docusaurus/docs/cms/features/users-permissions/rest-api.md @@ -13,7 +13,7 @@ tags: The Users & Permissions feature provides REST API endpoints for authentication, user management, roles, and permissions. -The Users & Permissions feature exposes a set of REST API endpoints for authentication, user management, and role/permission management. These endpoints are separate from the standard content-type CRUD endpoints and have their own response shapes. For a general overview of the plugin's features and configuration, see the [Users & Permissions introduction](/cms/features/users-permissions). +The Users & Permissions feature exposes a set of REST API endpoints for authentication, user management, and role/permission management. These endpoints are separate from the standard content-type CRUD endpoints and have their own response shapes. For a general overview and configuration options, see the [Users & Permissions introduction](/cms/features/users-permissions). All endpoints use the `/api` prefix. For example, if your Strapi server runs at `http://localhost:1337`, the login endpoint is `http://localhost:1337/api/auth/local`. @@ -229,7 +229,7 @@ Possible errors: |--------|---------|-------| | 400 | `"passwordConfirmation is a required field"` | Missing `passwordConfirmation` | | 400 | `"The provided current password is invalid"` | Wrong current password | -| 400 | `"New password must differ from current password"` | Same password reused | +| 400 | `"Your new password must be different than your current password"` | Same password reused | This endpoint is rate limited. From 34a98838fa6950a89aa4a9c56374a186f76b92e5 Mon Sep 17 00:00:00 2001 From: Pierre Wizla <4233866+pwizla@users.noreply.github.com> Date: Thu, 21 May 2026 17:51:48 +0200 Subject: [PATCH 29/29] Fix 'plugin' to 'feature' in prose and align '(default)' comments --- docusaurus/docs/cms/features/users-permissions.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docusaurus/docs/cms/features/users-permissions.md b/docusaurus/docs/cms/features/users-permissions.md index 8ba0cbc903..475062f8e5 100644 --- a/docusaurus/docs/cms/features/users-permissions.md +++ b/docusaurus/docs/cms/features/users-permissions.md @@ -258,8 +258,8 @@ module.exports = ({ env }) => ({ jwtManagement: 'refresh', sessions: { accessTokenLifespan: 600, // 10 minutes (default) - maxRefreshTokenLifespan: 2592000, // 30 days - idleRefreshTokenLifespan: 1209600, // 14 days + maxRefreshTokenLifespan: 2592000, // 30 days (default) + idleRefreshTokenLifespan: 1209600, // 14 days (default) maxSessionLifespan: 86400, // 1 day (default) idleSessionLifespan: 7200, // 2 hours (default) httpOnly: false, // Set to true for HTTP-only cookies @@ -289,8 +289,8 @@ export default ({ env }) => ({ jwtManagement: 'refresh', sessions: { accessTokenLifespan: 600, // 10 minutes (default) - maxRefreshTokenLifespan: 2592000, // 30 days - idleRefreshTokenLifespan: 1209600, // 14 days + maxRefreshTokenLifespan: 2592000, // 30 days (default) + idleRefreshTokenLifespan: 1209600, // 14 days (default) maxSessionLifespan: 86400, // 1 day (default) idleSessionLifespan: 7200, // 2 hours (default) httpOnly: false, // Set to true for HTTP-only cookies @@ -530,7 +530,7 @@ If you need to configure a custom handler to accept other URLs, you can create a ### Route and policy customization {#customizing-routes-and-policies} -The Users & Permissions plugin routes and controllers can be extended and overridden through the [plugin extension system](/cms/plugins-development/plugins-extension). This is useful for adding custom policies to user endpoints, overriding controller logic, or adding new routes. +The Users & Permissions feature routes and controllers can be extended and overridden through the [plugin extension system](/cms/plugins-development/plugins-extension). This is useful for adding custom policies to user endpoints, overriding controller logic, or adding new routes.