NextJS, Clerk - Organisation won't apply on user

1.7k views Asked by At

I'm using Clerk to do user auth for my app's back office and i applied an organization on a user with the admin role on Clerk's dashboard. However when inside the Clerk middleware and trying to access user infos it shows the user but the orgId is undefined.

First, i applied org to user on Clerk dashboard : enter image description here enter image description here

Tried console.log the user infos with the auth() method provided in the afterAuth option :

import { authMiddleware, redirectToSignUp } from "@clerk/nextjs";

export default authMiddleware({
    afterAuth(auth, req, evt) {
        console.log(auth)

    },
    publicRoutes: [
        "/api/albums/all",
        "/",
        "/player/(.*)",
        "/login"
    ],
});

export const config = {
    matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'],
};

This is the console.log result :

{
  actor: undefined,
  sessionClaims: {
  azp: 'http://localhost:3000',
  exp: 1698860167,
  iat: 1698860107,
  iss: 'https://able-pelican-18.clerk.accounts.dev',
  nbf: 1698860097,
  sid: 'sess_2XaGCNVMHEqLEujCfqDe88xxVo7',
  sub: 'user_2XaG41***********RM8'
},
  sessionId: 'sess_2XaGCNVMHEqLEujCfqDe88xxVo7',
  session: undefined,
  userId: 'user_2XaG41***********RM8',
  user: undefined,
  orgId: undefined,
  orgRole: undefined,
  orgSlug: undefined,
  organization: undefined,
  getToken: [Function],
  debug: [Function],
  isPublicRoute: true,
  isApiRoute: true
}
4

There are 4 answers

1
5war00p On

orgId only gives active Organization (Refer: https://clerk.com/docs/references/nextjs/authentication-object#authentication-object), but until unless we use we cannot set the active org. So to have this from the backend code we can use Clerk Node.js SDK and get org list of the user (either admin or a member) of the specified clerk account. One use-case is using the org list we can decide whether to redirect to /onboarding or to the /dashboard without client code (on route.ts for Next.js apps).

No configuration is required for this, one single ENV variable as specified in [1].

Sample output:

[
  _OrganizationMembership {
    id: 'orgmem_2YqOBEos2USIkjhgtjfQwBb5KsoZ',
    role: 'admin',
    publicMetadata: {},
    privateMetadata: {},
    createdAt: 1701249505835,
    updatedAt: 1701249505835,
    organization: _Organization {
      id: 'org_2YqOBFR9fAnMNyykAddjGPrZerJ',
      name: "Swaroop's Org",
      slug: 'afia-pro',
      imageUrl: 'https://img.clerk.com/eyJ0eXBlIjoiZGVmYXVsdCIsImlpZCI6Imluc18yWUFtNm9LMEM3Nm1LWFhJbjZPbk1rYzUxUHIiLCJyaWQiOiJvcmdfMllxT0JGUjlmQW5NTnl5a0FFZWpHUHJaZXJKIiwiaW5pdGlhbHMiOiJTIn0',
      hasImage: false,
      createdBy: 'user_2YlHzwOltj24hUxYFh6jTWowX3Z',
      createdAt: 1701249505821,
      updatedAt: 1701249505821,
      publicMetadata: {},
      privateMetadata: [Object],
      maxAllowedMemberships: 3,
      adminDeleteEnabled: true,
      members_count: undefined
    },
    publicUserData: _OrganizationMembershipPublicUserData {
      identifier: '[email protected]',
      firstName: 'zyzylafy',
      lastName: 'afia',
      imageUrl: 'https://img.clerk.com/eyJ0eXBlIjoiZGVmYXVsdCIsImlpZCI6Imluc18yWUFtNm9LMEM3Nm1LWFhJbjZPbk1rYzUxUHIiLCJyaWQiOiJ1c2VyXzJZbEh6d09sdGoyNGhVeFlGaDZqVFdvd1gzWiIsImluaXRpYWxzIjoiWkEifQ',
      hasImage: false,
      userId: 'user_2YlHzwOltj24hUxYFh6jTWowX3Z'
    }
  }
]

In my case I did this inside route.ts (this can be done in afterAuth function in middleware.ts):

import Clerk from "@clerk/clerk-sdk-node"
import { auth } from "@clerk/nextjs"
import { redirect } from "next/navigation"
import { NextRequest } from "next/server"

export async function GET(_request: NextRequest) {
  const { userId, orgId } = auth()

  if (userId) {
    const orgList = await Clerk.users.getOrganizationMembershipList({
      userId: userId ?? "",
    })

    if (orgList.length === 0) return redirect("/onboarding")
  }

  return redirect("/directory/supplier")
}

Hope this helps!!

0
webcu On

By default, the user, after logging in to your application, won't have a predefined org. To switch to a specific organization, you could use the Organization Switcher in the UI of your application, and if you want to handle the organization switcher directly with your code, you can use the setActive function of this hook. I haven't found a way to set up an organization automatically using backend code.

2
Geolavz On

What worked for me was using the Organization Switcher component.

I logged in with the user Admin in the Organization and switched to the organization that I want. Once this happened I was getting a response with the actual information of the Organization.

Auth Response: {
  actor: undefined,
  sessionClaims: {
  azp: 'http://localhost:3000',
  exp: 010101010101,
  iat: 010101010101,
  iss: 'https://mature-tapir-22.clerk.accounts.dev',
  nbf: 1701000000,
  sid: 'sess_2YvT2s....',
  sub: 'user_2YrWTn....'
},
  sessionId: 'sess_2YvT2s....',
  session: undefined,
  userId: 'user_2YrWTn.....',
  user: undefined,
  orgId: 'org_2Yrc.....',
  orgRole: 'admin',
  orgSlug: 'rhiz',
  organization: undefined,
  getToken: [Function],
  debug: [Function],
  isPublicRoute: true,
  isApiRoute: true
}

This is how I configured my afterAuth inside the authMiddleware

    afterAuth(auth, req:NextRequest, evt) {
    console.log('Auth Response:', auth);
    if(req.nextUrl.pathname.startsWith('/admin')){

        if(!(auth.orgRole === 'admin')){
            const redirectUrl = `${req.nextUrl.origin}/`;
            return NextResponse.redirect(redirectUrl);
        } else {
            console.log('AUTH.ORG.SLUG:', auth.orgRole);

        }
    }

I hope this helps you. I was surprised this step of switching the organization was not in Clerk's documentation.

It is a important step in order to have some kind of Role based auth.

0
nyxz On

I found the Clerk's guide Hide Personal Accounts and force organizations very helpful.

The guide says:

Setting an active organization can only be performed client-side.

Thus they give you several ways to set the active organization on the client (1 automatic approach as well). And once you have the active org then you can use the authMiddleware to restrict the access only to the users having an organization.