-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Show error if user didn't give correct Gmail permissions #242
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| "use client"; | ||
|
|
||
| import { useEffect } from "react"; | ||
| import { useRouter } from "next/navigation"; | ||
| import { checkPermissionsAction } from "@/utils/actions/permissions"; | ||
|
|
||
| let permissionsChecked = false; | ||
|
|
||
| export function PermissionsCheck() { | ||
| const router = useRouter(); | ||
|
|
||
| useEffect(() => { | ||
| if (permissionsChecked) return; | ||
| permissionsChecked = true; | ||
|
|
||
| checkPermissionsAction().then((result) => { | ||
| if (!result?.hasAllPermissions) router.replace("/permissions/error"); | ||
| }); | ||
| }, [router]); | ||
|
|
||
| return null; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,13 @@ | ||
| import { PermissionsCheck } from "@/app/(app)/PermissionsCheck"; | ||
| import { BulkUnsubscribe } from "./BulkUnsubscribe"; | ||
| import { checkAndRedirectForUpgrade } from "@/utils/premium/check-and-redirect-for-upgrade"; | ||
|
|
||
| export default async function BulkUnsubscribePage() { | ||
| await checkAndRedirectForUpgrade(); | ||
| return <BulkUnsubscribe />; | ||
| return ( | ||
| <> | ||
| <PermissionsCheck /> | ||
| <BulkUnsubscribe /> | ||
| </> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| "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 ( | ||
| <div className="flex flex-col items-center justify-center sm:p-20 md:p-32"> | ||
| <PageHeading className="text-center"> | ||
| You are missing permissions 😔 | ||
| </PageHeading> | ||
|
|
||
| <TypographyP className="mx-auto mt-4 max-w-prose text-center"> | ||
| You must sign in and give access to all permissions for Inbox Zero to | ||
| work. | ||
| </TypographyP> | ||
|
|
||
| <Button className="mt-4" onClick={() => logOut("/login")}> | ||
| Sign in again | ||
| </Button> | ||
|
|
||
| <div className="mt-8"> | ||
| <Image | ||
| src="/images/falling.svg" | ||
| alt="" | ||
| width={400} | ||
| height={400} | ||
| unoptimized | ||
| /> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,13 @@ | ||
| import { PermissionsCheck } from "@/app/(app)/PermissionsCheck"; | ||
| import { Stats } from "./Stats"; | ||
| import { checkAndRedirectForUpgrade } from "@/utils/premium/check-and-redirect-for-upgrade"; | ||
|
|
||
| export default async function StatsPage() { | ||
| await checkAndRedirectForUpgrade(); | ||
| return <Stats />; | ||
| return ( | ||
| <> | ||
| <PermissionsCheck /> | ||
| <Stats /> | ||
| </> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| "use server"; | ||
|
|
||
| import { auth } from "@/app/api/auth/[...nextauth]/auth"; | ||
| import { withActionInstrumentation } from "@/utils/actions/middleware"; | ||
| import { getGmailAccessToken } from "@/utils/gmail/client"; | ||
| import { checkGmailPermissions } from "@/utils/gmail/permissions"; | ||
|
|
||
| export const checkPermissionsAction = withActionInstrumentation( | ||
| "checkPermissions", | ||
| async () => { | ||
| const session = await auth(); | ||
| if (!session?.user.id) return { error: "Not logged in" }; | ||
|
|
||
| try { | ||
| const token = await getGmailAccessToken(session); | ||
| if (!token.token) return { error: "No Gmail access token" }; | ||
|
|
||
| const { hasAllPermissions, error } = await checkGmailPermissions( | ||
| token.token, | ||
| ); | ||
| if (error) return { error }; | ||
| return { hasAllPermissions }; | ||
| } catch (error) { | ||
| return { error: "Failed to check permissions" }; | ||
| } | ||
| }, | ||
| ); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| import { SCOPES } from "@/utils/auth"; | ||
|
|
||
| export async function checkGmailPermissions(accessToken: string): Promise<{ | ||
| hasAllPermissions: boolean; | ||
| missingScopes: string[]; | ||
| error?: string; | ||
| }> { | ||
| if (!accessToken) { | ||
| console.error("No access token available"); | ||
| return { | ||
| hasAllPermissions: false, | ||
| missingScopes: SCOPES, | ||
| error: "No access token available", | ||
| }; | ||
| } | ||
|
|
||
| try { | ||
| const response = await fetch( | ||
| `https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=${accessToken}`, | ||
| ); | ||
|
|
||
| const data = await response.json(); | ||
|
|
||
| if (data.error) { | ||
| console.error("Error checking Gmail permissions:", data.error); | ||
| return { | ||
| hasAllPermissions: false, | ||
| missingScopes: SCOPES, // Assume all scopes are missing if we can't check | ||
| error: data.error, | ||
| }; | ||
| } | ||
|
|
||
| const grantedScopes = data.scope?.split(" ") || []; | ||
| const missingScopes = SCOPES.filter( | ||
| (scope) => !grantedScopes.includes(scope), | ||
| ); | ||
|
|
||
| return { | ||
| hasAllPermissions: missingScopes.length === 0, | ||
| missingScopes, | ||
| }; | ||
| } catch (error) { | ||
| console.error("Error checking Gmail permissions:", error); | ||
| return { | ||
| hasAllPermissions: false, | ||
| missingScopes: SCOPES, // Assume all scopes are missing if we can't check | ||
| error: "Failed to check permissions", | ||
| }; | ||
| } | ||
| } | ||
|
Comment on lines
+1
to
+50
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Security Concern Confirmed: Sanitization of Error Logs Required The review comment regarding the security implications of error logging has been validated. The Affected Line:
Recommendation: function sanitizeError(error: unknown): string {
if (error instanceof Error) {
return error.message;
}
return String(error);
}
// Usage:
console.error("Error checking Gmail permissions:", sanitizeError(error));🔗 Analysis chainGreat implementation overall. Consider security implications of error logging. The However, there's a potential security concern with logging error objects. These might contain sensitive information. Consider sanitizing the error logs: If the script finds instances of logging error objects, consider implementing a sanitize function: function sanitizeError(error: unknown): string {
if (error instanceof Error) {
return error.message;
}
return String(error);
}
// Usage:
console.error("Error checking Gmail permissions:", sanitizeError(error));Action Required: Verify Sensitive Error Logging The previous script encountered an error due to an unrecognized file type. Please use the corrected script below to check for potential sensitive data logging: 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Check for potential sensitive data logging
rg --type typescript 'console\.(log|error|warn|info).*error' apps/web/
Length of output: 107 Script: #!/bin/bash
# Check for potential sensitive data logging
rg --type ts 'console\.(log|error|warn|info).*error' apps/web/
Length of output: 3263 |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve image accessibility and consider performance optimization.
While using the Next.js Image component is good for performance, there are a few areas that could be improved:
alt=" "(with a space) instead of an empty string.unoptimizedprop bypasses Next.js's image optimization. Unless there's a specific reason for this, consider removing it to leverage Next.js's built-in image optimization.Apply the following changes to improve accessibility and potentially enhance performance:
Also, consider using responsive image sizing if appropriate for your design:
This allows the image to adapt to different screen sizes while maintaining the aspect ratio.