From 86d4b0f2ababfc64fb1ca877cfaab53e21696c0a Mon Sep 17 00:00:00 2001
From: Eliezer Steinbock <3090527+elie222@users.noreply.github.com>
Date: Thu, 2 Jan 2025 17:41:20 +0200
Subject: [PATCH 1/2] Reload refresh token if required
---
apps/web/app/(app)/PermissionsCheck.tsx | 1 +
.../app/(app)/permissions/consent/page.tsx | 38 +++++++++++++++++++
apps/web/app/(app)/permissions/error/page.tsx | 7 +++-
apps/web/app/(landing)/login/LoginForm.tsx | 19 ++++++----
apps/web/app/(landing)/login/page.tsx | 2 +-
apps/web/app/api/auth/[...nextauth]/auth.ts | 20 +++++++++-
apps/web/components/TokenCheck.tsx | 6 ++-
apps/web/utils/actions/permissions.ts | 17 ++++++++-
apps/web/utils/auth.ts | 24 ++++++++----
9 files changed, 113 insertions(+), 21 deletions(-)
create mode 100644 apps/web/app/(app)/permissions/consent/page.tsx
diff --git a/apps/web/app/(app)/PermissionsCheck.tsx b/apps/web/app/(app)/PermissionsCheck.tsx
index 3d7dce79b..d7e60b8fc 100644
--- a/apps/web/app/(app)/PermissionsCheck.tsx
+++ b/apps/web/app/(app)/PermissionsCheck.tsx
@@ -15,6 +15,7 @@ export function PermissionsCheck() {
checkPermissionsAction().then((result) => {
if (!result?.hasAllPermissions) router.replace("/permissions/error");
+ if (!result?.hasRefreshToken) router.replace("/permissions/consent");
});
}, [router]);
diff --git a/apps/web/app/(app)/permissions/consent/page.tsx b/apps/web/app/(app)/permissions/consent/page.tsx
new file mode 100644
index 000000000..02f1d9138
--- /dev/null
+++ b/apps/web/app/(app)/permissions/consent/page.tsx
@@ -0,0 +1,38 @@
+"use client";
+
+import Image from "next/image";
+import { Button } from "@/components/ui/button";
+import { logOut } from "@/utils/user";
+import { PageHeading, TypographyP } from "@/components/Typography";
+
+export default function PermissionsErrorPage() {
+ return (
+
+
+ We are missing consent 😔
+
+
+
+ You must sign in and give access to all permissions for Inbox Zero to
+ work.
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/app/(app)/permissions/error/page.tsx b/apps/web/app/(app)/permissions/error/page.tsx
index 69db18c7d..1533164a7 100644
--- a/apps/web/app/(app)/permissions/error/page.tsx
+++ b/apps/web/app/(app)/permissions/error/page.tsx
@@ -9,7 +9,7 @@ export default function PermissionsErrorPage() {
return (
- You are missing permissions 😔
+ We are missing permissions 😔
@@ -17,7 +17,10 @@ export default function PermissionsErrorPage() {
work.
-
- {searchParams?.error && (
+ {searchParams?.error && searchParams?.error !== "RequiresReconsent" && (
<>
{
+ if (req?.url) {
+ const url = new URL(req?.url);
+ const consent = url.searchParams.get("consent");
+ if (consent) {
+ logger.info("Consent requested");
+ return getAuthOptions({ consent: true });
+ }
+ }
+
+ return authOptions;
+});
diff --git a/apps/web/components/TokenCheck.tsx b/apps/web/components/TokenCheck.tsx
index f4d663407..9e1ff7721 100644
--- a/apps/web/components/TokenCheck.tsx
+++ b/apps/web/components/TokenCheck.tsx
@@ -10,8 +10,12 @@ export function TokenCheck() {
useEffect(() => {
if (session?.error === "RefreshAccessTokenError") {
- console.log("Token check error");
router.replace("/login?error=RefreshAccessTokenError");
+ return;
+ }
+ if (session?.error === "RequiresReconsent") {
+ router.replace("/login?error=RequiresReconsent");
+ return;
}
}, [session, router]);
diff --git a/apps/web/utils/actions/permissions.ts b/apps/web/utils/actions/permissions.ts
index fc83dfe69..c84ad714d 100644
--- a/apps/web/utils/actions/permissions.ts
+++ b/apps/web/utils/actions/permissions.ts
@@ -21,7 +21,22 @@ export const checkPermissionsAction = withActionInstrumentation(
token.token,
);
if (error) return { error };
- return { hasAllPermissions };
+
+ if (!hasAllPermissions) return { hasAllPermissions: false };
+
+ // Check for refresh token
+ const user = await prisma.account.findFirst({
+ where: {
+ userId: session.user.id,
+ provider: "google",
+ },
+ select: { refresh_token: true },
+ });
+
+ if (!user?.refresh_token)
+ return { hasRefreshToken: false, hasAllPermissions };
+
+ return { hasRefreshToken: true, hasAllPermissions };
} catch (error) {
return { error: "Failed to check permissions" };
}
diff --git a/apps/web/utils/auth.ts b/apps/web/utils/auth.ts
index dee6855e9..9e38b871c 100644
--- a/apps/web/utils/auth.ts
+++ b/apps/web/utils/auth.ts
@@ -23,9 +23,9 @@ export const SCOPES = [
: []),
];
-const getAuthOptions: (options?: { consent: boolean }) => NextAuthConfig = (
- options,
-) => ({
+export const getAuthOptions: (options?: {
+ consent: boolean;
+}) => NextAuthConfig = (options) => ({
// debug: true,
providers: [
GoogleProvider({
@@ -45,7 +45,7 @@ const getAuthOptions: (options?: { consent: boolean }) => NextAuthConfig = (
},
}),
],
- adapter: PrismaAdapter(prisma) as any, // TODO
+ adapter: PrismaAdapter(prisma),
session: { strategy: "jwt" },
// based on: https://authjs.dev/guides/basics/refresh-token-rotation
// and: https://github.com/nextauthjs/next-auth-refresh-token-example/blob/main/pages/api/auth/%5B...nextauth%5D.js
@@ -158,8 +158,6 @@ const getAuthOptions: (options?: { consent: boolean }) => NextAuthConfig = (
},
});
-export const authOptions = getAuthOptions();
-
/**
* Takes a token, and returns a new token with updated
* `access_token` and `expires_at`. If an error occurs,
@@ -262,11 +260,23 @@ export async function saveRefreshToken(
},
account: Pick,
) {
+ const refreshToken = tokens.refresh_token ?? account.refresh_token;
+
+ if (!refreshToken) {
+ logger.error("Attempted to save null refresh token", {
+ providerAccountId: account.providerAccountId,
+ });
+ captureException("Cannot save null refresh token", {
+ extra: { providerAccountId: account.providerAccountId },
+ });
+ return;
+ }
+
return await prisma.account.update({
data: {
access_token: tokens.access_token,
expires_at: tokens.expires_at,
- refresh_token: tokens.refresh_token ?? account.refresh_token,
+ refresh_token: refreshToken,
},
where: {
provider_providerAccountId: {
From d8408bc106932bd3ba4967d8990e57d5212ba7c5 Mon Sep 17 00:00:00 2001
From: Eliezer Steinbock <3090527+elie222@users.noreply.github.com>
Date: Thu, 2 Jan 2025 19:30:18 +0200
Subject: [PATCH 2/2] Fix coderabbit nitpick comments
---
apps/web/app/(app)/permissions/consent/page.tsx | 2 +-
apps/web/app/api/auth/[...nextauth]/auth.ts | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/apps/web/app/(app)/permissions/consent/page.tsx b/apps/web/app/(app)/permissions/consent/page.tsx
index 02f1d9138..097a76d90 100644
--- a/apps/web/app/(app)/permissions/consent/page.tsx
+++ b/apps/web/app/(app)/permissions/consent/page.tsx
@@ -5,7 +5,7 @@ import { Button } from "@/components/ui/button";
import { logOut } from "@/utils/user";
import { PageHeading, TypographyP } from "@/components/Typography";
-export default function PermissionsErrorPage() {
+export default function PermissionsConsentPage() {
return (
diff --git a/apps/web/app/api/auth/[...nextauth]/auth.ts b/apps/web/app/api/auth/[...nextauth]/auth.ts
index e80aaaf1f..fc9655592 100644
--- a/apps/web/app/api/auth/[...nextauth]/auth.ts
+++ b/apps/web/app/api/auth/[...nextauth]/auth.ts
@@ -4,7 +4,7 @@ import { createScopedLogger } from "@/utils/logger";
const logger = createScopedLogger("Auth API");
-const authOptions = getAuthOptions();
+const defaultAuthOptions = getAuthOptions();
export const {
handlers: { GET, POST },
@@ -19,5 +19,5 @@ export const {
}
}
- return authOptions;
+ return defaultAuthOptions;
});