Morning! How can I correctly set up a flow to allow users (from my collection appUsers) to edit their profile details and only theirs? Currently I have setup their userApiKey which is working but surely this key would actually allow them to change any profile, or for that matter any part of our cms if they were to hack in. I guess my real question is, is there a way to restrict a users api key to only be accepted on their profile and thats it? Or should I be using their JWT instead? Just want to make sure I'm following the best practises. I hope that makes sense! I am protecting our endpoints doing the updating with our own internal auth too but I just want to make sure I'm doing things as securely as possible.
You could simply write an update access function, like so:
import type { Access } from 'payload'
export const adminsAndUser: Access = ({ req: { user } }) => {
if (!user) return false
if (user.role === 'admin') {
return true
}
return {
id: {
equals: user.id
}
}
}This checks whether the document
idyou're editing, i.e. the user document matches with
user.idthat's coming from the request.
Hi
@415764121280184330, thank you for getting back to me! That sounds like exactly what I need, thank you! So would I use their apitoken to auth and it works out the user in the request as below?
try {
const req = await fetch('{cms-url}/api/appUsers/{id}', {
method: "PATCH",
credentials: "include",
headers: {
"Content-Type": "application/json",
Authorization: `appUsers API-Key ${userApiKey}`,
},
body: JSON.stringify({
firstName: body.firstName,
lastName: body.lastName
}),
})
const data = await req.json()
} catch (err) {
console.log(err)
}Or do I need to pass the user object in the body?
I'm not using any API keys, what I do is I useJWT like so (where
payloadTokenis
'payload-token':
"use server"
import { payloadToken } from "@/api/token"
export const createJob = async (data) => {
const { cookies } = await import('next/headers')
const token = cookies().get(payloadToken)
const doc = await fetch(`${process.env.NEXT_PUBLIC_SERVER_URL}/api/jobs`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(token?.value ? { Authorization: `JWT ${token.value}` } : {}),
},
body: JSON.stringify(data),
cache: 'no-store',
})
?.then(res => res.json())
?.then(res => {
if (res.errors) throw new Error(res?.errors?.[0]?.message ?? 'Error creating job')
return res?.message ?? 'Job created successfully'
})
return doc
}This is for creating, but same thing basically (PATCH instead of PUT) for updating.
When using access hooks in your collections, yes, you get a request object with information about the user. So after you set up your Payload collection to accept updates only as I showed above, you can make the request. It should work with API keys as well, but I haven't tried it.
Now that I looked again at your code, if you're doing it on the client,
credentials: "include",and the header auth should cover you.
Hi
@415764121280184330, sorry I'm slightly confused about the access as I have 2 user collections, users and appUsers - I want to restrct the access of editing an appUser but the access returns the user type, not the app user?
In this case you'll simply have to compare your
user.idthat you get from the
reqobject with something from the
appUsersthat tells you the user of the current ID. The
idin that case won't help, but you can create another field where you can save the user's ID to compare it later.
Check this out:
https://discord.com/channels/967097582721572934/1259919447762796566/1259922148240396349So you can have a new field, like
createdByor
appUser, or
profile, or similar, that links to the user, populate that automatically on creation and then use it to compare with
user.idfrom
req.
EDIT: Oh sorry, I thought all of your users are in
usersand
appUserscontains the users' profile data or something. In that case this should be working:
return {
id: {
equals: user.id
}
}The access gives you req object that has the current user's data. Try logging in with an appUser and see (console.log) what you have for the user. It returns either a boolean or a 'where' query that you can use to compare.
Sorry for the delayed response, but yes your right its working perfectly thank you! The only change I made was to check which collection the user was coming from:
import type { Access } from 'payload'
export const UpdateAppUsers: Access = ({ req: { user } }) => {
console.log('user', user)
if (!user) return false
if (user?.collection === 'users') {
return true
}
return {
id: {
equals: user.id,
},
}
}So our admin users can always update others if needed - thank you!
Star
Discord
online
Get dedicated engineering support directly from the Payload team.