refactor: centralize authentication handling

- Replaced direct localStorage token access with a centralized `getBearerToken` function across various components and hooks to improve code maintainability and security.
- Updated API calls to use `authenticatedFetch` for consistent authentication handling.
- Enhanced user experience by ensuring proper redirection to login when authentication fails.
- Cleaned up unused imports and improved overall code structure for better readability.
This commit is contained in:
DESKTOP-RTLN3BA\$punk 2025-12-02 01:24:09 -08:00
parent 6cc9e38e1d
commit b2a97b39ce
35 changed files with 396 additions and 497 deletions

View file

@ -2,22 +2,25 @@
import { useRouter, useSearchParams } from "next/navigation";
import { useEffect } from "react";
import { getAndClearRedirectPath, setBearerToken } from "@/lib/auth-utils";
interface TokenHandlerProps {
redirectPath?: string; // Path to redirect after storing token
redirectPath?: string; // Default path to redirect after storing token (if no saved path)
tokenParamName?: string; // Name of the URL parameter containing the token
storageKey?: string; // Key to use when storing in localStorage
storageKey?: string; // Key to use when storing in localStorage (kept for backwards compatibility)
}
/**
* Client component that extracts a token from URL parameters and stores it in localStorage
* After storing the token, it redirects the user back to the page they were on before
* being redirected to login (if available), or to the default redirectPath.
*
* @param redirectPath - Path to redirect after storing token (default: '/')
* @param redirectPath - Default path to redirect after storing token (default: '/dashboard')
* @param tokenParamName - Name of the URL parameter containing the token (default: 'token')
* @param storageKey - Key to use when storing in localStorage (default: 'auth_token')
* @param storageKey - Key to use when storing in localStorage (default: 'surfsense_bearer_token')
*/
const TokenHandler = ({
redirectPath = "/",
redirectPath = "/dashboard",
tokenParamName = "token",
storageKey = "surfsense_bearer_token",
}: TokenHandlerProps) => {
@ -33,14 +36,22 @@ const TokenHandler = ({
if (token) {
try {
// Store token in localStorage
// Store token in localStorage using both methods for compatibility
localStorage.setItem(storageKey, token);
// console.log(`Token stored in localStorage with key: ${storageKey}`);
setBearerToken(token);
// Redirect to specified path
router.push(redirectPath);
// Check if there's a saved redirect path from before the auth flow
const savedRedirectPath = getAndClearRedirectPath();
// Use the saved path if available, otherwise use the default redirectPath
const finalRedirectPath = savedRedirectPath || redirectPath;
// Redirect to the appropriate path
router.push(finalRedirectPath);
} catch (error) {
console.error("Error storing token in localStorage:", error);
// Even if there's an error, try to redirect to the default path
router.push(redirectPath);
}
}
}, [searchParams, tokenParamName, storageKey, redirectPath, router]);

View file

@ -14,6 +14,7 @@ import {
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";
import { useSearchSpace } from "@/hooks/use-search-space";
import { authenticatedFetch, getBearerToken } from "@/lib/auth-utils";
interface BreadcrumbItemInterface {
label: string;
@ -41,17 +42,12 @@ export function DashboardBreadcrumb() {
useEffect(() => {
if (segments[2] === "editor" && segments[3] && searchSpaceId) {
const documentId = segments[3];
const token =
typeof window !== "undefined" ? localStorage.getItem("surfsense_bearer_token") : null;
const token = getBearerToken();
if (token) {
fetch(
authenticatedFetch(
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/search-spaces/${searchSpaceId}/documents/${documentId}/editor-content`,
{
headers: {
Authorization: `Bearer ${token}`,
},
}
{ method: "GET" }
)
.then((res) => res.json())
.then((data) => {

View file

@ -13,6 +13,7 @@ import { Switch } from "@/components/ui/switch";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea";
import { type CommunityPrompt, useCommunityPrompts } from "@/hooks/use-community-prompts";
import { authenticatedFetch } from "@/lib/auth-utils";
interface SetupPromptStepProps {
searchSpaceId: number;
@ -74,14 +75,11 @@ export function SetupPromptStep({ searchSpaceId, onComplete }: SetupPromptStepPr
// Only send update if there's something to update
if (Object.keys(payload).length > 0) {
const response = await fetch(
const response = await authenticatedFetch(
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/${searchSpaceId}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem("surfsense_bearer_token")}`,
},
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
}
);

View file

@ -25,6 +25,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea";
import { type CommunityPrompt, useCommunityPrompts } from "@/hooks/use-community-prompts";
import { useSearchSpace } from "@/hooks/use-search-space";
import { authenticatedFetch } from "@/lib/auth-utils";
interface PromptConfigManagerProps {
searchSpaceId: number;
@ -78,14 +79,11 @@ export function PromptConfigManager({ searchSpaceId }: PromptConfigManagerProps)
// Only send request if we have something to update
if (Object.keys(payload).length > 0) {
const response = await fetch(
const response = await authenticatedFetch(
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/${searchSpaceId}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem("surfsense_bearer_token")}`,
},
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
}
);

View file

@ -14,6 +14,7 @@ import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Progress } from "@/components/ui/progress";
import { Separator } from "@/components/ui/separator";
import { getAuthHeaders } from "@/lib/auth-utils";
import { GridPattern } from "./GridPattern";
interface DocumentUploadTabProps {
@ -168,9 +169,7 @@ export function DocumentUploadTab({ searchSpaceId }: DocumentUploadTabProps) {
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/documents/fileupload`,
{
method: "POST",
headers: {
Authorization: `Bearer ${window.localStorage.getItem("surfsense_bearer_token")}`,
},
headers: getAuthHeaders(),
body: formData,
}
);

View file

@ -19,6 +19,7 @@ import {
CardTitle,
} from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { authenticatedFetch } from "@/lib/auth-utils";
const youtubeRegex =
/^(https:\/\/)?(www\.)?(youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})$/;
@ -66,14 +67,11 @@ export function YouTubeTab({ searchSpaceId }: YouTubeTabProps) {
const videoUrls = videoTags.map((tag) => tag.text);
const response = await fetch(
const response = await authenticatedFetch(
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/documents`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem("surfsense_bearer_token")}`,
},
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
document_type: "YOUTUBE_VIDEO",
content: videoUrls,