Simplify your stack and build anything. Or everything.
Build tomorrow’s web with a modern solution you truly own.
Code-based nature means you can build on top of it to power anything.
It’s time to take back your content infrastructure.

Rest api - restrict users apiKey to edit ONLY their profile

default discord avatar
erv27122last year
6

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.

  • default discord avatar
    hristo6004last year
    https://payloadcms.com/docs/access-control/collections#collection-access-control

    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

    id

    you're editing, i.e. the user document matches with

    user.id

    that's coming from the request.

  • default discord avatar
    erv27122last year

    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?

  • default discord avatar
    hristo6004last year

    I'm not using any API keys, what I do is I useJWT like so (where

    payloadToken

    is

    '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.

  • default discord avatar
    erv27122last year

    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?

  • default discord avatar
    hristo6004last year
    @1122834387956940892

    In this case you'll simply have to compare your

    user.id

    that you get from the

    req

    object with something from the

    appUsers

    that tells you the user of the current ID. The

    id

    in 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/1259922148240396349

    So you can have a new field, like

    createdBy

    or

    appUser

    , or

    profile

    , or similar, that links to the user, populate that automatically on creation and then use it to compare with

    user.id

    from

    req

    .



    EDIT: Oh sorry, I thought all of your users are in

    users

    and

    appUsers

    contains 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.

  • default discord avatar
    erv27122last year

    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 on GitHub

Star

Chat on Discord

Discord

online

Can't find what you're looking for?

Get dedicated engineering support directly from the Payload team.