Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,7 @@ Create [new credentials](https://console.cloud.google.com/apis/credentials):
2. In `Application Type`, Choose `Web application`
3. Choose a name for your web client
4. In Authorized JavaScript origins, add a URI and enter `http://localhost:3000`
5. In `Authorized redirect URIs` enter:
- `http://localhost:3000/api/auth/callback/google`
- `http://localhost:3000/api/google/linking/callback`
5. In `Authorized redirect URIs` enter `http://localhost:3000/api/auth/callback/google`
6. Click `Create`.
7. A popup will show up with the new credentials, including the Client ID and secret.
3. Update .env file:
Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/(app)/ErrorMessages.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { auth } from "@/utils/auth";
import { headers } from "next/headers";
import { AlertError } from "@/components/Alert";
import { Button } from "@/components/ui/button";
import { clearUserErrorMessagesAction } from "@/utils/actions/error-messages";
import { getUserErrorMessages } from "@/utils/error-messages";

export async function ErrorMessages() {
const session = await auth();
const session = await auth.api.getSession({ headers: await headers() });
if (!session?.user) return null;

const errorMessages = await getUserErrorMessages(session.user.id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import { Badge } from "@/components/ui/badge";
import { notFound } from "next/navigation";
import { formatDistanceToNow } from "date-fns";
import { auth } from "@/utils/auth";
import { headers } from "next/headers";

export default async function RuleHistoryPage(props: {
params: Promise<{ emailAccountId: string; ruleId: string }>;
}) {
const { emailAccountId, ruleId } = await props.params;
const session = await auth();
const session = await auth.api.getSession({ headers: await headers() });
if (!session?.user.id) notFound();

const rule = await prisma.rule.findFirst({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { useCallback } from "react";
import { type SubmitHandler, useFieldArray, useForm } from "react-hook-form";
import { useSession } from "@/utils/auth-client";
import { authClient } from "@/utils/auth-client";
import { zodResolver } from "@hookform/resolvers/zod";
import useSWR from "swr";
import { usePostHog } from "posthog-js/react";
Expand Down Expand Up @@ -31,7 +31,7 @@ import { useAction } from "next-safe-action/hooks";
import { toastError, toastSuccess } from "@/components/Toast";

export function MultiAccountSection() {
const { data: session } = useSession();
const { data: session } = authClient.useSession();
const { data, isLoading, error, mutate } = useSWR<MultiAccountEmailsResponse>(
"/api/user/settings/multi-account",
);
Expand Down
12 changes: 7 additions & 5 deletions apps/web/app/(app)/accounts/AddAccount.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useState } from "react";
import { signIn } from "@/utils/auth-client";
import { authClient } from "@/utils/auth-client";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { toastError } from "@/components/Toast";
Expand All @@ -19,9 +19,10 @@ import { SCOPES as OUTLOOK_SCOPES } from "@/utils/outlook/scopes";

export function AddAccount() {
const handleConnectGoogle = async () => {
await signIn.social({
await authClient.signIn.social({
provider: "google",
callbackURL: "/welcome",
callbackURL: "/accounts",
newUserCallbackURL: "/welcome",
scopes: [...GMAIL_SCOPES],
});
};
Expand All @@ -38,9 +39,10 @@ export function AddAccount() {
};

const handleConnectMicrosoft = async () => {
await signIn.social({
await authClient.signIn.social({
provider: "microsoft",
callbackURL: "/welcome",
callbackURL: "/accounts",
newUserCallbackURL: "/welcome",
scopes: [...OUTLOOK_SCOPES],
});
};
Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/(app)/admin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AdminUpgradeUserForm } from "@/app/(app)/admin/AdminUpgradeUserForm";
import { AdminUserControls } from "@/app/(app)/admin/AdminUserControls";
import { TopSection } from "@/components/TopSection";
import { auth } from "@/utils/auth";
import { headers } from "next/headers";
import { ErrorPage } from "@/components/ErrorPage";
import { isAdmin } from "@/utils/admin";
import {
Expand All @@ -13,7 +14,7 @@ import {
export const maxDuration = 800;

export default async function AdminPage() {
const session = await auth();
const session = await auth.api.getSession({ headers: await headers() });

if (!isAdmin({ email: session?.user.email })) {
return (
Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/(app)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { redirect } from "next/navigation";
import { SideNavWithTopNav } from "@/components/SideNavWithTopNav";

import { auth } from "@/utils/auth";
import { headers } from "next/headers";
import { PostHogIdentify } from "@/providers/PostHogProvider";
import { CommandK } from "@/components/CommandK";
import { AppProviders } from "@/providers/AppProviders";
Expand Down Expand Up @@ -35,7 +36,7 @@ export default async function AppLayout({
}: {
children: React.ReactNode;
}) {
const session = await auth();
const session = await auth.api.getSession({ headers: await headers() });

if (!session?.user.email) redirect("/login");

Expand Down
6 changes: 3 additions & 3 deletions apps/web/app/(landing)/login/LoginForm.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useState } from "react";
import { signIn } from "@/utils/auth-client";
import { authClient } from "@/utils/auth-client";
import Image from "next/image";
import { useSearchParams } from "next/navigation";
import { Button } from "@/components/Button";
Expand All @@ -24,7 +24,7 @@ export function LoginForm() {

const handleGoogleSignIn = async () => {
setLoadingGoogle(true);
await signIn.social({
await authClient.signIn.social({
provider: "google",
errorCallbackURL: "/login",
callbackURL: next && next.length > 0 ? next : "/welcome",
Expand All @@ -35,7 +35,7 @@ export function LoginForm() {

const handleMicrosoftSignIn = async () => {
setLoadingMicrosoft(true);
await signIn.social({
await authClient.signIn.social({
provider: "microsoft",
errorCallbackURL: "/login",
callbackURL: next && next.length > 0 ? next : "/welcome",
Expand Down
11 changes: 8 additions & 3 deletions apps/web/app/(landing)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { redirect } from "next/navigation";
import Link from "next/link";
import { LoginForm } from "@/app/(landing)/login/LoginForm";
import { auth } from "@/utils/auth";
import { headers } from "next/headers";
import AutoLogOut from "@/app/(landing)/login/error/AutoLogOut";
import { AlertBasic } from "@/components/Alert";
import { env } from "@/env";
import { Button } from "@/components/ui/button";
Expand All @@ -18,7 +20,7 @@ export default async function AuthenticationPage(props: {
searchParams?: Promise<Record<string, string>>;
}) {
const searchParams = await props.searchParams;
const session = await auth();
const session = await auth.api.getSession({ headers: await headers() });
if (session?.user && !searchParams?.error) {
if (searchParams?.next) {
redirect(searchParams?.next);
Expand All @@ -43,7 +45,7 @@ export default async function AuthenticationPage(props: {
</div>

{searchParams?.error && (
<ErrorAlert error={searchParams?.error} />
<ErrorAlert error={searchParams?.error} loggedIn={!!session?.user} />
)}

<p className="px-8 pt-10 text-center text-sm text-muted-foreground">
Expand Down Expand Up @@ -80,7 +82,7 @@ export default async function AuthenticationPage(props: {
);
}

function ErrorAlert({ error }: { error: string; }) {
function ErrorAlert({ error, loggedIn }: { error: string; loggedIn: boolean }) {
if (error === "RequiresReconsent") return null;

if (error === "OAuthAccountNotLinked") {
Expand All @@ -101,10 +103,13 @@ function ErrorAlert({ error }: { error: string; }) {
}

return (
<>
<AlertBasic
variant="destructive"
title="Error logging in"
description={`There was an error logging in. Please try log in again. If this error persists please contact support at ${env.NEXT_PUBLIC_SUPPORT_EMAIL}`}
/>
{/* <AutoLogOut loggedIn={loggedIn} /> */}
</>
);
}
3 changes: 2 additions & 1 deletion apps/web/app/(landing)/welcome/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Suspense } from "react";
import type { Metadata } from "next";
import { redirect } from "next/navigation";
import { auth } from "@/utils/auth";
import { headers } from "next/headers";
import { OnboardingForm } from "@/app/(landing)/welcome/form";
import { SquaresPattern } from "@/app/(landing)/home/SquaresPattern";
import { env } from "@/env";
Expand All @@ -21,7 +22,7 @@ export default async function WelcomePage(props: {
searchParams: Promise<{ question?: string; force?: boolean }>;
}) {
const searchParams = await props.searchParams;
const session = await auth();
const session = await auth.api.getSession({ headers: await headers() });

if (!session?.user) redirect("/login");
if (!env.NEXT_PUBLIC_POSTHOG_ONBOARDING_SURVEY_ID)
Expand Down
4 changes: 2 additions & 2 deletions apps/web/app/api/auth/[...all]/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { betterAuthConfig } from "@/utils/auth";
import { auth } from "@/utils/auth";
import { toNextJsHandler } from "better-auth/next-js";

export const { POST, GET } = toNextJsHandler(betterAuthConfig);
export const { POST, GET } = toNextJsHandler(auth);
21 changes: 11 additions & 10 deletions apps/web/app/api/outlook/linking/callback/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,18 @@ export const GET = withError(async (request: NextRequest) => {
}

const profile = await profileResponse.json();
const providerAccountId = profile.id;
const providerEmail = profile.mail || profile.userPrincipalName;

if (!providerEmail) {
throw new Error("Profile missing required email");
if (!providerAccountId || !providerEmail) {
throw new Error("Profile missing required id or email");
}

const existingAccount = await prisma.account.findFirst({
const existingAccount = await prisma.account.findUnique({
where: {
provider: "microsoft",
user: {
email: providerEmail,
provider_providerAccountId: {
provider: "microsoft",
providerAccountId,
},
},
select: {
Expand All @@ -115,15 +116,15 @@ export const GET = withError(async (request: NextRequest) => {

if (!existingAccount) {
logger.warn(
`Merge Failed: Microsoft account ${providerEmail} not found in the system. Cannot merge.`,
`Merge Failed: Microsoft account ${providerEmail} (${providerAccountId}) not found in the system. Cannot merge.`,
);
redirectUrl.searchParams.set("error", "account_not_found_for_merge");
return NextResponse.redirect(redirectUrl, { headers: response.headers });
}

if (existingAccount.userId === targetUserId) {
logger.warn(
`Microsoft account ${providerEmail} is already linked to the correct user ${targetUserId}. Merge action unnecessary.`,
`Microsoft account ${providerEmail} (${providerAccountId}) is already linked to the correct user ${targetUserId}. Merge action unnecessary.`,
);
redirectUrl.searchParams.set("error", "already_linked_to_self");
return NextResponse.redirect(redirectUrl, {
Expand All @@ -132,7 +133,7 @@ export const GET = withError(async (request: NextRequest) => {
}

logger.info(
`Merging Microsoft account ${providerEmail} linked to user ${existingAccount.userId}, merging into ${targetUserId}.`,
`Merging Microsoft account ${providerEmail} (${providerAccountId}) linked to user ${existingAccount.userId}, merging into ${targetUserId}.`,
);
await prisma.$transaction([
prisma.account.update({
Expand All @@ -153,7 +154,7 @@ export const GET = withError(async (request: NextRequest) => {
]);

logger.info(
`Account ${providerEmail} re-assigned to user ${targetUserId}. Original user was ${existingAccount.userId}`,
`Account ${providerAccountId} re-assigned to user ${targetUserId}. Original user was ${existingAccount.userId}`,
);
redirectUrl.searchParams.set("success", "account_merged");
return NextResponse.redirect(redirectUrl, {
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/api/user/complete-registration/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { ReadonlyHeaders } from "next/dist/server/web/spec-extension/adapte
const logger = createScopedLogger("complete-registration");

export const POST = withError(async () => {
const session = await auth();
const session = await auth.api.getSession({ headers: await headers() });
if (!session?.user.email)
return NextResponse.json({ error: "Not authenticated" });

Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/api/user/me/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import prisma from "@/utils/prisma";
import { withError } from "@/utils/middleware";
import { SafeError } from "@/utils/error";
import { auth } from "@/utils/auth";
import { headers } from "next/headers";

export type UserResponse = Awaited<ReturnType<typeof getUser>> | null;

Expand Down Expand Up @@ -40,7 +41,7 @@ async function getUser({ userId }: { userId: string }) {

// Intentionally not using withAuth because we want to return null if the user is not authenticated
export const GET = withError(async () => {
const session = await auth();
const session = await auth.api.getSession({ headers: await headers() });
const userId = session?.user.id;
if (!userId) return NextResponse.json(null);

Expand Down
4 changes: 2 additions & 2 deletions apps/web/components/TopNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import Link from "next/link";
import { useRouter } from "next/navigation";
import { useSession } from "@/utils/auth-client";
import { authClient } from "@/utils/auth-client";
import {
Menu,
MenuButton,
Expand Down Expand Up @@ -43,7 +43,7 @@ export function TopNav({ trigger }: { trigger: React.ReactNode }) {
}

function ProfileDropdown() {
const { data: session, isPending } = useSession();
const { data: session, isPending } = authClient.useSession();
const { emailAccountId, emailAccount, provider } = useAccount();
const router = useRouter();

Expand Down
2 changes: 2 additions & 0 deletions apps/web/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ export const env = createEnv({
LICENSE_25_SEAT_VARIANT_ID: z.coerce.number().optional(),

DUB_API_KEY: z.string().optional(),

COOKIE_DOMAIN: z.string().default("getinboxzero.com"),
},
client: {
// stripe
Expand Down
4 changes: 2 additions & 2 deletions apps/web/providers/PostHogProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useEffect } from "react";
import posthog from "posthog-js";
import { PostHogProvider as PHProvider } from "posthog-js/react";
import { useSession } from "@/utils/auth-client";
import { authClient } from "@/utils/auth-client";
import { usePathname, useSearchParams } from "next/navigation";
import { env } from "@/env";

Expand All @@ -29,7 +29,7 @@ export function PostHogPageview() {
}

export function PostHogIdentify() {
const { data: session } = useSession();
const { data: session } = authClient.useSession();

useEffect(() => {
if (session?.user.email)
Expand Down
4 changes: 2 additions & 2 deletions apps/web/providers/SessionProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"use client";

import { useSession } from "@/utils/auth-client";
import { authClient } from "@/utils/auth-client";

export { useSession };
export const useSession = authClient.useSession;

export function SessionProvider({ children }: { children: React.ReactNode }) {
return <>{children}</>;
Expand Down
3 changes: 2 additions & 1 deletion apps/web/utils/account.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { auth } from "@/utils/auth";
import { headers } from "next/headers";
import {
getGmailClientWithRefresh,
getAccessTokenFromClient,
Expand Down Expand Up @@ -135,7 +136,7 @@ async function getTokens({ emailAccountId }: { emailAccountId: string }) {
}

export async function redirectToEmailAccountPath(path: `/${string}`) {
const session = await auth();
const session = await auth.api.getSession({ headers: await headers() });

const userId = session?.user.id;
if (!userId) throw new Error("Not authenticated");
Expand Down
Loading