diff --git a/agents/authoring/AGENTS.cms.features.md b/agents/authoring/AGENTS.cms.features.md index 33b100d412..82fb101c51 100644 --- a/agents/authoring/AGENTS.cms.features.md +++ b/agents/authoring/AGENTS.cms.features.md @@ -54,6 +54,10 @@ Heading Conventions - Use H2 for major sections (Configuration, Usage); H3 for subsections. - Keep headings action‑oriented and specific (e.g., “Admin panel configuration”, not “Configuration details”). +Sidebar +- Feature pages use flat `type: 'doc'` entries in `sidebars.js`. Never create, replace, or convert them to `type: 'category'` entries. +- If a feature has sub-pages (e.g., API references), link to them from the page content using `` and `` components — not via sidebar nesting. + Cross‑linking - Link to related features (e.g., Content‑type Builder, Content Manager) and relevant API references. - Prefer relative links within `/cms/` and use the consistent link text used across docs. diff --git a/agents/prompts/style-checker.md b/agents/prompts/style-checker.md index fec3a22089..4f61513fe7 100644 --- a/agents/prompts/style-checker.md +++ b/agents/prompts/style-checker.md @@ -191,13 +191,14 @@ Beyond the 12 rules, also check for: - **Severity:** warning - **Note:** Strapi documentation always uses absolute-style paths starting with `/`. The `./` relative prefix should not appear in documentation prose or code examples referencing project file paths. -### Em dashes -- **Detect:** Em dashes (—) anywhere in prose +### Em dashes and double hyphens +- **Detect:** Em dashes (—) and double hyphens used as dashes ( -- ) anywhere in prose - **Severity:** error -- **Note:** Em dashes are not used in Strapi technical documentation. They are a common signal of AI-generated text. Replace with a colon, a period, or restructure the sentence. +- **Note:** Neither em dashes nor double hyphens are used as dashes in Strapi technical documentation. They are a common signal of AI-generated text. Replace with a colon, a period, parentheses, or restructure the sentence. - `"Status — shows the current state"` → `"Status: shows the current state"` - `"The button — visible only to admins — opens the panel"` → `"The button opens the panel. It is only visible to admins."` -- **Exception:** Em dashes inside HTML comments (``) 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 diff --git a/docusaurus/docs/cms/backend-customization/examples/authentication.md b/docusaurus/docs/cms/backend-customization/examples/authentication.md index a7b38c5d60..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). ::: @@ -99,7 +95,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(); /** @@ -145,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. @@ -159,16 +156,18 @@ 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) }, }, }, }); ``` -### Enhanced Login Component +### Enhanced login component Here's an updated login component that handles both JWT and refresh tokens: @@ -257,7 +256,6 @@ const EnhancedLogin = () => { export default EnhancedLogin; ``` -```
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..11e0b362e5 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 @@ -3,7 +3,7 @@ title: Auth0 provider setup for Users & Permissions description: Learn how to setup the Auth0 provider for the Users & Permissions feature. displayed_sidebar: cmsSidebar tags: -- users and permissions +- users & permissions - providers - configuration - customization @@ -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..e46768c30f 100644 --- a/docusaurus/docs/cms/configurations/users-and-permissions-providers/cas.md +++ b/docusaurus/docs/cms/configurations/users-and-permissions-providers/cas.md @@ -1,9 +1,9 @@ --- title: CAS provider setup for Users & Permissions -description: Learn how to setup the CAS provider for the Users & Permissions feature. +description: Learn how to set up the CAS provider for the Users & Permissions feature. displayed_sidebar: cmsSidebar tags: -- users and permissions +- users & permissions - providers - configuration - customization @@ -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 set up 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..7307e8d5a8 100644 --- a/docusaurus/docs/cms/configurations/users-and-permissions-providers/instagram.md +++ b/docusaurus/docs/cms/configurations/users-and-permissions-providers/instagram.md @@ -3,7 +3,7 @@ title: Instagram provider setup for Users & Permissions description: Learn how to setup the Instagram provider for the Users & Permissions feature. displayed_sidebar: cmsSidebar tags: -- users and permissions +- users & permissions - providers - configuration - customization @@ -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 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 :::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..8eb7d32baf 100644 --- a/docusaurus/docs/cms/configurations/users-and-permissions-providers/linkedin.md +++ b/docusaurus/docs/cms/configurations/users-and-permissions-providers/linkedin.md @@ -3,7 +3,7 @@ title: LinkedIn provider setup for Users & Permissions description: Learn how to setup the LinkedIn provider for the Users & Permissions feature. displayed_sidebar: cmsSidebar tags: -- users and permissions +- users & permissions - providers - configuration - customization @@ -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 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 :::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..09092e76ba 100644 --- a/docusaurus/docs/cms/configurations/users-and-permissions-providers/twitter.md +++ b/docusaurus/docs/cms/configurations/users-and-permissions-providers/twitter.md @@ -3,7 +3,7 @@ title: Twitter provider setup for Users & Permissions description: Learn how to setup the Twitter provider for the Users & Permissions feature. displayed_sidebar: cmsSidebar tags: -- users and permissions +- users & permissions - providers - configuration - customization @@ -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 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 :::note diff --git a/docusaurus/docs/cms/features/users-permissions.md b/docusaurus/docs/cms/features/users-permissions.md index 11fa3c7017..475062f8e5 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. Users currently assigned to a deleted role are automatically reassigned to the Public role. 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. @@ -138,7 +138,7 @@ Click the search button above the table :::note Username generation during provider registration When an end user registers via an authentication provider (such as Google or GitHub), Strapi automatically generates a username from the provider email address, using the part before the `@` symbol (e.g., `joe` from `joe@gmail.com`). -If the generated username is already taken, Strapi appends an incrementing number to make it unique (e.g., `joe1`, then `joe2`, and so on). The existing account's username is not affected. +If the generated username is already taken, Strapi appends a random number to make it unique (e.g., `joe1234`). The existing account's username is not affected. ::: ### Email templates @@ -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) - 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) 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) - 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) 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. @@ -528,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. @@ -552,7 +554,7 @@ With the Users & Permissions feature, the end users and their account informatio }} /> -Registering new end users in a front-end application with the Users & Permissions plugin consists of adding a new entry to the User collection type. +Registering new end users in a front-end application with the Users & Permissions feature consists of adding a new entry to the User collection type. 1. Go to the User collection type in the Content Manager. 2. Click on the **Create new entry** button in the top right corner. @@ -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. +### Token usage -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 +### 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 => { 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..2a6f846dd8 --- /dev/null +++ b/docusaurus/docs/cms/features/users-permissions/graphql-api.md @@ -0,0 +1,350 @@ +--- +title: Users & Permissions GraphQL API +description: GraphQL queries and mutations for authentication, user management, and role-based access with the Users & Permissions feature. +displayed_sidebar: cmsSidebar +tags: + - users & permissions + - GraphQL API + - authentication + - API +--- + +# Users & Permissions GraphQL API + +The Users & Permissions feature provides GraphQL queries and mutations for authentication, user management, and role-based access. + +This page documents all GraphQL queries and mutations provided by the Users & Permissions feature. For configuration details, see the main [Users & Permissions page](/cms/features/users-permissions). + +:::prerequisites +The [GraphQL plugin](/cms/plugins/graphql) must be installed. +::: + +## 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: + +```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: + +```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!), passed as a 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. + +:::note Identifier convention +User and role mutations (`updateUsersPermissionsUser`, `deleteUsersPermissionsUser`, `updateUsersPermissionsRole`, `deleteUsersPermissionsRole`) accept the numeric database `id` as the `id` argument, the same value used by their REST equivalents. The `documentId` is exposed in the response for reference but is not accepted as the mutation argument. +::: + +### 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: "1", 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: "1") { + 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. +::: + +## Session management + +Session management operations (token refresh and logout) are only available through the [REST API](/cms/features/users-permissions/rest-api#session-management). There are no GraphQL equivalents for these endpoints. + +## 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` | 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..2f839eafd4 --- /dev/null +++ b/docusaurus/docs/cms/features/users-permissions/rest-api.md @@ -0,0 +1,816 @@ +--- +title: Users & Permissions REST API +description: REST API reference for authentication, user management, roles, and permissions with the Users & Permissions feature. +displayed_sidebar: cmsSidebar +tags: + - users & permissions + - REST API + - authentication + - API +--- + +# Users & Permissions REST API + +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 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`. + +## 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. + + + + + +```bash +curl -X POST http://localhost:1337/api/auth/local \ + -H "Content-Type: application/json" \ + -d '{"identifier": "user@example.com", "password": "yourPassword"}' +``` + + + + + +```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: 5 requests per 5 minutes). + +### Register + +`POST /api/auth/local/register` + +Creates a new user account and returns a JWT along with the user object. + + + + + +```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!"}' +``` + + + + + +```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, which is "Authenticated" unless changed in Settings > Users & Permissions > Advanced Settings. +::: + +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. + + + + + +```bash +curl -X POST http://localhost:1337/api/auth/forgot-password \ + -H "Content-Type: application/json" \ + -d '{"email": "user@example.com"}' +``` + + + + + +```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. The `code`, `password`, and `passwordConfirmation` fields are all required. + + + + + +```bash +curl -X POST http://localhost:1337/api/auth/reset-password \ + -H "Content-Type: application/json" \ + -d '{"code": "resetTokenFromEmail", "password": "NewPassword123!", "passwordConfirmation": "NewPassword123!"}' +``` + + + + + +```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 | `"passwordConfirmation is a required field"` | Missing `passwordConfirmation` | +| 400 | `"Passwords do not match"` | `password` and `passwordConfirmation` differ | +| 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. The `currentPassword`, `password`, and `passwordConfirmation` fields are all required. + + + + + +```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!"}' +``` + + + + + +```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 | `"passwordConfirmation is a required field"` | Missing `passwordConfirmation` | +| 400 | `"Passwords do not match"` | `password` and `passwordConfirmation` differ | +| 400 | `"The provided current password is invalid"` | Wrong current password | +| 400 | `"Your new password must be different than your 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). + + + + + +```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". + +Possible errors: + +| Status | Message | Cause | +|--------|---------|-------| +| 400 | `"confirmation is a required field"` | `confirmation` query parameter missing | +| 400 | `"Invalid token"` | Confirmation token malformed, expired, or already used | + +### Send email confirmation + +`POST /api/auth/send-email-confirmation` + +Resends the confirmation email to a user who has not yet confirmed their email address. + + + + + +```bash +curl -X POST http://localhost:1337/api/auth/send-email-confirmation \ + -H "Content-Type: application/json" \ + -d '{"email": "user@example.com"}' +``` + + + + + +```json +{ + "email": "user@example.com", + "sent": true +} +``` + + + + + +Possible errors: + +| Status | Message | Cause | +|--------|---------|-------| +| 400 | `"Already confirmed"` | User already confirmed their email | +| 400 | `"User blocked"` | Account blocked | + +This endpoint is 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). + +If the username derived from the provider profile already exists, a unique username is generated automatically to avoid conflicts. + +## Session management + +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). + + + + + +```bash +curl -X POST http://localhost:1337/api/auth/refresh \ + -H "Content-Type: application/json" \ + -d '{"refreshToken": "yourRefreshToken"}' +``` + + + + + +```json +{ + "jwt": "newAccessToken", + "refreshToken": "newRefreshToken" +} +``` + + + + + +:::note +When `httpOnly` is enabled in the session configuration, the new refresh token is set as an HTTP-only cookie instead of being included in the response body. In that case, the response only contains `{ "jwt": "newAccessToken" }`. +::: + +### Logout + +`POST /api/auth/logout` + +Revokes all sessions for the authenticated user. Requires a valid Bearer token. + + + + + +```bash +curl -X POST http://localhost:1337/api/auth/logout \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + + + + + +```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. + + + + + +```bash +curl http://localhost:1337/api/users/me \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + + + + + +```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. + + + + + +```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`. + + + + + +```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. + + + + + +```bash +curl http://localhost:1337/api/users/count \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + + + + + +```json +42 +``` + + + + + +### Create a user + +`POST /api/users` + +Creates a new user. If `role` is omitted, the default role is assigned. + + + + + +```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. + + + + + +```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`. + + + + + +```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. + + + + + +```bash +curl http://localhost:1337/api/users-permissions/roles \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + + + + + +```json +{ + "roles": [ + { + "id": 1, + "documentId": "abcdefghijklmnop12345678", + "name": "Authenticated", + "description": "Default role given to authenticated user.", + "type": "authenticated", + "createdAt": "2024-03-15T10:00:00.000Z", + "updatedAt": "2024-03-15T10:00:00.000Z", + "publishedAt": "2024-03-15T10:00:00.000Z", + "nb_users": 2 + }, + { + "id": 2, + "documentId": "qrstuvwxyzabcdef98765432", + "name": "Public", + "description": "Default role given to unauthenticated user.", + "type": "public", + "createdAt": "2024-03-15T10:00:00.000Z", + "updatedAt": "2024-03-15T10:00:00.000Z", + "publishedAt": "2024-03-15T10:00:00.000Z", + "nb_users": 0 + } + ] +} +``` + + + + + +### Get a role + +`GET /api/users-permissions/roles/:id` + +Returns a single role by `id`, including its full permissions tree. + + + + + +```bash +curl http://localhost:1337/api/users-permissions/roles/1 \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + + + + + +Example response (200): An object of the form `{ "role": { ... } }`, where the role contains a nested `permissions` structure mapping plugins to controllers to actions. + +### Create a role + +`POST /api/users-permissions/roles` + +Creates a new role. + + + + + +```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"}' +``` + + + + + +```json +{ + "ok": true +} +``` + + + + + +### Update a role + +`PUT /api/users-permissions/roles/:id` + +Updates an existing role. + + + + + +```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"}' +``` + + + + + +```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. + + + + + +```bash +curl -X DELETE http://localhost:1337/api/users-permissions/roles/3 \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + + + + + +```json +{ + "ok": true +} +``` + + + + + +Possible errors: + +| Status | Message | Cause | +|--------|---------|-------| +| 400 | `"Cannot delete public role"` | Attempted to delete the Public role | + +## 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 each plugin name to a `controllers` object, which itself maps controller names to actions. + + + + + +```bash +curl http://localhost:1337/api/users-permissions/permissions \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + + + + + +Example response (200): A nested `permissions` object where top-level keys are plugin names, each containing a `controllers` object whose keys are controller names, and each controller maps to actions with their `enabled` status and `policy` information. 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..2174906431 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 @@ -49,6 +49,10 @@ An undefined `allowedFields` is treated as an empty array, and no fields are acc +### 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 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"`. + ## Migration @@ -56,3 +60,19 @@ 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). + +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 relied on the Strapi v4 behavior (all fields accepted by default), list every extra field in `allowedFields`. Fields not listed will be silently dropped or rejected. diff --git a/docusaurus/sidebars.js b/docusaurus/sidebars.js index ab560557c9..5f88fdb1a7 100644 --- a/docusaurus/sidebars.js +++ b/docusaurus/sidebars.js @@ -132,6 +132,9 @@ const sidebars = { type: 'doc', label: 'Users & Permissions', id: 'cms/features/users-permissions', + customProps: { + updated: true, + }, }, { type: 'category', diff --git a/docusaurus/static/llms-code.txt b/docusaurus/static/llms-code.txt index d7c2ffc118..b0a17e02cd 100644 --- a/docusaurus/static/llms-code.txt +++ b/docusaurus/static/llms-code.txt @@ -11587,7 +11587,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(); /** @@ -11647,9 +11648,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) }, }, }, @@ -12667,7 +12670,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; @@ -12700,7 +12703,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; @@ -12885,7 +12888,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 @@ -12944,7 +12947,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 @@ -24452,9 +24455,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', @@ -24481,9 +24486,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', @@ -24507,7 +24514,7 @@ module.exports = ({ env }) => ({ config: { jwtManagement: 'legacy-support', jwt: { - expiresIn: '7d', // Traditional JWT expiry + expiresIn: '30d', // Traditional JWT expiry }, }, }, @@ -24556,7 +24563,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 @@ -24570,8 +24577,8 @@ module.exports = ({ env }) => ({ config: { ratelimit: { enabled: true, - interval: { min: 5 }, - max: 5, + interval: 60000, // 1 minute + max: 10, }, }, }, @@ -24591,8 +24598,8 @@ export default ({ env }) => ({ config: { ratelimit: { enabled: true, - interval: { min: 5 }, - max: 5, + interval: 60000, // 1 minute + max: 10, }, }, }, @@ -24602,7 +24609,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 @@ -24663,376 +24670,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" ``` @@ -27770,6 +28277,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 f09506642d..ba1974f7f9 100644 --- a/docusaurus/static/llms-full.txt +++ b/docusaurus/static/llms-full.txt @@ -9767,541 +9767,1005 @@ To access the admin panel using a specific provider instead of logging in with a -# MCP server -Source: https://docs.strapi.io/cms/features/strapi-mcp-server +# Users & Permissions GraphQL API +Source: https://docs.strapi.io/cms/features/users-permissions/graphql-api -# MCP server +# Users & Permissions GraphQL API -The MCP server exposes a set of content management tools to AI clients such as Claude Desktop, Claude Code, Cursor, or any MCP-compatible tool. An AI client connected to the MCP server can, for example, create a blog article, list recent entries, or publish a page. Which tools are available depends on the permissions granted to the Admin token used for authentication. +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 -## Configuration +Authentication mutations handle login, registration, and password management. Public mutations do not require an Authorization header. -Before first use, the Strapi MCP server must be: -- enabled through the server configuration file and authenticated with Admin tokens created in the admin panel -- connected to your AI client. +### Login -### Strapi code-based configuration +The `login` mutation authenticates a user and returns a JWT token: -Enable the MCP server by adding the `mcp` object to the server configuration file: +```graphql +mutation { + login(input: { identifier: "user@example.com", password: "yourPassword" }) { + jwt + user { + id + documentId + username + email + confirmed + blocked + } + } +} +``` -
+Input type `UsersPermissionsLoginInput`: -Once the setting is in place, restart Strapi. The MCP endpoint becomes available at `/mcp` on your Strapi server (e.g., `http://localhost:1337/mcp`). +- `identifier` (String!) -- email or username +- `password` (String!) +- `provider` (String, defaults to "local") -#### Advanced options +Returns `UsersPermissionsLoginPayload`: `jwt` (String), `user` (UsersPermissionsMe) -The following optional keys can be added to the `mcp` configuration object: +Auth: Public -| Option | Type | Default | Description | -|--------|------|---------|-------------| -| `enabled` | Boolean | `false` | Enable or disable the MCP server. | -| `connectTimeoutMs` | Number | `5000` | Maximum time (in ms) for the internal MCP transport to connect before the request is aborted. | -| `requestTimeoutMs` | Number | `60000` | Maximum time (in ms) for a single MCP request to complete before it times out. | +### Register -```js title="config/server.js" -mcp: { - enabled: true, - connectTimeoutMs: 10000, // 10 seconds - requestTimeoutMs: 120000, // 2 minutes -}, +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 + } +} ``` -### Strapi admin panel configuration +Input: `email` (String!) -- direct argument, not wrapped in an input type. -The MCP server authenticates requests using Admin tokens. Each MCP session is scoped to the permissions of the token used to connect: +Returns `UsersPermissionsPasswordPayload`: `ok` (Boolean!) -1. Create a new Admin token (see [Creating an Admin token](/cms/features/admin-tokens#creating-a-new-admin-token) on the Admin tokens feature page). -2. Copy the token value. You will need it when configuring the AI client. +Auth: Public -The token's permissions determine which MCP tools are exposed to the AI client. For instance, if the token only grants `read` on an `Article` content-type, the AI client will only see listing and reading tools for articles. +### Reset password -### AI client configuration +The `resetPassword` mutation sets a new password using the token received by email: -Once you have enabled the MCP server through the server configuration file and created an Admin token in the admin panel, connect your AI client to the Strapi MCP server. +```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 -`http://localhost:1337/` is used in configuration examples on this page. If your Strapi server is hosted on another URL or port, please update the code accordingly. +Unlike the REST equivalent (which redirects), the GraphQL mutation returns a JWT and user object directly. ::: -#### Connecting Claude Desktop +## User queries and mutations -Open Claude Desktop's configuration file. The location varies depending on your system: +These operations retrieve and manage user records. They require the corresponding permission to be enabled for the requesting user's role. -| OS | File location | -|----|---------------| -| macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` | -| Windows | `%APPDATA%\Claude\claude_desktop_config.json` | +### Get authenticated user (me query) -:::tip -You can also open the configuration file for Claude Desktop from Claude's settings: go to Settings > Desktop app > Developer, then click on the **Edit config** button. -::: +The `me` query returns the profile of the currently authenticated user: -Add the Strapi MCP server to Claude's configuration file, as in the following example, replacing `YOUR_ADMIN_TOKEN` with the Admin token value copied from the [Strapi admin panel configuration](#strapi-admin-panel-configuration): +```graphql +query { + me { + id + documentId + username + email + confirmed + blocked + role { + id + name + description + type + } + } +} +``` -```json title="claude_desktop_config.json" -{ - "mcpServers": { - "strapi-mcp": { - "command": "npx", - "args": [ - "-y", - "mcp-remote", - "http://localhost:1337/mcp", - "--header", - "Authorization: Bearer YOUR_ADMIN_TOKEN" - ] +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 } } } ``` -Restart Claude Desktop for the changes to take effect. +Input: `UsersPermissionsUserInput` (auto-generated from User content-type schema, with `password` field added) -#### Connecting Claude Code +Auth: Requires `plugin::users-permissions.user.create` permission -Run the following command, replacing `YOUR_ADMIN_TOKEN` with the Admin token value copied from the [Strapi admin panel configuration](#strapi-admin-panel-configuration): +### Update a user -```bash -claude mcp add strapi-mcp --transport http http://localhost:1337/mcp -H "Authorization: Bearer YOUR_ADMIN_TOKEN" +The `updateUsersPermissionsUser` mutation updates an existing user record: + +```graphql +mutation { + updateUsersPermissionsUser(id: "documentId123", data: { username: "updatedname" }) { + data { + documentId + username + email + } + } +} ``` -Restart Claude Code, then run `/mcp` to confirm `strapi-mcp` reports as connected. +Auth: Requires `plugin::users-permissions.user.update` permission -#### Connecting Cursor +### Delete a user -Add the server to your `.cursor/mcp.json` file: +The `deleteUsersPermissionsUser` mutation removes a user record: -```json title=".cursor/mcp.json" -{ - "mcpServers": { - "strapi-mcp": { - "type": "streamable-http", - "url": "http://localhost:1337/mcp", - "headers": { - "Authorization": "Bearer YOUR_ADMIN_TOKEN" - } +```graphql +mutation { + deleteUsersPermissionsUser(id: "documentId123") { + data { + documentId + username } } } ``` -#### Connecting Windsurf +Auth: Requires `plugin::users-permissions.user.destroy` permission -Add the server to your `~/.codeium/windsurf/mcp_config.json` file: +## Role mutations -```json title="~/.codeium/windsurf/mcp_config.json" +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` | + + + +# Users & Permissions REST API +Source: https://docs.strapi.io/cms/features/users-permissions/rest-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 { - "mcpServers": { - "strapi-mcp": { - "serverUrl": "http://localhost:1337/mcp", - "headers": { - "Authorization": "Bearer YOUR_ADMIN_TOKEN" - } - } + "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" } } ``` -#### Connecting other MCP clients +Possible errors: -Any client that supports the MCP Streamable HTTP transport can connect. The generic configuration is as follows: +| 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 | -| Setting | Value | -|---------|-------| -| Transport type | `streamable-http` | -| URL | `http://localhost:1337/mcp` (adjust host and port to your Strapi instance) | -| Authorization header | `Bearer YOUR_ADMIN_TOKEN` | +This endpoint is rate limited (default: 10 requests per 60 seconds). -## Usage +### Register -The MCP server uses the Streamable HTTP transport protocol. Any MCP-compatible client can connect by pointing to the `/mcp` endpoint with a `Bearer` token in the `Authorization` header. Once connected, the AI client can interact with your Strapi content using natural language prompts. +`POST /api/auth/local/register` -### Available tools +Creates a new user account and returns a JWT along with the user object. -The MCP server exposes 2 categories of tools: content-type tools generated from your schema, and built-in utility tools. +Request body: -#### Content-type tools +```json +{ + "username": "newuser", + "email": "newuser@example.com", + "password": "Password123!" +} +``` -The tools generated differ depending on whether the content type is a collection type or a single type. +Example request: -**Collection types** generate up to 8 tools — 5 for CRUD operations and 3 for [Draft & Publish](/cms/features/draft-and-publish) actions: +```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!"}' +``` -| Tool | Action | Permission required | Description | -|------|--------|-------------------|-------------| -| `list` | Read | `read` | List entries with pagination, sorting, and filtering | -| `get` | Read | `read` | Get a single entry by document ID | -| `create` | Create | `create` | Create a new entry (created as draft if Draft & Publish is enabled) | -| `update` | Update | `update` | Update an existing entry by document ID | -| `delete` | Delete | `delete` | Delete an entry by document ID | -| `publish` | Publish | `publish` | Publish a draft entry | -| `unpublish` | Unpublish | `publish` | Unpublish a published entry | -| `discard_draft` | Discard draft | `publish` | Discard draft changes and revert to the published version | +Example response (200): -**Single types** generate up to 6 tools. Because a single type always represents exactly one document, there is no `list` tool and create/update are merged into a single `write` tool: +```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" + } +} +``` -| Tool | Action | Permission required | Description | -|------|--------|-------------------|-------------| -| `get` | Read | `read` | Get the single-type document | -| `write` | Create or update | `create` and/or `update` | Create the document if none exists; update the existing draft otherwise | -| `delete` | Delete | `delete` | Delete the single-type document | -| `publish` | Publish | `publish` | Publish the document | -| `unpublish` | Unpublish | `publish` | Unpublish the published document | -| `discard_draft` | Discard draft | `publish` | Discard draft changes and revert to the published version | +:::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. -The publish, unpublish, and discard_draft tools are only generated when [Draft & Publish](/cms/features/draft-and-publish) is enabled on the content type. +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. -#### Built-in utility tools +The newly registered user is assigned the default role (typically "Authenticated"). +::: -In addition to content-type tools, Strapi registers the following built-in tools: +Possible errors: -| Tool | Availability | Description | -|------|-------------|-------------| -| `log` | Development mode only | Logs a message to the Strapi server console at a specified level (`info`, `warn`, `error`, `http`, `log`). Useful for debugging MCP interactions. | +| 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 | -Built-in utility tools are only available in development mode (when `autoReload` is enabled) and do not require specific admin permissions. +This endpoint is rate limited. -### Content management through prompts +### Forgot password -Once connected, you can interact with your Strapi content using natural language: +`POST /api/auth/forgot-password` -| Prompt | What happens | -|--------|-------------| -| "Create a new article titled 'Hello World' with body 'First post'." | Creates a draft article entry | -| "List the 5 most recent articles." | Returns paginated list, newest first | -| "Show me article with ID abc123." | Returns the full entry | -| "Update article abc123, change the title to 'Hello Strapi'." | Updates the title, other fields untouched | -| "Publish article abc123." | Changes the entry status to published | -| "Delete article abc123." | Removes the entry | -| "Create an article in French with the title 'Bonjour le monde'." | Creates a draft article with `locale` set to `fr` | +Sends a password reset email to the specified address. Requires an email provider to be configured. -#### Internationalization (i18n) +Request body: -When [Internationalization (i18n)](/cms/features/internationalization) is enabled on a content type, MCP tools accept an optional `locale` parameter (e.g., `"en"`, `"fr"`). If omitted, the default locale is used. +```json +{ + "email": "user@example.com" +} +``` -The AI client sees which locales are available in each tool's schema, so you can ask it to create or update content in a specific language. For example, asking "Create an article in French titled 'Bonjour'" passes `locale: "fr"` to the `create` tool. Which locales are available depends on the Admin token's permissions (see [Permission boundaries](#permission-boundaries)). +Example request: -:::tip -When working with localized content, explicitly mention the target language in your prompt so the AI client passes the correct `locale` value. For instance, prefer "Create an article **in French**" over "Create an article titled 'Bonjour'" to avoid ambiguity. +```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. ::: -#### Sorting +This endpoint is rate limited. -The `list` tool accepts a `sort` parameter that supports 4 notations: +### Reset password -| Notation | Example | -|----------|---------| -| String | `"title:asc"` | -| Array of strings | `["title:asc", "createdAt:desc"]` | -| Object | `{ "title": "asc" }` | -| Array of objects | `[{ "title": "asc" }, { "createdAt": "desc" }]` | +`POST /api/auth/reset-password` -Sort field names are constrained to the content type's scalar attributes (strings, numbers, booleans, dates, enumerations). Relation, component, dynamic zone, media, and JSON fields cannot be sorted on. +Resets a user's password using a token received by email. All three fields are required. -#### Filtering +Request body: -The `list` tool accepts a `filters` parameter using Strapi's filter syntax: +```json +{ + "code": "resetTokenFromEmail", + "password": "NewPassword123!", + "passwordConfirmation": "NewPassword123!" +} +``` -- **Field operators**: `$eq`, `$ne`, `$in`, `$notIn`, `$lt`, `$lte`, `$gt`, `$gte`, `$between`, `$contains`, `$notContains`, `$startsWith`, `$endsWith`, `$null`, `$notNull`, and their case-insensitive variants (`$eqi`, `$nei`, `$containsi`, `$notContainsi`, `$startsWithi`, `$endsWithi`). -- **Logical operators**: `$and`, `$or` (accept arrays of filter objects), `$not` (wraps a single filter object). -- **Implicit equality**: Passing a value directly (e.g., `{ "title": "Hello" }`) is equivalent to `{ "title": { "$eq": "Hello" } }`. +Example request: -Like sort fields, filter fields are constrained to scalar attributes only. +```bash +curl -X POST http://localhost:1337/api/auth/reset-password \ + -H "Content-Type: application/json" \ + -d '{"code": "resetTokenFromEmail", "password": "NewPassword123!", "passwordConfirmation": "NewPassword123!"}' +``` -#### Pagination +Example response (200): Same shape as the [login](#login) response (jwt + user). -The `list` tool also accepts `page` (1-indexed, default: 1) and `pageSize` (default: 25, max: 100) parameters. +Possible errors: -#### Relations +| Status | Message | Cause | +|--------|---------|-------| +| 400 | `"passwordConfirmation is a required field"` | Missing `passwordConfirmation` | +| 400 | `"Incorrect code provided"` | Invalid or expired reset token | -Relation fields support both a shorthand document ID string and a full relation object. +This endpoint is rate limited. -**To-one relations** (`oneToOne`, `manyToOne`) accept: -- A document ID string: `"z7v8zma53x01r6oceimv922b"` -- A relation object: `{ "documentId": "z7v8zma53x01r6oceimv922b", "locale": "en", "status": "draft" }` (`locale` and `status` are optional) -- `null` to clear the relation +### Change password -**To-many relations** (`oneToMany`, `manyToMany`) accept a relation object with one or more of the following keys: +`POST /api/auth/change-password` -| Key | Description | -|-----|-------------| -| `connect` | Add relations. Accepts an array of document ID strings or `{ documentId, locale?, status?, position? }` objects. The optional `position` key supports `{ before?, after?, start?, end? }` ordering hints (default: `{ end: true }`). | -| `disconnect` | Remove relations. Accepts an array of document ID strings or `{ documentId, locale?, status? }` objects. | -| `set` | Replace all existing relations with the provided array. Pass `null` to clear all relations. Mutually exclusive with `connect`/`disconnect`. | +Changes the password for the currently authenticated user. Requires a valid Bearer token. All three fields are required. -### Permission boundaries +Request body: -The MCP server enforces the same permission model as the Strapi admin panel. Permissions are checked at multiple levels: +```json +{ + "currentPassword": "OldPassword123!", + "password": "NewPassword456!", + "passwordConfirmation": "NewPassword456!" +} +``` -1. **Tool visibility**: When an AI client connects, Strapi checks the Admin token's permissions and only exposes tools the token has access to. If the token does not grant `delete` on `Article`, the AI client will not see a delete tool for articles at all. +Example request: -2. **Field filtering**: Even within an exposed tool, input and output schemas are narrowed to the fields the token can access. If the token grants `read` on `Article` but excludes the `body` field, the AI client will not see or receive `body` content. Field restrictions are applied independently per action. Write schemas (`create`, `update`) only include fields permitted for the corresponding action. +```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!"}' +``` -3. **Locale filtering**: When the [Internationalization (i18n)](/cms/features/internationalization) feature is enabled and locale-level permissions are configured, the `locale` parameter is narrowed per action. For example, a token might allow reading content in `en` and `fr` but only creating content in `en`. If the default locale is permitted for a given action, that locale is applied as the Zod schema default, so the AI client does not need to specify a locale explicitly. +Example response (200): Same shape as the [login](#login) response (jwt + user). -4. **Runtime enforcement**: Beyond schema-level narrowing, each handler calls Strapi's permission checker at runtime to verify access on the specific document being read, written, or published. Condition-based permissions (e.g., "only update entries you own") are enforced at this level. +Possible errors: -This means you can create tokens with fine-grained access: +| 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 | -- A "read-only" token that only exposes listing and reading tools -- A token scoped to specific content types (e.g., articles but not categories) -- A token restricted to specific fields or locales -- A token with condition-based permissions (e.g., only update entries you own) +This endpoint is rate limited. -:::tip -Create dedicated Admin tokens for each AI client or use case. Use the most restrictive permissions that still allow the AI to accomplish its task. -::: +### Email confirmation -### Stateless architecture +`GET /api/auth/email-confirmation` -The MCP server uses a stateless architecture. Each POST request to the `/mcp` endpoint creates a fresh, ephemeral MCP server instance scoped to the authenticated token's permissions. There is no session persistence between requests: every request is independently authenticated and authorized. Because there is no session state, the AI client does not need to manage session IDs, and permission changes (such as revoking a token or updating its permissions) take effect on the next request. +Confirms a user's email address using a token from the confirmation email. -GET and DELETE HTTP methods on the `/mcp` endpoint return a `405 Method Not Allowed` JSON-RPC error, as the MCP server only accepts POST requests. +Query parameter: `confirmation` -- the token received in the confirmation email. -### Known limitations +Example request: -The MCP server has the following limitations: +```bash +curl -G http://localhost:1337/api/auth/email-confirmation \ + --data-urlencode "confirmation=confirmationTokenHere" +``` -- **Dynamic zones**: Dynamic zone fields are passed as untyped arrays in tool schemas. The internal structure of each component within a dynamic zone is not described. -- **Nested population parameters**: The `list` and `get` tools do not support nested population parameters for relations. -- **Media upload**: Media fields accept existing media asset references but the MCP server cannot upload new files. Use Strapi's media library or upload API to add files first, then reference them in MCP tool calls. -- **Custom fields**: Custom fields registered via plugins are mapped to their underlying Strapi type. If the custom field registry is not populated when MCP tools are registered, the custom field falls back to an `unknown` type. -- **Circular component references**: Components that reference themselves (directly or indirectly) fall back to an open `record` schema at the point of the cycle, rather than an infinite recursive structure. +After confirming the email, Strapi redirects the user to the URL configured in the admin panel under Settings > Users & Permissions > Advanced Settings > "Redirection url". -## Plugin API +### Send email confirmation -Strapi plugins can register additional MCP tools, prompts, and resources through the `strapi.ai.mcp` service. All registrations must happen during the plugin's `register()` lifecycle phase, before the MCP server starts. +`POST /api/auth/send-email-confirmation` -### Registering a custom tool +Resends the confirmation email to a user who has not yet confirmed their email address. -Use `strapi.ai.mcp.registerTool()` to expose a custom tool to AI clients: +Request body: - +```json +{ + "email": "user@example.com" +} +``` + +Example request: -#### Tool definition options +```bash +curl -X POST http://localhost:1337/api/auth/send-email-confirmation \ + -H "Content-Type: application/json" \ + -d '{"email": "user@example.com"}' +``` -| Option | Type | Required | Description | -|--------|------|----------|-------------| -| `name` | String | Yes | Unique tool name. Must be unique across all registered MCP tools. | -| `title` | String | Yes | Human-readable title shown to the AI client. | -| `description` | String | Yes | Short description of what the tool does. | -| `auth` | Object | Yes (or `devModeOnly`) | Auth requirement. The session gate passes when the token satisfies **any** policy in the `policies` array. Each policy is `{ action, subject? }`. | -| `devModeOnly` | Boolean | Yes (or `auth`) | Set to `true` to restrict the tool to development mode only (equivalent to the built-in `log` tool). | -| `resolveInputSchema` | Function | No | Returns a Zod schema for the tool's input arguments. Called per request so RBAC constraints can be applied dynamically. Omit for tools with no input. | -| `resolveOutputSchema` | Function | Yes | Returns a Zod schema for the tool's structured output. Called per request. | -| `createHandler` | Function | Yes | Factory that returns the async tool handler. Receives the Strapi instance and per-request context (including `userAbility` and `user`). | -| `telemetry` | Object | No | Optional analytics metadata: `{ source?: string; name?: string }`. Use `source` to identify the plugin and `name` to override the raw tool name in analytics events. | +Example response (200): -:::note -`resolveInputSchema` and `resolveOutputSchema` are called once per incoming MCP request, so you can narrow schemas dynamically based on the token's permissions (via `context.userAbility`). +```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` -# Users & Permissions -Source: https://docs.strapi.io/cms/features/users-permissions +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. -# Users & Permissions +### Provider callback -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. +`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). -## Admin panel configuration +## Session management endpoints -The Users & Permissions feature is configured from both the admin panel settings, and via the code base. +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. -### Roles +### Refresh token -The Users & Permissions feature allows creating and managing roles for end users, to configure what they can have access to. +`POST /api/auth/refresh` -#### Creating a new role +Exchanges a refresh token for a new access token. The old refresh token is invalidated and a new one is returned (token rotation). -**Path:** +Request body: -