Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻️ Upgrade to next-auth v5 #1547

Merged
merged 18 commits into from
Feb 8, 2025
Merged
2 changes: 1 addition & 1 deletion apps/web/.env.test
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ NEXTAUTH_URL=$NEXT_PUBLIC_BASE_URL
SECRET_PASSWORD=abcdef1234567890abcdef1234567890
DATABASE_URL=postgres://postgres:postgres@localhost:5450/rallly
SUPPORT_EMAIL=support@rallly.co
SMTP_HOST=localhost
SMTP_HOST=0.0.0.0
SMTP_PORT=1025
QUICK_CREATE_ENABLED=true
1 change: 1 addition & 0 deletions apps/web/declarations/next-auth.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ declare module "next-auth" {
}

interface User extends DefaultUser {
id: string;
locale?: string | null;
timeZone?: string | null;
timeFormat?: TimeFormat | null;
Expand Down
4 changes: 2 additions & 2 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"docker:start": "./scripts/docker-start.sh"
},
"dependencies": {
"@auth/prisma-adapter": "^1.0.3",
"@auth/prisma-adapter": "^2.7.4",
"@aws-sdk/client-s3": "^3.645.0",
"@aws-sdk/s3-request-presigner": "^3.645.0",
"@hookform/resolvers": "^3.3.1",
Expand Down Expand Up @@ -67,7 +67,7 @@
"lucide-react": "^0.387.0",
"micro": "^10.0.1",
"nanoid": "^5.0.9",
"next-auth": "^4.24.5",
"next-auth": "^5.0.0-beta.25",
"next-i18next": "^13.0.3",
"php-serialize": "^4.1.1",
"postcss": "^8.4.31",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function DeleteAccountDialog({
onSuccess() {
posthog?.capture("delete account");
signOut({
callbackUrl: "/login",
redirectTo: "/login",
});
},
});
Expand Down
22 changes: 14 additions & 8 deletions apps/web/src/app/[locale]/(auth)/login/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,24 @@ import { prisma } from "@rallly/database";
import { cookies } from "next/headers";

export async function setVerificationEmail(email: string) {
const count = await prisma.user.count({
const user = await prisma.user.findUnique({
where: {
email,
},
select: {
email: true,
},
});

cookies().set("verification-email", email, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
maxAge: 15 * 60,
});
if (user) {
cookies().set("verification-email", user.email, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
maxAge: 15 * 60,
});
return true;
}

return count > 0;
return false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ export function LoginWithEmailForm() {
if (doesExist) {
await signIn("email", {
email: identifier,
callbackUrl: searchParams?.get("callbackUrl") ?? undefined,
redirectTo: searchParams?.get("redirectTo") ?? undefined,
redirect: false,
});
// redirect to verify page with callbackUrl
// redirect to verify page with redirectTo
router.push(
`/login/verify?callbackUrl=${encodeURIComponent(
searchParams?.get("callbackUrl") ?? "",
`/login/verify?redirectTo=${encodeURIComponent(
searchParams?.get("redirectTo") ?? "",
)}`,
);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import { Trans } from "@/components/trans";

export async function LoginWithOIDC({
name,
callbackUrl,
redirectTo,
}: {
name: string;
callbackUrl?: string;
redirectTo?: string;
}) {
return (
<Button
onClick={() => {
signIn("oidc", {
callbackUrl,
redirectTo: redirectTo,
});
}}
variant="link"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function SSOImage({ provider }: { provider: string }) {
);
}

if (provider === "azure-ad") {
if (provider === "microsoft-entra-id") {
return (
<Image
src="/static/microsoft.svg"
Expand All @@ -40,11 +40,11 @@ function SSOImage({ provider }: { provider: string }) {
export function SSOProvider({
providerId,
name,
callbackUrl,
redirectTo,
}: {
providerId: string;
name: string;
callbackUrl?: string;
redirectTo?: string;
}) {
const { t } = useTranslation();
return (
Expand All @@ -58,7 +58,7 @@ export function SSOProvider({
key={providerId}
onClick={() => {
signIn(providerId, {
callbackUrl,
redirectTo: redirectTo,
});
}}
>
Expand Down
38 changes: 18 additions & 20 deletions apps/web/src/app/[locale]/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import Link from "next/link";
import { Trans } from "react-i18next/TransWithoutContext";

import { getOAuthProviders } from "@/auth";
import { GoogleProvider } from "@/auth/providers/google";
import { MicrosoftProvider } from "@/auth/providers/microsoft";
import { OIDCProvider } from "@/auth/providers/oidc";
import { getTranslation } from "@/i18n/server";

import {
Expand All @@ -22,20 +24,14 @@ export default async function LoginPage({
searchParams,
}: {
searchParams?: {
callbackUrl?: string;
redirectTo?: string;
};
}) {
const { t } = await getTranslation();
const oAuthProviders = getOAuthProviders();

const hasAlternateLoginMethods = oAuthProviders.length > 0;

const oidcProvider = oAuthProviders.find(
(provider) => provider.id === "oidc",
);
const socialProviders = oAuthProviders.filter(
(provider) => provider.id !== "oidc",
);
const oidcProvider = OIDCProvider();
const socialProviders = [GoogleProvider(), MicrosoftProvider()];
const hasAlternateLoginMethods = socialProviders.length > 0 || !!oidcProvider;

return (
<AuthPageContainer>
Expand All @@ -58,19 +54,21 @@ export default async function LoginPage({
{oidcProvider ? (
<LoginWithOIDC
name={oidcProvider.name}
callbackUrl={searchParams?.callbackUrl}
redirectTo={searchParams?.redirectTo}
/>
) : null}
{socialProviders ? (
<div className="grid gap-4">
{socialProviders.map((provider) => (
<SSOProvider
key={provider.id}
providerId={provider.id}
name={provider.name}
callbackUrl={searchParams?.callbackUrl}
/>
))}
{socialProviders.map((provider) =>
provider ? (
<SSOProvider
key={provider.id}
providerId={provider.id}
name={provider.options?.name || provider.name}
redirectTo={searchParams?.redirectTo}
/>
) : null,
)}
</div>
) : null}
</AuthPageContent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function OTPForm({ email }: { email: string }) {
message: t("wrongVerificationCode"),
});
} else {
window.location.href = searchParams?.get("callbackUrl") ?? "/";
window.location.href = searchParams?.get("redirectTo") ?? "/";
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export function OTPForm({ token }: { token: string }) {

signIn("registration-token", {
token,
callbackUrl: searchParams?.get("callbackUrl") ?? "/",
redirectTo: searchParams?.get("redirectTo") ?? "/",
});
});

Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import React from "react";

import { TimeZoneChangeDetector } from "@/app/[locale]/timezone-change-detector";
import { Providers } from "@/app/providers";
import { getServerSession } from "@/auth";
import { SessionProvider } from "@/auth/session-provider";
import { auth } from "@/next-auth";

const inter = Inter({
subsets: ["latin"],
Expand All @@ -30,7 +30,7 @@ export default async function Root({
children: React.ReactNode;
params: { locale: string };
}) {
const session = await getServerSession();
const session = await auth();

return (
<html lang={locale} className={inter.className}>
Expand Down
6 changes: 6 additions & 0 deletions apps/web/src/app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { withPosthog } from "@rallly/posthog/server";

import { handlers } from "@/next-auth";

export const GET = withPosthog(handlers.GET);
export const POST = withPosthog(handlers.POST);
4 changes: 2 additions & 2 deletions apps/web/src/app/api/notifications/unsubscribe/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { cookies } from "next/headers";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";

import { getServerSession } from "@/auth";
import { auth } from "@/next-auth";
import type { DisableNotificationsPayload } from "@/trpc/types";
import { decryptToken } from "@/utils/session";

Expand All @@ -14,7 +14,7 @@ export const GET = async (req: NextRequest) => {
return NextResponse.redirect(new URL("/login", req.url));
}

const session = await getServerSession();
const session = await auth();

if (!session || !session.user?.email) {
return NextResponse.redirect(new URL("/login", req.url));
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/app/api/stripe/checkout/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { z } from "zod";

import { getServerSession } from "@/auth";
import { auth } from "@/next-auth";

const inputSchema = z.object({
period: z.enum(["monthly", "yearly"]).optional(),
Expand All @@ -14,7 +14,7 @@ const inputSchema = z.object({
});

export async function POST(request: NextRequest) {
const userSession = await getServerSession();
const userSession = await auth();
const formData = await request.formData();
const { period = "monthly", return_path } = inputSchema.parse(
Object.fromEntries(formData.entries()),
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/app/api/stripe/portal/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as Sentry from "@sentry/nextjs";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";

import { getServerSession } from "@/auth";
import { auth } from "@/next-auth";

export async function GET(request: NextRequest) {
const sessionId = request.nextUrl.searchParams.get("session_id");
Expand All @@ -32,7 +32,7 @@ export async function GET(request: NextRequest) {
);
}
} else {
const userSession = await getServerSession();
const userSession = await auth();
if (!userSession?.user || userSession.user.email === null) {
Sentry.captureException(new Error("User not logged in"));
return NextResponse.json(
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/app/api/trpc/[trpc]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ipAddress } from "@vercel/functions";
import type { NextRequest } from "next/server";

import { getLocaleFromHeader } from "@/app/guest";
import { getServerSession } from "@/auth";
import { auth } from "@/next-auth";
import type { TRPCContext } from "@/trpc/context";
import { appRouter } from "@/trpc/routers";
import { getEmailClient } from "@/utils/emails";
Expand All @@ -15,7 +15,7 @@ const handler = (req: NextRequest) => {
req,
router: appRouter,
createContext: async () => {
const session = await getServerSession();
const session = await auth();
const locale = await getLocaleFromHeader(req);
const user = session?.user
? {
Expand Down
6 changes: 3 additions & 3 deletions apps/web/src/app/api/user/verify-email-change/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { cookies } from "next/headers";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";

import { getServerSession } from "@/auth";
import { auth } from "@/next-auth";
import { decryptToken } from "@/utils/session";

type EmailChangePayload = {
Expand Down Expand Up @@ -50,11 +50,11 @@ export const GET = async (request: NextRequest) => {
return NextResponse.json({ error: "No token provided" }, { status: 400 });
}

const session = await getServerSession();
const session = await auth();

if (!session?.user || !session.user.email) {
return NextResponse.redirect(
new URL(`/login?callbackUrl=${request.url}`, request.url),
new URL(`/login?redirectTo=${request.url}`, request.url),
);
}

Expand Down
Loading
Loading