deadcoder0904
deadcoder0904

Reputation: 8693

`withAuth` pattern with `iron-session` in Next.js?

I like the pattern used on next-authhttps://next-auth.js.org/getting-started/client#custom-client-session-handling

Basically, the gist is to put .auth = true & then check it in _app.tsx like:

export default function App({
  Component,
  pageProps: { session, ...pageProps },
}) {
  return (
    <SessionProvider session={session}>
      {Component.auth ? (
        <Auth>
          <Component {...pageProps} />
        </Auth>
      ) : (
        <Component {...pageProps} />
      )}
    </SessionProvider>
  )
}

function Auth({ children }) {
  const { data: session, status } = useSession()
  const isUser = !!session?.user
  React.useEffect(() => {
    if (status === "loading") return // Do nothing while loading
    if (!isUser) signIn() // If not authenticated, force log in
  }, [isUser, status])

  if (isUser) {
    return children
  }

  // Session is being fetched, or no user.
  // If no user, useEffect() will redirect.
  return <div>Loading...</div>
}

Is there a way to do that in iron-session?

Upvotes: 1

Views: 1519

Answers (2)

deadcoder0904
deadcoder0904

Reputation: 8693

I've found a decent solution.

import { GetServerSidePropsContext, GetServerSidePropsResult, NextApiHandler } from 'next'
import { withIronSessionApiRoute, withIronSessionSsr } from 'iron-session/next'
import { IronSessionOptions } from 'iron-session'

export const IRON_OPTIONS: IronSessionOptions = {
    cookieName: process.env.IRON_COOKIE_NAME,
    password: process.env.IRON_PASSWORD,
    // 1 minute ttl = 1 + 1, 60 minute ttl = 60 + 1
    // ttl: 60 * (1 + 1),
}

function withSessionRoute(handler: NextApiHandler) {
    return withIronSessionApiRoute(handler, IRON_OPTIONS)
}

// Theses types are compatible with InferGetStaticPropsType https://nextjs.org/docs/basic-features/data-fetching#typescript-use-getstaticprops
function withSessionSsr<P extends { [key: string]: unknown } = { [key: string]: unknown }>(
    handler: (
        context: GetServerSidePropsContext
    ) => GetServerSidePropsResult<P> | Promise<GetServerSidePropsResult<P>>
) {
    return withIronSessionSsr(handler, IRON_OPTIONS)
}

export { withSessionRoute, withSessionSsr }

withSessionRoute is used for /api routes & withSessionSsr for components.

import { withSessionSsr } from '@/utils/index'

const ProtectedRoute = () => {
   return <div>Protected Router</div>
}

export const getServerSideProps = withSessionSsr(async function ({ req, res }) {
    const admin = req.session.admin

    if (admin === undefined) {
        res.setHeader('location', '/admin')
        res.statusCode = 302
        res.end()
        return {
            props: {
                admin: { isLoggedIn: false } as Admin,
            },
        }
    }

    return {
        props: { admin },
    }
})

I redirect the protected page to /admin if the admin session is not found.

Upvotes: 1

AbstractProblemFactory
AbstractProblemFactory

Reputation: 9811

I was looking for the same solution with iron-session, here is what I ended up with (_app.tsx):

function MyApp({
  Component,
  pageProps
}: AppProps
) {
  store.dispatch(update(pageProps.user));

  return (
    <div>
      <Provider store={store}>
        <Component {...pageProps} />
      </Provider>
    </div>
  );
}

MyApp.getInitialProps = async function (appContext: AppContext) {
  const req = appContext.ctx.req
  const res = appContext.ctx.res;
  const session = await getIronSession(req, res, sessionOptions);
  const DEFAULT_PROPS = {
    pageProps: {},
  };

  if (session.user) {
    return {
      ...DEFAULT_PROPS,
      pageProps: {
        user: session.user,
      },
    };
  }

  return DEFAULT_PROPS;
}

Notice store.dispatch which fills up the store with user data.

Upvotes: 1

Related Questions