3

How can I trigger a redirect on the server side if a signed in user has not completed their profile page

const enforceUserIsAuthed = t.middleware(({ ctx, next }) => {
  if (!ctx.session || !ctx.session.user) {
    throw new TRPCError({ code: "UNAUTHORIZED" });
  }

  // redirect to profile page if user has not completed profile


  return next({
    ctx: {
      // infers the `session` as non-nullable
      session: { ...ctx.session, user: ctx.session.user },
    },
  });
});
Mani Muridi
  • 400
  • 4
  • 18

1 Answers1

1

This is not currently possible in the way you are describing to the best of my knowledge.

Here are some alternatives that might be helpful:

In getServerSideProps

this only works if you want to redirect before the initial page load. You could also create a wrapper around gSSP to make this more DRY if you're going to use it on a lot of pages.

import { type GetServerSidePropsContext } from "next";
import { getServerAuthSession } from "../server/auth";

export async function getServerSideProps(ctx: GetServerSidePropsContext) {
  const session = await getServerAuthSession(ctx);

  if (!session) {
    return {
      redirect: {
        destination: "/",
        permanent: false,
      },
    };
  }

  return {
    props: {},
  };
}

export default function AuthedPage() {
  return <div>Authed</div>;
}

As part of a query or mutation clientside

this is useful for a query or mutation that is only fired after the page has loaded. Again this is a very simple example and could be DRYed, probably the easiest way would be to extract into a custom hook.

import { useRouter } from "next/router";
import { api } from "../utils/api";

export default function AuthedPage() {
  const router = useRouter();

  // `authedHello` is the example Create T3 App "hello" procedure
  // but as a protectedProcedure, ie throws "UNAUTHORIZED" if no session.
  // Replace this with a middleware that throws on whatever condition you need it to.
  const authedHello = api.example.protectedHello.useQuery(
    { text: "world" },
    {
      retry: (_count, err) => {
        // `onError` only runs once React Query stops retrying
        if (err.data?.code === "UNAUTHORIZED") {
          return false;
        }
        return true;
      },
      onError: (err) => {
        if (err.data?.code === "UNAUTHORIZED") {
          void router.push("/");
        }
      },
    }
  );

  return (
    <div>
      <h1>Authed Page</h1>
      <p>{authedHello.data?.greeting}</p>
    </div>
  );
}

Using Next.js middleware

This is easy to apply to a bunch of routes using the matcher, but it falls a bit outside of T3 conventions.

// pages/middleware.ts
import { NextResponse } from "next/server";
import { getServerSession } from "next-auth";
import { authOptions } from "../server/auth";
import type { NextApiRequest, NextApiResponse } from "next";

export async function middleware(req: NextApiRequest, res: NextApiResponse) {
  const session = await getServerSession(req, res, authOptions);
  if (!session?.user) {
    return NextResponse.redirect(new URL("/", req.url));
  }
}

export const config = {
  matcher: ["/protectedPage", "/anotherProtectedPage"],
};

Using require in next-auth's useSession

this is useful if you want to guard a page but can't use getServerSideProps. It doesn't quite solve your specific problem, but might be useful to other people who find this. See: https://next-auth.js.org/getting-started/client#require-session