honojs / middleware

monorepo for Hono third-party middleware/helpers/wrappers
https://hono.dev
337 stars 111 forks source link

Getting Auth Session in a server component, Set CallbackURL issue! #483

Open aifirstd3v opened 2 months ago

aifirstd3v commented 2 months ago

I'am developing a simple NextJS app with hono & auth.js.

My Question is 1) How to make a valid hook function to get the auth session normally in server components. auth.js middlware only supports to get it in client components through useSession hook or session provider? 2) callbackUrl doesn't work with signIn(NextAuth library works! tho), I've inspected the cookie in the browser. After processing signIn and authHandler, the middleware overwrites the authjs.callback-url cookie for the default callback url. So even if I call setCookie to overwrite the cookie, it doesn't work.

For sure, The Client useSession works like the official document(readme).

import { useQuery } from '@tanstack/react-query'

export const useSession = () => {
  const { data, status } = useQuery({
    queryKey: ['session'],
    queryFn: async () => {
      const res = await fetch('/api/auth/session')
      // console.log('useSession res: ', res)

      return res.json()
    },
    staleTime: 5 * (60 * 1000),
    gcTime: 10 * (60 * 1000),
    refetchOnWindowFocus: true
  })
  return { session: data, status }
}
'use client'
import { useSession } from '@/hooks/use-session'
export default function page() {
  const { status, session } = useSession()
...

But, I'd like to get an auth session from a server component in NextJS. So, I created a hook for this purpose.

export async function checkServerSession() {
  const fetchUrl = process.env.BASE_URL + '/api/auth/session'
  // console.log("fetchUrl: ", fetchUrl)
  const res = await fetch(fetchUrl)
  const result = await res.json() 
  console.log('checkServerSession res: ', result)

  return result
}
import 'server-only'
import { checkServerSession } from '@/hooks/checkServerSession'
import { redirect } from 'next/navigation'

async function layout({ children }: { children: ReactNode }) {
  const session = await checkServerSession()

  if (!session) {
    console.log("No Session Found")
    redirect('/signin?callbackUrl=/finance')
  } else {
    console.log('Session Found: ', session)
  }
  ...

However, The session is always Null although I have've signed-in and had an auth session after github(provider) login. I need get a session in server components not client components because of a special purpose(to check the auth status in the server more securely without evaluating a client page component. ) Next-Auth library has a getServerSession() but it seems that hono auth.js middleware doesn't have such a thing.

signIn('GitHub', { redirect: false, callbackUrl: searchParams.get('callbackUrl') || '/' })
const app = new Hono()
app.use(logger(customLogger))

app.use(
  '*',
  cors({
    origin: (origin) => origin,
    allowHeaders: ['Content-Type'],
    allowMethods: ['*'],
    maxAge: 86400,
    credentials: true
  })
)

app.use('*', initAuthConfig(getAuthConfig))
const authMiddleware = async (c: Context, next: Next) => {
  console.log('👍Hono authMiddleware!')
  // console.log("Context: ", c)
  await next()
}
const setCookieMiddleware = async (c: Context, next: Next) => {
  console.log('👍Hono setCookieMiddleware!')
  setCookie(c, 'authjs.callback-url', 'http://localhost:3000/finance')
  await next()
}
app.use('/api/auth/*', authMiddleware, setCookieMiddleware, authHandler())

Any idea would be helpful 👍

yusukebe commented 2 months ago

Hi @aifirstd3v

@divyam234 Do you have any idea?

divyam234 commented 2 months ago

@aifirstd3v If you are using nextjs you should use next-auth only as both of them are tightly coupled. Your checkServerSession implementation will not work as on server side you need to get session from cookies rather than calling fetch. Also getting auth session requires passing honojs context which is not possible in server components.But you can write simple authChecker in server components also.

import { Auth } from '@auth/core'
import type { Session } from '@auth/core/types'
import type { JWT } from '@auth/core/jwt'
import type { AdapterUser } from '@auth/core/adapters'

type AuthUser = {
  session: Session
  token?: JWT
  user?: AdapterUser
}

function getServerSession(req:Request) {
  const config = initAuthConfig()  // Authjs config
  config.secret ??= process.env.AUTH_SECRET
  config.basePath ??= '/api/auth'
  config.trustHost = true
  const origin = process.env.AUTH_URL ? new URL(process.env.AUTH_URL ).origin : new URL(c.req.url).origin
  const request = new Request(`${origin}${config.basePath}/session`, {
    headers: { cookie: req.headers.get('cookie') ?? '' },
  })

  let authUser: AuthUser = {} as AuthUser

  const response = (await Auth(request, {
    ...config,
    callbacks: {
      ...config.callbacks,
      async session(...args) {
        authUser = args[0]
        const session = (await config.callbacks?.session?.(...args)) ?? args[0].session
        const user = args[0].user ?? args[0].token
        return { user, ...session } satisfies Session
      },
    },
  })) as Response

  const session = (await response.json()) as Session | null

  return session && session.user ? authUser : null
}

You can add this in Nextjs middleware

aifirstd3v commented 2 months ago

@divyam234 Thing is how to call the getServerSession(req) in server components with Request parameter? I don't know how to pass the req in nextjs server component..

Also, setting callbackUrl doesn't work. After logging, there is no way return to the callback url that I set.

Well..If I use this auth middleware in react 19 with server components, I think it would be very useful to have such a feature for a server component.

divyam234 commented 2 months ago

@aifirstd3v you dont have to use this in server component use this in nextjs midleware which gives you request object and run this for client routes you want to secure.

aifirstd3v commented 2 months ago

@aifirstd3v Of course, it should work for the NextJS middleware because it provides the Request/Response. I just wanted to know how to use such a thing in server components freely :).
I think it would be better to use Next-Auth itselt for NextJS apps like you mentioned ! Thanks anyway for helping me.

beorn commented 1 month ago

The original problem is probably related to https://github.com/honojs/middleware/issues/537 (which now includes a fix) - getAuthUser() fails because origin gets set without taking into consideration the X-Forwarded-* headers.

alvalau commented 4 weeks ago

Does anyone manage to get the auth session in server components?