diff --git a/docs/Admin/overview.mdx b/docs/Admin/overview.mdx index 4e15a5bc42..fde9faad21 100644 --- a/docs/Admin/overview.mdx +++ b/docs/Admin/overview.mdx @@ -59,7 +59,7 @@ For example, you may wish to have two Collections that both support `Authenticat - `admins` - meant to have a higher level of permissions to manage your data and access the Admin panel - `customers` - meant for end users of your app that should not be allowed to log into the Admin panel -This is totally possible. For the above scenario, by specifying `admin: { user: 'admins' }`, your Payload Admin panel will use `admins` and `customers` will not be able to log in via the Admin panel. +This is totally possible. For the above scenario, by specifying `admin: { user: 'admins' }`, your Payload Admin panel will use `admins`. Any users logged in as `customers` will not be able to log in via the Admin panel. ### Restricting user access @@ -68,10 +68,3 @@ If you would like to restrict which users from a single Collection can access th ### License enforcement Payload requires a valid license key to be used on production domains. You can use it as much as you'd like locally and on staging / UAT domains, but when you deploy to production, you'll need a license key to activate Payload's Admin panel. For more information, [click here](/docs/production/licensing). - -- Main config -- Collection-level config -- Global-level config -- Field-level config - - diff --git a/docs/Authentication/operations.mdx b/docs/Authentication/operations.mdx index b3051c30cf..12112bf510 100644 --- a/docs/Authentication/operations.mdx +++ b/docs/Authentication/operations.mdx @@ -4,40 +4,382 @@ label: Operations order: 30 --- -Talk about Auth operations here. +Enabling Authentication on a Collection automatically exposes additional auth-based operations in the Local, REST, and GraphQL APIs. -#### Me +### Access -Returns null or logged in user with token (useful for HTTP only) +The Access operation returns what a logged in user can and can't do with the collections and globals that are registered via your config. This data can be immensely helpful if your app needs to show and hide certain features based on access control, as the Payload Admin panel does. -#### Create operation modifications +**REST API endpoint**: -Discuss if verification is required, etc etc +`GET http://localhost:3000/api/access` -#### Init +Example response: +```js +{ + canAccessAdmin: true, + license: 'LICENSE_KEY_HERE', + collections: { + pages: { + create: { + permission: true, + }, + read: { + permission: true, + }, + update: { + permission: true, + }, + delete: { + permission: true, + }, + fields: { + title: { + create: { + permission: true, + }, + read: { + permission: true, + }, + update: { + permission: true, + }, + } + } + } + } +} +``` -Checks if there have been users created, checks Admin access +**Example GraphQL Query**: -#### Login +``` +query { + Access { + pages { + read { + permission + } + } + } +} +``` -HTTP-only cookies, token response. Discuss max login attempts +### Me -#### Logout +Returns either a logged in user with token or null when there is no logged in user. -Removes HTTP-only cookie +**REST API endpoint**: -#### Refresh +`GET http://localhost:3000/api/[collection-slug]/me` -Refreshes token (requires valid token) +Example response: +```js +{ + user: { // The JWT "payload" ;) from the logged in user + email: 'dev@payloadcms.com', + createdAt: "2020-12-27T21:16:45.645Z", + updatedAt: "2021-01-02T18:37:41.588Z", + id: "5ae8f9bde69e394e717c8832" + }, + token: '34o4345324...', // The token that can be used to authenticate the user + exp: 1609619861, // Unix timestamp representing when the user's token will expire +} +``` -#### Register First User +**Example GraphQL Query**: -Allows for anyone to register first user through UI +``` +query { + Me[collection-singular-label] { + user { + email + } + exp + } +} +``` -#### Forgot Password +### Login -Takes an email, sends email to that address. Discuss how to customize email +Accepts an `email` and `password`. On success, it will return the logged in user as well as a token that can be used to authenticate. In the GraphQL and REST APIs, this operation also automatically sets an HTTP-only cookie including the user's token. If you pass an Express `res` to the Local API operation, Payload will set a cookie there as well. -#### Access +**Example REST API login**: +```js +const res = await fetch('http://localhost:3000/api/[collection-slug]/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + email: 'dev@payloadcms.com', + password: 'this-is-not-our-password...or-is-it?', + }) +}) -Shows what the user can and cant do. +const json = await res.json(); + +// JSON will be equal to the following: +/* +{ + user: { + email: 'dev@payloadcms.com', + createdAt: "2020-12-27T21:16:45.645Z", + updatedAt: "2021-01-02T18:37:41.588Z", + id: "5ae8f9bde69e394e717c8832" + }, + token: '34o4345324...', + exp: 1609619861 +} +*/ +``` + +**Example GraphQL Mutation**: + +``` +mutation { + login[collection-singular-label](email: "dev@payloadcms.com", password: "yikes") { + user { + email + } + exp + token + } +} +``` + +**Example Local API login**: + +```js +const result = await payload.login({ + collection: '[collection-slug]', + data: { + email: 'dev@payloadcms.com', + password: 'get-out', + }, +}) +``` + +### Logout + +As Payload sets HTTP-only cookies, logging out cannot be done by just removing a cookie in JavaScript, as HTTP-only cookies are inaccessible by JS within the browser. So, Payload exposes a `logout` operation to delete the token in a safe way. + +**Example REST API logout**: +```js +const res = await fetch('http://localhost:3000/api/[collection-slug]/logout', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, +}) +``` + +**Example GraphQL Mutation**: + +``` +mutation { + logout[collection-singular-label] +} +``` + +### Refresh + +Allows for "refreshing" JWTs. If your user has a token that is about to expire, but the user is still active and using the app, you might want to use the `refresh` operation to receive a new token by sending the operation the token that is about to expire. + +This operation requires a non-expired token to send back a new one. If the user's token has already expired, you will need to allow them to log in again to retrieve a new token. + +If successful, this operation will automatically renew the user's HTTP-only cookie and will send back the updated token in JSON. + +**Example REST API token refresh**: +```js +const res = await fetch('http://localhost:3000/api/[collection-slug]/refresh', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, +}) + +const json = await res.json(); + +// JSON will be equal to the following: +/* +{ + user: { + email: 'dev@payloadcms.com', + createdAt: "2020-12-27T21:16:45.645Z", + updatedAt: "2021-01-02T18:37:41.588Z", + id: "5ae8f9bde69e394e717c8832" + }, + refreshedToken: '34o4345324...', + exp: 1609619861 +} +*/ +``` + +**Example GraphQL Mutation**: + +``` +mutation { + refreshToken[collection-singular-label] { + user { + email + } + refreshedToken + } +} +``` + + + The Refresh operation will automatically find the user's token in either a JWT header or the HTTP-only cookie. But, you can specify the token you're looking to refresh by providing the REST API with a `token` within the JSON body of the request, or by providing the GraphQL resolver a `token` arg. + + +### Verify by Email + +If your collection supports email verification, the Verify operation will be exposed which accepts a verification token and sets the user's `_verified` property to `true`, thereby allowing the user to authenticate with the Payload API. + +**Example REST API user verification**: +```js +const res = await fetch(`http://localhost:3000/api/[collection-slug]/verify/${TOKEN_HERE}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, +}) +``` + +**Example GraphQL Mutation**: + +``` +mutation { + verifyEmail[collection-singular-label](token: "TOKEN_HERE") +} +``` + +**Example Local API verification**: + +```js +const result = await payload.verifyEmail({ + collection: '[collection-slug]', + token: 'TOKEN_HERE', +}) +``` + +### Unlock + +If a user locls themselves out and you wish to deliberately unlock them, you can utilize the Unlock operation. The Admin panel features an Unlock control automatically for all collections that feature max login attempts, but you can programmatically unlock users as well by using the Unlock operation. + +To restrict who is allowed to unlock users, you can utilize the [`unlock`](/docs/access-control/config#unlock) access control function. + +**Example REST API unlock**: +```js +const res = await fetch(`http://localhost:3000/api/[collection-slug]/unlock`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, +}) +``` + +**Example GraphQL Mutation**: + +``` +mutation { + unlock[collection-singular-label] +} +``` + +**Example Local API unlock**: + +```js +const result = await payload.unlock({ + collection: '[collection-slug]', +}) +``` + +### Forgot Password + +Payload comes with built-in forgot password functionality. Submitting an email address to the Forgot Password operation will generate an email and send it to the respective email address with a link to reset their password. + +The link to reset the user's password contains a token which is what allows the user to securely reset their password. + +By default, the Forgot Password operations send users to the Payload Admin panel to reset their password, but you can customize the generated email to send users to the frontend of your app instead by [overriding the email HTML](/docs/authentication/config#forgot-password). + +**Example REST API Forgot Password**: +```js +const res = await fetch(`http://localhost:3000/api/[collection-slug]/forgot-password`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + email: 'dev@payloadcms.com', + }), +}) +``` + +**Example GraphQL Mutation**: + +``` +mutation { + forgotPassword[collection-singular-label](email: "dev@payloadcms.com") +} +``` + +**Example Local API forgot password**: + +```js +const token = await payload.forgotPassword({ + collection: '[collection-slug]', + data: { + email: 'dev@payloadcms.com', + }, + disableEmail: false // you can disable the auto-generation of email via local API +}) +``` + + + Tip:
+ You can stop the reset-password email from being sent via using the local API. This is helpful if you need to create user accounts programmatically, but not set their password for them. This effectively generates a reset password token which you can then use to send to a page you create, allowing a user to "complete" their account by setting their password. In the background, you'd use the token to "reset" their password. +
+ +### Reset Password + +After a user has "forgotten" their password and a token is generated, that token can be used to send to the reset password operation along with a new password which will allow the user to reset their password securely. + +**Example REST API Reset Password**: +```js +const res = await fetch(`http://localhost:3000/api/[collection-slug]/reset-password`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + token: 'TOKEN_GOES_HERE' + password: 'not-today', + }), +}) + +const json = await res.json(); + +// JSON will be equal to the following: +/* +{ + user: { + email: 'dev@payloadcms.com', + createdAt: "2020-12-27T21:16:45.645Z", + updatedAt: "2021-01-02T18:37:41.588Z", + id: "5ae8f9bde69e394e717c8832" + }, + token: '34o4345324...', + exp: 1609619861 +} +*/ +``` + +**Example GraphQL Mutation**: + +``` +mutation { + resetPassword[collection-singular-label](token: "TOKEN_GOES_HERE", password: "not-today") +} +``` diff --git a/docs/Authentication/overview.mdx b/docs/Authentication/overview.mdx index cc7b59a418..b2f41fc807 100644 --- a/docs/Authentication/overview.mdx +++ b/docs/Authentication/overview.mdx @@ -4,7 +4,11 @@ label: Overview order: 10 --- -Payload provides for highly secure and customizable user Authentication out of the box, which allows for users to identify themselves to Payload. Authentication is used within the Payload Admin panel itself as well as throughout your app(s) themselves however you determine necessary. + + Payload provides for highly secure and customizable user Authentication out of the box, which allows for users to identify themselves to Payload. + + +Authentication is used within the Payload Admin panel itself as well as throughout your app(s) themselves however you determine necessary. Here are some common use cases of Authentication outside of Payload's dashboard itself: @@ -15,7 +19,7 @@ Here are some common use cases of Authentication outside of Payload's dashboard By default, Payload provides you with a `User` collection that supports Authentication, which is used to access the Admin panel. But, you can add support to one or many Collections of your own. For more information on how to customize, override, or remove the default `User` collection, [click here](/docs/admin#the-admin-user-collection). -### How it works +### Enabling Auth on a collection Every Payload Collection can opt-in to supporting Authentication by specifying the `auth` property on the Collection's config to either `true` or to an object containing `auth` options. @@ -32,6 +36,7 @@ const Admins = { verify: true, // Require email verification before being allowed to authenticate maxLoginAttempts: 5, // Automatically lock a user out after X amount of failed logins lockTime: 600 * 1000, // Time period to allow the max login attempts + // More options are available }, // highlight-end fields: [ @@ -62,7 +67,7 @@ Once enabled, each document that is created within the Collection can be thought [Click here](/docs/authentication/operations) for a list of all automatically-enabled Auth operations, including `login`, `logout`, `refresh`, and others. -### Token-based +### Token-based auth Successfully logging in returns a `JWT` (JSON web token) which is how a user will identify themselves to Payload. By providing this JWT via either an HTTP-only cookie or an `Authorization` header, Payload will automatically identify the user and add its user JWT data to the Express `req`, which is available throughout Payload including within access control, hooks, and more. diff --git a/docs/Authentication/using-middleware.mdx b/docs/Authentication/using-middleware.mdx index 7eb9b2f6e0..6af2bf5d3e 100644 --- a/docs/Authentication/using-middleware.mdx +++ b/docs/Authentication/using-middleware.mdx @@ -4,4 +4,45 @@ label: Using the Middleware order: 40 --- -Talk about how to use `payload.authenticate()` outside of Payload - show examples +Because Payload uses your existing Express server, you are free to add whatever logic you need to your app through endpoints of your own. However, Payload does not add its middleware to your Express app itself—instead, ist scopes all of its middelware to Payload-specific routers. + +This approach has a ton of benefits - it's great for isolation of concerns and limiting scope, but it also means that your additional routes won't have access to Payload's user authentication. + +If you would like to make use of Payload's built-in authentication within your custom routes, you can to add Payload's authentication middeware. + +Example in `server.js`: +```js +import express from 'express'; +import payload from 'payload'; + +const app = express(); + +payload.init({ + secret: 'PAYLOAD_SECRET_KEY', + mongoURL: 'mongodb://localhost/payload', + express: app, +}); + +const router = express.Router(); + +router.use(payload.authenticate); // highlight-line + +router.get('/', (req, res) => { + if (req.user) { + return res.send(`Authenticated successfully as ${req.user.email}.`); + } + + return res.send('Not authenticated'); +}); + +app.use('/some-route-here', router); + +app.listen(3000, async () => { + payload.logger.info(`listening on ${3000}...`); +}); + +``` + + + With Payload's auth middleware, you can make full use of Payload's authentication features within your own Express endpoints. +