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 },
},
});
});
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:
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>;
}
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>
);
}
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"],
};
require
in next-auth's useSessionthis 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