clerk / javascript

Official JavaScript repository for Clerk authentication
https://clerk.com
MIT License
1.14k stars 258 forks source link

getServerData Support #189

Closed graysonhicks closed 2 years ago

graysonhicks commented 2 years ago

First, this plugin is great! Probably the most seamless auth experience I've had in a while.

I'm looking to add support for SSR auth with Gatsby. Since Gatsby 4 in October 2021, Gatsby has supported SSR rendering pages outside of SSG. The pattern is similar to Next.js, but has a few differences. I have found that using the withServerSideAuth function basically works with Gatsby, but I'm not sure it's working as well as it could.

First Gatsby relies on exporting an async function called getServerData. That makes following the HOC pattern difficult. When trying to copy and paste the example directly, the context will be undefined inside withServerSideAuth:

export const getServerData = withServerSideAuth(({ req, resolvedUrl }) => {
  const { sessionId } = req.auth

  if (!sessionId) {
    return { redirect: { destination: '/sign-in?redirect_url=' + resolvedUrl } }
  }

  return { props: {} }
})

The Gatsby pattern looks more like this:

export async function getServerData(context) {
  try {
    withServerSideAuth(context)

    return {
      status: 200, 
      props: {
        test: 'foo',
      },
      headers: {},
    }
  } catch (err) {
    console.log(err)
    return {
      status: 500,
      headers: {},
      props: {},
    }
  }
}

With this pattern, the auth actually seems to work (it redirects to the auth url from Clerk), so I assume the Node logic from the Clerk library is working. When logged in, it successfully renders the page. But note that there is no callback to get the sessionId or resolvedUrl.

My guess is that this plugin could have an ssr folder that tweaks this same logic slightly and allows for SSR auth with Gatsby as well. Something like this:

import { serverSideAuth } from 'gatsby-plugin-clerk'

// ... components

export async function getServerData(context) {
  try {
    const auth = await serverSideAuth(context)

  const { sessionId } = auth.req;

  if (!sessionId) {
    return { redirect: { destination: "/sign-in?redirect_url=" + resolvedUrl } };
  }

    return {
      status: 200, 
      props: {
        test: 'foo',
      },
      headers: {},
    }
  } catch (err) {
    console.log(err)
    return {
      status: 500,
      headers: {},
      props: {},
    }
  }
}

Or the callback could be passed in to serverSideAuth as well. Either way, I think the existing Next.js pattern is already very, very close to what would work here, with maybe additional tweaks to sanitizing/serializing props that might not be needed.

Happy to help with this, thanks!

nikosdouvlis commented 2 years ago

Hello @graysonhicks 👋🏻

Great to hear that you liked the auth experience, even if this plugin is mainly focused on CSR until now :) Happy to say that updating this plugin to fully leverage the SSR capabilities of Clerk v3 that was released two weeks ago was on our roadmap already; the actual implementation would be starting next week.

I think you're correct; the final solution could look very similar to @clerk/nextjs, but I'd like to explore how we can handle the "interstitial" page. When we detect that a JWT is expired (among other edge cases), we use this interstitial page to refresh the JWT and then reload the page. In the @clerk/nextjs to ensure no user logic runs before we get a fresh JWT, the interstitial page is thrown by middleware (withServerSideAuth or withAuth in _middleware functions) before the application does any processing. (Please note that we have plans to replace this logic soon.)

It'd be great if you could give me a couple of days to run some tests internally and get back to you once I can share more details about the above.

We always aim to provide the best possible DX, so could you please share the kind of public API (HOC vs. callback, and possible naming) you think would feel more natural to a Gatsby user? I believe you are just the right person to ask :)

Gatsby is fantastic and we're more than happy to work on this plugin again

graysonhicks commented 2 years ago

@nikosdouvlis Awesome to hear! I'm going to share this with the OSS team and will get you some feedback.

graysonhicks commented 2 years ago

I think a minimal gatsby site using the Clerk plugin would be a good first step. Maybe this could be a good place to do that on a branch. That would make npm link easy to use for testing. @pieh