Clerk: "The <SignUp/> and <SignIn/> components cannot render when a user is already signed in, unless the application allows multiple sessions. Since a user is signed in and this application only allows a single session..."

Related to this issue

@colinclerk @dom

After signing in, I get the following message as many as 132 times:

clerk.browser.js:2 :lock: Clerk:
The SignUp and SignIn components cannot render when a user is already signed in, unless the application allows multiple sessions. Since a user is signed in and this application only allows a single session, Clerk is redirecting to the Home URL instead.
(This notice only appears in development)

in addition to seeing the page and address bar flicker.

web/src/Routes.tsx:

import { Router, Route, Set } from '@redwoodjs/router'

import { useAuth } from './auth'
import Home from './components/Home/Home'
import SignIn from './components/SignIn/SignIn'
import MainLayout from './layouts/MainLayout/MainLayout'

const Routes = () => {
  return (
    <Router useAuth={useAuth}>
      <Route notfound page={NotFoundPage} />
      <Route path="/sign-in" page={SignIn} name="signIn" />
      <Set wrap={MainLayout} private unauthenticated="signIn">
        <Route path="/" page={Home} name="home" />
      </Set>
    </Router>
  )
}

export default Routes
 "dependencies": {
    "@clerk/clerk-react": "^4",
    "@redwoodjs/auth-clerk-web": "4.3.1",
    "@redwoodjs/forms": "4.3.1",
    "@redwoodjs/router": "4.3.1",
    "@redwoodjs/web": "4.3.1",
    "prop-types": "15.8.1",
    "react": "17.0.2",
    "react-dom": "17.0.2"
  }

What may be unique compared to the example in the linked issue is that my root path is private.

I created a small test app to isolate this issue - I can provide provide a GitHub repo link if necessary.

2 Likes

Hey @zorteaj, thanks for reporting, a GitHub repo would be great! :pray: @asher-eastham tagging you because you liked the post but feel free to ignore, are you also seeing an issue like this?

Thanks @dom

https://github.com/zorteaj/rw-clerk-test

Also, I’m just realizing that the issue only seems to occur when signing in by entering an email address and verification code, as opposed to signing in via OAuth

1 Like

Thanks @dom! @zorteaj is one of our engineers at MarvelBus – essentially, this started happening when we tried to mark the ParentLayout set from the previous thread private.

1 Like

Thanks @asher-eastham and thanks for the reproduction @zorteaj, had no trouble seeing the issue. It seems like it’s all happening during the call to getCurrentUser here:

Even though the bug is the same, it seems to be happening for a different reason internally this time. If I set skipFetchCurrentUser to true in the ClerkRwAuthProvider in web/src/auth.tsx, the behavior goes away. (This isn’t a real solution; we’d like the currentUser of course.)

   return (
     <ClerkProvider {...clerkOptions} navigate={(to) => navigate(to)}>
+       <ClerkRwAuthProvider skipFetchCurrentUser={true}>
         {children}
         <ClerkStatusUpdater />
       </ClerkRwAuthProvider>
     </ClerkProvider>
   )

It’s possible a lot of information is duplicated between currentUser and userMetadata. I think currentUser is what your GraphQL api returns.

I’m mostly just providing this information to keep you in the loop; I’ll keep debugging and report back when I have a real fix.

2 Likes

Another udpate @zorteaj: @Tobbe and I paired on this for a bit earlier and came up with this fix: fix(clerk auth): split setting `AuthProvider` state by jtoar · Pull Request #7852 · redwoodjs/redwood · GitHub. But it doesn’t seem to work across auth providers yet. I’m debugging the failing tests.

But I do think there’s a legitimate workaround for Clerk specifically that you could take advantage of right now since 1) the new auth api has a dedicated useCurrentUser option, and 2) Clerk is on the window object. (Credit to @Chris for this fix too)

// web/src/auth.tsx

export const { AuthProvider: ClerkRwAuthProvider, useAuth } = createAuth({
  useCurrentUser() {
    const clerk = typeof window === 'undefined' ? undefined : ((window as any).Clerk as Clerk)

    return clerk?.user
  },
})

This will get the current user using the provided function, instead of trying to fetch it from the api side. We originally added it for users who weren’t using the GraphQL api, but it actually makes sense with Clerk.

Let me know if you have any questions, will post updates again when I have them!

Thanks a lot @dom - this workaround does seem to work for the test case (although, FYI, I do get the warning in the title of this post one time); but the routing in our actual app relies on user roles, which we’re currently fetching from our database. I’m concerned that making an api call to add the user’s roles to the user object returned in useCurentUser would be a security concern, since that code in the browser can be modified, not mention this would make useCurrentUser async, which may or may not be a problem.

Is there a safe way to get roles from our db working with this solution?

@zorteaj another potential fix here for you try out via yarn patch. I’m more confident in this one. We can do it together when you’re available, but if you want to try it, the steps are:

yarn patch
# Then open the directory yarn gives you,
# and replace the `useReauthenticate.js` file with the contents of this gist:
# https://gist.github.com/jtoar/5a51ef95fda92e23d06059c46fe08f42
yarn patch-commit -s ... # The argument here is the same directory yarn gave you
yarn install
# Check if it worked:
yarn rw dev

Friendlier gist link:

Hey @dom - it looks so far like everything works as expected with this patch!

One small issue, which I’m assuming is related, is that when I sign out I get this error:

api | 09:46:13 :rotating_light: graphql-server Error building context. Error: Exception in getAuthenticationContext: The Authorization header is not valid.
api | 09:46:13 :rotating_light: Exception in getAuthenticationContext: The Authorization header is not valid.
api |
api | :rotating_light: Error Info
api |
api | {}
api |
api | :pancakes: Error Stack
api |
api | Error: Exception in getAuthenticationContext: The Authorization header is not valid.
api | at onContextBuilding (/Users/me/MarvelBus/GitHub/marvelbus-app/node_modules/@redwoodjs/graphql-server/dist/plugins/useRedwoodAuthContext.js:30:15)
api | at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
api | at async Object.contextFactory (/Users/me/MarvelBus/GitHub/marvelbus-app/node_modules/graphql-yoga/node_modules/@envelop/core/cjs/orchestrator.js:198:45)
api | at async processRequest (/Users/me/MarvelBus/GitHub/marvelbus-app/node_modules/graphql-yoga/cjs/process-request.js:46:26)
api | at async YogaServer.getResultForParams (/Users/me/MarvelBus/GitHub/marvelbus-app/node_modules/graphql-yoga/cjs/server.js:289:26)
api | at async YogaServer.getResponse (/Users/me/MarvelBus/GitHub/marvelbus-app/node_modules/graphql-yoga/cjs/server.js:361:23)
api | at async YogaServer.handle (/Users/me/MarvelBus/GitHub/marvelbus-app/node_modules/graphql-yoga/cjs/server.js:42:34)
api | at async handlerFn (/Users/me/MarvelBus/GitHub/marvelbus-app/node_modules/@redwoodjs/graphql-server/dist/functions/graphql.js:178:24)
api | at async execFn (/Users/me/MarvelBus/GitHub/marvelbus-app/node_modules/@redwoodjs/graphql-server/dist/functions/graphql.js:233:16)
api | at async requestHandler (/Users/me/MarvelBus/GitHub/marvelbus-app/node_modules/@redwoodjs/api-server/dist/requestHandlers/awsLambdaFastify.js:72:30)

1 Like

I have a new recommendation from the Clerk team. Here’s a repo demonstrating the solution:

The change is fairly simple: tell Clerk’s SignIn/SignUp components and functions to redirect to a new route, “reauthenticate”:

That route calls Redwood auth’s reauthenticate function, which brings Redwood’s auth up to date:

A little background as to why this is happening. Clerk keeps track of it’s own auth state separate from Redwood. Redwood makes a component in web/src/auth.tsx to keep its auth state in sync with Clerk’s, but because useEffect runs after render, Redwood’s auth state never gets synchronized with Clerk’s:

Telling Clerk to redirect to a reauthenticate Route gives Redwood’s auth state a chance to get in sync before trying to render auth-dependent pages.

After discussing the solution above a bit, we realize it may not always be desirable to include a redirect page. Here’s another solution from the Clerk team; I’ve tried it locally with success, and it avoids having a “middleware” page. Copy this template to your web/src/auth.tsx file, taking care to preserve any custom changes you may have made:

This calls reauthenticate before navigate which syncs Redwood’s auth state up with Clerk’s before the router tries to render the page.