Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remix.run Clerk SDK server functions not returning permissions, roles, organization info, etc... #3211

Closed
4 tasks done
LumDermaku opened this issue Apr 17, 2024 · 6 comments
Closed
4 tasks done
Labels
needs-triage A ticket that needs to be triaged by a team member

Comments

@LumDermaku
Copy link

LumDermaku commented Apr 17, 2024

Preliminary Checks

Reproduction

https://github.com/LumDermaku/remix-clerk-bug

Publishable key

pk_test_bmF0aXZlLWNvdy01NS5jbGVyay5hY2NvdW50cy5kZXYk

Description

I cannot get any "custom" user info (org-related like permissions, roles, org itself) through Clerk SDK's server-related functions, such as getAuth from @clerk/remix/ssr.server as described by Clerk docs here.

Not even the `` component works when i pass my custom permission in the permission prop. I made sure to copy-paste it from the Clerk dashboard. Same applies to roles.

The only way I can get all user info from Clerk is through client-components hooks, like useUser's getOrganizationMemberships() method, which returns all the correct info from Clerk, but for the moment I cannot use this when I can use `` for getting the user Info from the client-side, or getAuth from server-side which is even better for me.
I have also customized the session token with the following:
"{permissions": "{{org_membership.permissions}}"}

The project is on Remix.run v2 with Vite v5.1.0 on Node.js v20.11.0, with Clerk/Remix 4.0.0-beta.43. I have to use the beta version otherwise I'll get that infamous Error 401 issue when running on v3.2.3

Steps to reproduce:

  1. Create a remix route but make sure you are signed in. For demo purposes, use getAuth in a loader, and also the `` component even though in this case if you use one you don't need the other:
export const loader = async (args: LoaderFunctionArgs) => {
    const currentUser = await getAuth(args);
    console.log(' *  user info from server * : ', currentUser);
    console.log(' * user permission from server * :', currentUser.has({ permission: 'my:custom:permission' }));
    return json(currentUser)
}
export default function SomeComponent() {
const user = useUser()
... useEffect code to invoke getOrganizationMemberships() ...
console.log(' current user\'s permissions: ', currentUserPermissions)
return <Protect permission="my:custom:permission" fallback={<p>this renders</p>}>
       This part will not be rendered
</Protect>
}

Expected behavior:
Based on the codebase (check the github repo)

On the server console.logs, i should get:

* user info from server* : {
  actor: undefined,
  sessionClaims: {
    ...default clerk properties...
    permissions: ['my:custom:permission', 'what:ever'], <----------- i should get my permissions in here
    ...default clerk properties...
  },
  sessionId: 'sess_someSession',
  userId: 'user_aUserId',
  orgId: 'myOrgId', <----------- i should get orgId here
  orgRole: 'myOrgRole', <----------- i should get an org role here
  orgSlug: '', <----------- same story
  orgPermissions: undefined, <----------- here, too
  getToken: [AsyncFunction (anonymous)],
  has: [Function (anonymous)],
  debug: [Function (anonymous)],
  user: undefined, <----------- should i get anything here?
  organization: undefined <----------- should i get anything here?
}
 * user has the permission i need from server * : true

Actual behavior:

* user info from server* :  {
  actor: undefined,
  sessionClaims: {
    ...default clerk properties...
    permissions: null,
    ...default clerk properties...
  },
  sessionId: 'sess_someSession',
  userId: 'user_aUserId',
  orgId: undefined,
  orgRole: undefined,
  orgSlug: undefined,
  orgPermissions: undefined,
  getToken: [AsyncFunction (anonymous)],
  has: [Function (anonymous)],
  debug: [Function (anonymous)],
  user: undefined,
  organization: undefined
}
 * user has the permission i need from server * : false

On the client using the useUser() hook, i am successfully getting all the signed-in user's info from Clerk.

current user's permissions: (7) ['org:sys_domains:manage', 'org:sys_domains:read', 'org:sys_memberships:manage', 'org:sys_memberships:read', 'org:sys_profile:delete', 'org:sys_profile:manage', 'my:custom:permission']

Environment

System:
    OS: Linux 5.15 Ubuntu 22.04.4 LTS 22.04.4 LTS (Jammy Jellyfish)
    CPU: (12) x64 Intel(R) Core(TM) i7-10710U CPU @ 1.10GHz
    Memory: 10.95 GB / 14.64 GB
    Container: Yes
    Shell: 5.1.16 - /bin/bash
  Binaries:
    Node: 20.11.0 - ~/.nvm/versions/node/v20.11.0/bin/node
    Yarn: 1.22.17 - /mnt/c/Users/Lum Dermaku/AppData/Roaming/npm/yarn
    npm: 10.2.4 - ~/.nvm/versions/node/v20.11.0/bin/npm
    pnpm: 8.15.4 - ~/.local/share/pnpm/pnpm
  npmPackages:
    @clerk/remix: 4.0.0-beta.43 => 4.0.0-beta.43
    @remix-run/dev: ^2.8.1 => 2.8.1 
    @remix-run/node: ^2.8.1 => 2.8.1 
    @remix-run/react: ^2.8.1 => 2.8.1 
    @remix-run/serve: ^2.8.1 => 2.8.1 
    @types/react: ^18.2.20 => 18.2.74 
    @types/react-dom: ^18.2.7 => 18.2.23 
    @typescript-eslint/eslint-plugin: ^6.7.4 => 6.21.0 
    @typescript-eslint/parser: ^6.7.4 => 6.21.0
    class-variance-authority: ^0.7.0 => 0.7.0
    eslint: ^8.38.0 => 8.57.0 
    eslint-import-resolver-typescript: ^3.6.1 => 3.6.1 
    eslint-plugin-import: ^2.28.1 => 2.29.1 
    eslint-plugin-jsx-a11y: ^6.7.1 => 6.8.0 
    eslint-plugin-react: ^7.33.2 => 7.34.1 
    eslint-plugin-react-hooks: ^4.6.0 => 4.6.0 
    isbot: ^4.1.0 => 4.4.0
    react: ^18.2.0 => 18.2.0 
    react-dom: ^18.2.0 => 18.2.0
    typescript: ^5.1.6 => 5.4.3 
    vite: ^5.1.0 => 5.2.8 
    vite-tsconfig-paths: ^4.2.1 => 4.3.2
@LumDermaku LumDermaku added the needs-triage A ticket that needs to be triaged by a team member label Apr 17, 2024
@panteliselef
Copy link
Member

Hey @LumDermaku, I quickly wanna mention that not receiving any of org:sys_* permissions from getAuth() is expected. Only your custom permissions should be returned from there.

Thanks for providing us with the reproduction repo. I used both @clerk/remix@3 and the @clerk/remix@beta I could not replicate, posting an image as example below.

image

The reason you were seeing a 401 error with v3 was probably dues not declaring and using the ClerkErrorBoundary

@LumDermaku
Copy link
Author

Thank you for your reply @panteliselef.
I actually need only custom permissions returned by getAuth(), but still I am not getting the same response like yours, not even orgId, or the default orgPermissions. Maybe something is wrong with Clerk's dev environment? Not sure if my pub key can help.

I have added a custom prop in the session token "{permissions": "{{org_membership.permissions}}"} but I am still getting null for that.

Since getAuth in loaders seem to work for you, is the <Protect> component also working? I am sure my user has the needed permissions since with the client-side useUser() hook I can see all permissions, including the custom ones.

image

For the Error Boundary, I've already tried implementing it but was still having the error. Someone from your team already recommended to upgrade to the latest beta which fixes that issues.

@panteliselef
Copy link
Member

So with clerk, you need to specify an active organization. Easiest way to do this would be to mount the OrganizationSwitcher component and select or create an organization from the UI.

We also have this guide on how to "force" users into an organization, in case your app would only work with a populated orgId. Have a look here

@LumDermaku
Copy link
Author

@panteliselef the link you provided has a Next.js example, and for this section, there's a server example and client example. Both assume that you get an orgId prop out of the returned object, either from the auth() server function (that looks like the Next.js version of getAuth() for Remix) or from the useAuth() hook.

Either I am doing something wrong with Clerk dashboard setup or the Remix SDK is not working. I even created a custom role now, assigned my custom permission to it; Added a new user to my Org and assigned this custom role to it. Still not getting even an orgId from the server function.
image.

Thank you for your time.

@LumDermaku
Copy link
Author

LumDermaku commented Apr 18, 2024

@panteliselef I used the <OrganizationList /> and selected the Org i created instead of the personal one. After i clicked it, refreshed the page, now the response from Clerk is looking good in console, just like your response.

Before I begin writing the logic to automatically set one of the orgs as Active by default (this is our case), the docs say that the user will automatically have an org set as Active as soon as they accept the invitation. What type of invitation in this case? Email invites? I didn't accept any email invitations when I added an email of mine to my Org.

There will only be one Custom org that I need some specific users to be part of, and everyone added there should have it set as Active, without having to manually choose an org from our web app since this is kind of a deal breaker.

@LumDermaku
Copy link
Author

@panteliselef
I think we can somehow mark this as done or closed. For anyone interested in what i did:
I ended up writing that custom logic in a component that gets imported in root.tsx. You can also do it in _index.tsx component because it is rendered within the <Outlet /> of root.tsx.
After the user signs-in, the homepage is loaded and that component does the following:

  1. invokes useOrganizationList() hook.
  2. In a useEffect hook, i check if userMemberships.data array has an element who'se name property is the same as the one i want. I invoke the setActive() function with the org id of the user's membership. Don't forget to add the objects, functions, variables in the dependency array of useEffect.
  3. With a global state manager I set a boolean value which i use to render a button conditionally. In the useEffect hook i also have some custom logic to check whether the global state already has/doesn't have anything set.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs-triage A ticket that needs to be triaged by a team member
Projects
None yet
Development

No branches or pull requests

2 participants