middleware
middleware copied to clipboard
Getting Auth Session in a server component, Set CallbackURL issue!
I'am developing a simple NextJS app with hono & auth.js.
My Question is
- 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?
- 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 👍
Hi @aifirstd3v
@divyam234 Do you have any idea?
@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
@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.
@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 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.
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.
Does anyone manage to get the auth session in server components?
@alvalau @aifirstd3v
This works for me while using Tanstack Query. Took me a while but so far it seems to work without problems. The original problem is server components dont include headers (why not(!)) when doing a fetch request. Client components -> included by default. Because Tanstack queries run on server (eg. prefetch) and also on the client, when being in a server component just load the next headers and append them to the request.
export const client = hc<AppType>(getBaseUrl(), {
async fetch(input, requestInit, Env, executionCtx) {
return fetch(input, {
...requestInit,
...(typeof window === "undefined"
? (await import("next/headers")).headers()
: {}),
});
},
});
@cptlchad
What do you mean append them to the request? In your tanstack queries for useSession? Can you provide some more code please im having trouble with this.
@alvalau @aifirstd3v
This works for me while using Tanstack Query. Took me a while but so far it seems to work without problems. The original problem is server components dont include headers (why not(!)) when doing a fetch request. Client components -> included by default. Because Tanstack queries run on server (eg. prefetch) and also on the client, when being in a server component just load the next headers and append them to the request.
export const client = hc<AppType>(getBaseUrl(), { async fetch(input, requestInit, Env, executionCtx) { return fetch(input, { ...requestInit, ...(typeof window === "undefined" ? (await import("next/headers")).headers() : {}), }); }, });
I believe your approach will force everything to be dynamically rendered
https://nextjs.org/docs/app/building-your-application/rendering/server-components#dynamic-apis