Skip to main content
Question

monday code full-stack app: sessionToken vs OAuth access token & user permissions

  • January 15, 2026
  • 5 replies
  • 31 views

Hi everyone,

I’m building a full-stack app using monday code (React UI + Express backend) and I want to make sure I’m following the correct authentication and authorization best practices.

Current architecture

UI → Backend authentication

  • From the UI, I fetch monday.get("sessionToken")

  • I send it to my backend in a request header

  • In the backend, I verify/decode the sessionToken using my client_secret

  • This works well for validating that the request comes from monday and from my app

When decoding the sessionToken JWT, I noticed that it does not include a short-lived token that can be used to call monday APIs.

 

OAuth & monday API calls

  • I implemented a standard OAuth flow

  • In the OAuth callback, I store the returned OAuth access token in Secure Storage

  • When calling monday APIs from the backend, I retrieve that token and use it like this:

 
import { ApiClient } from "@mondaydotcomorg/api"; 
const mondayApiClient = new ApiClient({ token: accessToken });
const query = ` query { custom_activity { color icon_id id name type } } `;
const response = await mondayApiClient.request(query);

The concern

Since the backend uses the app’s OAuth access token, API calls are executed with the permissions granted to the app, not necessarily the permissions of the current user interacting with the UI.

This means a user could potentially trigger backend endpoints that:

  • Perform actions they personally don’t have permission for

  • For example: updating an item, creating updates, modifying board data, etc.

 

I want to ensure my app design aligns with monday’s security model and recommended approach.

Thanks in advance for any guidance 🙏

5 replies

mitchell.hudson

Hey ​@emrekaraduman ,

You have a few options

 

1 - Recommended: Use the sessionToken to make API calls from the client side in your react app. This ensures that the user permissions match the logged in user.

 

2 - What you currently have, this is probably the easiest option outside of the first suggestion, but like you mentioned, your permissions may change. Sometimes this is required, i.e. you may want to perform an elevated action in some cases

 

3 - You can get a shortLivedToken when you trigger a custom automation (workflow/automation), but these need to be set up on a column change, etc… on the board and is quite annoying for the end user to manage this, requires lots of training, etc… and you would probably still end up with the same issue above that you are using a token for the user who added the automation, not the current user.

 

Is there a reason you can’t use the client side SDK to make the API calls?

It may just require sending a few requests between your front and back end to transfer the data you need and ultimately end up making the API request in your front end.


  • Author
  • Participating Frequently
  • January 15, 2026

Hey ​@mitchell.hudson , thanks for the reply — that helps clarify the general model 👍
Let me explain why client-side API calls don’t fully work for my use case, and where my concern comes from.

In my app, I have scheduler / background events that run without an active UI context.
Those scheduled executions read data from app storage, so the data must be stored and accessed on the backend.

I’m using:

 

import { Storage } from "@mondaycom/apps-sdk";

Because of this:

  • The UI sends structured objects to the backend

  • The backend persists them in Storage

  • Later, a scheduler event reads that stored data and performs actions

So moving all API calls to the client side isn’t really an option, because:

  • Schedulers don’t have a user session

  • They rely entirely on backend + storage state

The actual security concern

My backend endpoints are exposed under a live URL.

A legitimate flow today looks like this:

  1. User interacts with the UI

  2. UI sends:

    • sessionToken

    • an object containing an itemId (that the user does have access to)

  3. Backend:

    • decodes & verifies sessionToken

    • stores the object in Storage

However, the risk scenario is:

  • A user copies the request from the Network tab

  • Replays it via curl / Postman

  • Changes the itemId to another item they don’t have permission for

  • The sessionToken is still valid

  • My middleware accepts the request

  • The backend stores an itemId the user should not be allowed to reference

At that point, the backend should reject the request, but:

  • The OAuth access token I’m using represents the app

  • Not the current user

  • So permission validation at the backend level becomes ambiguous

That’s the gap I’m trying to reason about —
not how to elevate permissions, but how to avoid persisting data for resources the current user isn’t allowed to touch, when everything looks valid from a token perspective.

Just wanted to clarify the constraint and the concern clearly — appreciate any thoughts on whether this aligns with monday’s intended security model.


mitchell.hudson

You may be able to get around that without needing to expose any of those endpoints
 

You can store and read storage in both front and and back end via global storage - https://developer.monday.com/apps/docs/mondaystorage#global-level
You just need to set shared in the options

monday seperates this on a accountId + appId basis so there is no risk between different accounts, etc...

You can do all of your validation on the front end using the session token, save the itemId to global storage
Then in your backend you can query the storage values using the app’s API token 

Of course you may still run into an issue where the itemId that is stored in storage isn’t accessable by the user who authorised your app when processing your backend (although you will likely still have that issue with your current setup unless you are also verifying the itemId with both sessionToken and the app API token)

 

This probably doesn’t solve your root concern though, given someone could still clone the network request and alter the itemId that is being stored in storage


I can’t think of a way to protect against that without performing a full validation on the backend, i.e. getting the board permissions, checking the if the user has access to that item (i.e is assigned to a people column if needed), etc…

 


  • Author
  • Participating Frequently
  • January 20, 2026

Hi ​@mitchell.hudson ,

Thanks for the Global Storage tip—that’s a clever way to handle data sharing without exposing endpoints, though as you noted, the root risk of a replayed request with a manipulated itemId remains.

Regarding the "full validation" on the backend you mentioned:

Could you elaborate on what that looks like in practice with the monday API?

I’ve looked through the Core, Apps, and Marketplace API References, and I couldn’t find any explicit Permission object or a query/mutation that allows checking access levels (e.g., something like canUserEdit(userId, itemId)).

Since my backend executes requests using the App’s OAuth Token (which often has broader access than the specific user), simply querying the item will likely succeed even if the user shouldn't have access.

If there is a specific GraphQL pattern or endpoint for verifying User X’s access to Item Y via the backend, I’d love to know about it!

Thanks again for your help.


mitchell.hudson

@emrekaraduman 

 

Yeah, this is where it gets messy…

 

So you need to check the board, workspace, user and team permissions.

 

Firstly, you need to get the userId for User X.

 

query { boards (ids: $id) { permissions board_kind owners users team_owners team_subscribers workspace { kind team_owner_subscribers team_subscribers users_subscribers } } }

Using this query, you would then check the following

  • If board_kind is shareable/private then you need to check if the user has been added to the board directly (via owners or users) or via a team (get the team ID in team_owners and team_subscribers) and then check if the user is part of this team
    • You should be able to make a single query on the users query to retrieve all teams the user belongs to

  • If the board_kind is public, you will need to check the workspace kind, if this is closed, then you need to check the workspace teams and users (similar to board kind) to check if that user has access

  • If the board_kind is public and workspace kind is open, you need to check if the user has access to the workspace in the given product. I.e if I have a workspace in CRM, and I don’t have a CRM license I can view only, i can’t edit. (If you only need to worry about view only, then you can skip this step. I don’t actually know if you can check all products the user has access to)

  • If you have verified the steps above, you then need to check permissions on the board. This could beassignee collaborators everyone owners

    • If assignee, then you need to check all board People columns, check to see if User X is assigned to one of these columns directly of via a team

    • If everyone, then the user should have access

    • I don’t quite understand collaborators or owners (I assume owners only allows board owners, and collaborators is anyone assigned in a people column or via item_subscribers on the item level. It’s also a little tough because the https://developer.monday.com/api-reference/reference/boards#set-board-permission mutation accepts different values then the permissions field provides back.

 

I haven’t tested the query but it should work. It is messy, and would require maintence if monday ever add new permissions or change the structure, etc… but it should give the verification you are looking for.