From ba164d5c6e1422851fdcee1121e564f311034329 Mon Sep 17 00:00:00 2001 From: Meg Stepp Date: Tue, 8 Apr 2025 11:38:08 -0400 Subject: [PATCH 1/5] add onError callback to show expired session token message on sign-in page --- .../app/auth/sign-in/[[...sign-in]]/page.tsx | 18 ++++++++++++--- .../app/auth/sign-in/org-selector.tsx | 23 ++++++++++++++++--- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/apps/dashboard/app/auth/sign-in/[[...sign-in]]/page.tsx b/apps/dashboard/app/auth/sign-in/[[...sign-in]]/page.tsx index d2e56ea6fe..9f65957a2a 100644 --- a/apps/dashboard/app/auth/sign-in/[[...sign-in]]/page.tsx +++ b/apps/dashboard/app/auth/sign-in/[[...sign-in]]/page.tsx @@ -16,8 +16,16 @@ import { OAuthSignIn } from "../oauth-signin"; import { OrgSelector } from "../org-selector"; function SignInContent() { - const { isVerifying, accountNotFound, error, email, hasPendingAuth, orgs, handleSignInViaEmail } = - useSignIn(); + const { + isVerifying, + accountNotFound, + error, + email, + hasPendingAuth, + orgs, + handleSignInViaEmail, + setError, + } = useSignIn(); const searchParams = useSearchParams(); const verifyParam = searchParams?.get("verify"); const invitationToken = searchParams?.get("invitation_token"); @@ -26,6 +34,10 @@ function SignInContent() { // Initialize isLoading as false const [isLoading, setIsLoading] = useState(false); + const handleOrgSelectionError = (errorMessage: string) => { + setError(errorMessage); + }; + // Add clientReady state to handle hydration const [clientReady, setClientReady] = useState(false); const hasAttemptedSignIn = useRef(false); @@ -86,7 +98,7 @@ function SignInContent() { return (
- {hasPendingAuth && } + {hasPendingAuth && } {accountNotFound && ( diff --git a/apps/dashboard/app/auth/sign-in/org-selector.tsx b/apps/dashboard/app/auth/sign-in/org-selector.tsx index 522da61e6f..7afca7794f 100644 --- a/apps/dashboard/app/auth/sign-in/org-selector.tsx +++ b/apps/dashboard/app/auth/sign-in/org-selector.tsx @@ -22,9 +22,10 @@ import { completeOrgSelection } from "../actions"; interface OrgSelectorProps { organizations: Organization[]; + onError: (errorMessage: string) => void; } -export const OrgSelector: React.FC = ({ organizations }) => { +export const OrgSelector: React.FC = ({ organizations, onError }) => { const [selected, setSelected] = useState(); const [isOpen, setIsOpen] = useState(false); const [clientReady, setClientReady] = useState(false); @@ -40,8 +41,24 @@ export const OrgSelector: React.FC = ({ organizations }) => { if (!selected) { return; } - await completeOrgSelection(selected); - setIsOpen(false); + try { + const result = await completeOrgSelection(selected); + + if (!result.success) { + onError(result.message); + } + + return; + } catch (error) { + const errorMessage = + error instanceof Error && error.message === "no pending session" + ? "Organization selection timed out. Please re-authenticate." + : "Failed to complete organization selection. Please re-authenticate or contact support."; + + onError(errorMessage); + } finally { + setIsOpen(false); + } }; return ( From 65540fa475bddbe5d07492e2360005e1bffc44dc Mon Sep 17 00:00:00 2001 From: Meg Stepp Date: Tue, 8 Apr 2025 12:06:35 -0400 Subject: [PATCH 2/5] why use two functions when one function good? --- apps/dashboard/app/auth/sign-in/[[...sign-in]]/page.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/dashboard/app/auth/sign-in/[[...sign-in]]/page.tsx b/apps/dashboard/app/auth/sign-in/[[...sign-in]]/page.tsx index 9f65957a2a..37dcf4c44a 100644 --- a/apps/dashboard/app/auth/sign-in/[[...sign-in]]/page.tsx +++ b/apps/dashboard/app/auth/sign-in/[[...sign-in]]/page.tsx @@ -34,10 +34,6 @@ function SignInContent() { // Initialize isLoading as false const [isLoading, setIsLoading] = useState(false); - const handleOrgSelectionError = (errorMessage: string) => { - setError(errorMessage); - }; - // Add clientReady state to handle hydration const [clientReady, setClientReady] = useState(false); const hasAttemptedSignIn = useRef(false); @@ -98,7 +94,7 @@ function SignInContent() { return (
- {hasPendingAuth && } + {hasPendingAuth && } {accountNotFound && ( From a679b0cbe31be6126238788be530761d63b3f40f Mon Sep 17 00:00:00 2001 From: Meg Stepp Date: Tue, 8 Apr 2025 13:43:38 -0400 Subject: [PATCH 3/5] add better error messaging --- apps/dashboard/app/auth/actions.ts | 2 +- apps/dashboard/app/auth/sign-in/org-selector.tsx | 8 ++++---- apps/dashboard/lib/auth/types.ts | 2 ++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/dashboard/app/auth/actions.ts b/apps/dashboard/app/auth/actions.ts index 2c203f22a0..fbf4b894e2 100644 --- a/apps/dashboard/app/auth/actions.ts +++ b/apps/dashboard/app/auth/actions.ts @@ -161,7 +161,7 @@ export async function completeOrgSelection( ): Promise { const tempSession = cookies().get(PENDING_SESSION_COOKIE); if (!tempSession) { - throw new Error("No pending session"); + throw new Error(errorMessages[AuthErrorCode.PENDING_SESSION_EXPIRED]); } // Call auth provider with token and orgId diff --git a/apps/dashboard/app/auth/sign-in/org-selector.tsx b/apps/dashboard/app/auth/sign-in/org-selector.tsx index 7afca7794f..f1a2053ec1 100644 --- a/apps/dashboard/app/auth/sign-in/org-selector.tsx +++ b/apps/dashboard/app/auth/sign-in/org-selector.tsx @@ -14,7 +14,7 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import type { Organization } from "@/lib/auth/types"; +import { errorMessages, type Organization } from "@/lib/auth/types"; import { Button } from "@unkey/ui"; import type React from "react"; import { useEffect, useState } from "react"; @@ -51,9 +51,9 @@ export const OrgSelector: React.FC = ({ organizations, onError return; } catch (error) { const errorMessage = - error instanceof Error && error.message === "no pending session" - ? "Organization selection timed out. Please re-authenticate." - : "Failed to complete organization selection. Please re-authenticate or contact support."; + error instanceof Error + ? error.message + : "Failed to complete organization selection. Please re-authenticate or contact support@unkey.dev"; onError(errorMessage); } finally { diff --git a/apps/dashboard/lib/auth/types.ts b/apps/dashboard/lib/auth/types.ts index d809cbdfcc..91a37ce2ea 100644 --- a/apps/dashboard/lib/auth/types.ts +++ b/apps/dashboard/lib/auth/types.ts @@ -172,6 +172,7 @@ export enum AuthErrorCode { ACCOUNT_NOT_FOUND = "ACCOUNT_NOT_FOUND", ORGANIZATION_SELECTION_REQUIRED = "ORGANIZATION_SELECTION_REQUIRED", EMAIL_VERIFICATION_REQUIRED = "EMAIL_VERIFICATION_REQUIRED", + PENDING_SESSION_EXPIRED="PENDING_SESSION_EXPIRED", } export const errorMessages: Record = { @@ -188,6 +189,7 @@ export const errorMessages: Record = { "Please choose a workspace to continue authentication.", [AuthErrorCode.EMAIL_VERIFICATION_REQUIRED]: "Email address not verified. Please check your email for a verification code.", + [AuthErrorCode.PENDING_SESSION_EXPIRED]: "Pending Authentication has expired. Please sign-in again." }; export interface MiddlewareConfig { From d5b93feb4dd64a36499f40ccc17313d5a84780ef Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 19:44:54 +0000 Subject: [PATCH 4/5] [autofix.ci] apply automated fixes --- apps/dashboard/app/auth/sign-in/org-selector.tsx | 4 ++-- apps/dashboard/lib/auth/types.ts | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/dashboard/app/auth/sign-in/org-selector.tsx b/apps/dashboard/app/auth/sign-in/org-selector.tsx index f1a2053ec1..6906963bc5 100644 --- a/apps/dashboard/app/auth/sign-in/org-selector.tsx +++ b/apps/dashboard/app/auth/sign-in/org-selector.tsx @@ -14,7 +14,7 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { errorMessages, type Organization } from "@/lib/auth/types"; +import type { Organization } from "@/lib/auth/types"; import { Button } from "@unkey/ui"; import type React from "react"; import { useEffect, useState } from "react"; @@ -51,7 +51,7 @@ export const OrgSelector: React.FC = ({ organizations, onError return; } catch (error) { const errorMessage = - error instanceof Error + error instanceof Error ? error.message : "Failed to complete organization selection. Please re-authenticate or contact support@unkey.dev"; diff --git a/apps/dashboard/lib/auth/types.ts b/apps/dashboard/lib/auth/types.ts index 91a37ce2ea..e1e4d08dde 100644 --- a/apps/dashboard/lib/auth/types.ts +++ b/apps/dashboard/lib/auth/types.ts @@ -172,7 +172,7 @@ export enum AuthErrorCode { ACCOUNT_NOT_FOUND = "ACCOUNT_NOT_FOUND", ORGANIZATION_SELECTION_REQUIRED = "ORGANIZATION_SELECTION_REQUIRED", EMAIL_VERIFICATION_REQUIRED = "EMAIL_VERIFICATION_REQUIRED", - PENDING_SESSION_EXPIRED="PENDING_SESSION_EXPIRED", + PENDING_SESSION_EXPIRED = "PENDING_SESSION_EXPIRED", } export const errorMessages: Record = { @@ -189,7 +189,8 @@ export const errorMessages: Record = { "Please choose a workspace to continue authentication.", [AuthErrorCode.EMAIL_VERIFICATION_REQUIRED]: "Email address not verified. Please check your email for a verification code.", - [AuthErrorCode.PENDING_SESSION_EXPIRED]: "Pending Authentication has expired. Please sign-in again." + [AuthErrorCode.PENDING_SESSION_EXPIRED]: + "Pending Authentication has expired. Please sign-in again.", }; export interface MiddlewareConfig { From 16efaa7033f5b9f9855f08a14d6888b1fa8c87e2 Mon Sep 17 00:00:00 2001 From: Meg Stepp Date: Tue, 8 Apr 2025 16:27:12 -0400 Subject: [PATCH 5/5] don't throw an error, return an error response instead --- apps/dashboard/app/auth/actions.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/dashboard/app/auth/actions.ts b/apps/dashboard/app/auth/actions.ts index fbf4b894e2..b067862a53 100644 --- a/apps/dashboard/app/auth/actions.ts +++ b/apps/dashboard/app/auth/actions.ts @@ -161,7 +161,11 @@ export async function completeOrgSelection( ): Promise { const tempSession = cookies().get(PENDING_SESSION_COOKIE); if (!tempSession) { - throw new Error(errorMessages[AuthErrorCode.PENDING_SESSION_EXPIRED]); + return { + success: false, + code: AuthErrorCode.PENDING_SESSION_EXPIRED, + message: errorMessages[AuthErrorCode.PENDING_SESSION_EXPIRED] + }; } // Call auth provider with token and orgId