djangonext.jsjwtauthorizationrbac

How to handle feature and permission-based authorization in Next.js without delaying initial render?


I’m building a multi-tenant SaaS application with Django Ninja as the backend and Next.js as the frontend. I’m running into a problem around handling RBAC permissions and org-level feature entitlements without causing bad UI behaviour.

Current setup

Problem

For RBAC, I first tried fetching permissions/features from an API after login. But in Next.js, this caused a UX issue:

To avoid this for RBAC, I moved the permissions into the JWT, so the frontend can instantly check them at page load. This works well, but I’m unsure if I should do the same for feature entitlements (since those are org-level and can change dynamically).

Question

Key constraints


Solution

  • tldr: use SSR.

    This may not be the cleanest solution, but at least it should work.
    I assume that at some point you will eventually load the user from the backend, to display name, avatar, etc. So why not preload it on the backend (NextJS) during the SSR phase, so requests can be cached, UI will be prerendered, no need to clutter JWT with additional data. About authorization, you can just read the client cookie in the SSR handler and pass it on to the backend in the Authorization header. If needed, a middleware on the server can convert it to a cookie (at the end, it's just another header), so you won't even have to change any backend auth for that.
    Another plus of this method is that if you host your backend and frontend together on one machine, NextJS can talk directly to your backend, so fetching the user server-to-server will be even faster than making this request from the frontend.
    Finally you if you use OpenApiGenerator, you can modify the client in such a way that it automatically chooses the right address (https://api.yourAwesomeProject.com or http://api_container_name:5000/) for the server based on the presence of a window object and accepts an optional argument for client cookies to forward when needed.

    Notes:

    1. If using getInitialProps, beware that when the user navigates between pages using NextLink or back/forward buttons (in browser), it will run on the frontend, inside of it you can check if window is undefined to distinguish between scenarios
    2. useful links about app router prerendering , layouts , fetching data
    3. Such global info as user is really convinient to have in a ReactContext
    4. If you want to update user later on without refreshing the page you can combine ReactContext with UseSWR, it accepts initialValue argument (you pass it from SSR) and can be configured to refetch user on certaintTiggers, I would strongly advise to create a hook useUser based on ReactContext and SSR that would expose any methods assosiated with modifing user. swr docs