Skip to content
Open
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
9 changes: 9 additions & 0 deletions src/app/auth/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import CallbackPage from "@/auth/CallbackPage";

/**
* OIDC authentication callback route.
* This page handles the redirect from the identity provider after user authentication.
*/
export default function AuthCallbackPage() {
return <CallbackPage />;
}
9 changes: 9 additions & 0 deletions src/app/silent-auth/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import CallbackPage from "@/auth/CallbackPage";

/**
* OIDC silent authentication callback route.
* This page handles silent token renewal redirects from the identity provider.
*/
export default function SilentAuthCallbackPage() {
return <CallbackPage />;
}
29 changes: 29 additions & 0 deletions src/auth/CallbackPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use client";

import FullScreenLoading from "@components/ui/FullScreenLoading";
import { useRouter } from "next/navigation";
import { useEffect } from "react";

/**
* Callback page component for OIDC authentication redirects.
* This page provides a valid route for static export at /auth and /silent-auth,
* preventing 404 errors with standards-compliant OAuth 2.0 redirect URIs.
*
* The @axa-fr/react-oidc library intercepts these routes when OAuth callback
* parameters are present and renders its own callback handler. This component
* serves as a fallback if somehow rendered directly.
*/
export default function CallbackPage() {
const router = useRouter();

useEffect(() => {
// Fallback: if this component renders directly, redirect to /peers
const timer = setTimeout(() => {
router.replace("/peers");
}, 100);
Comment on lines +16 to +23
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic number 100ms delay lacks explanation. Add a comment or constant explaining the purpose of this delay.

Suggested change
export default function CallbackPage() {
const router = useRouter();
useEffect(() => {
// Fallback: if this component renders directly, redirect to /peers
const timer = setTimeout(() => {
router.replace("/peers");
}, 100);
// Delay before redirecting to allow any pending UI updates or transitions to complete.
const REDIRECT_DELAY_MS = 100;
export default function CallbackPage() {
const router = useRouter();
useEffect(() => {
// Fallback: if this component renders directly, redirect to /peers
// The 100ms delay ensures the UI has time to render before navigation,
// which can help avoid race conditions or abrupt transitions.
const timer = setTimeout(() => {
router.replace("/peers");
}, REDIRECT_DELAY_MS);

Copilot uses AI. Check for mistakes.

return () => clearTimeout(timer);
}, [router]);

return <FullScreenLoading />;
}
22 changes: 21 additions & 1 deletion src/auth/OIDCProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,26 @@ const CallBackSuccess = () => {
const params = useSearchParams();
const errorParam = params.get("error");
const currentPath = usePathname();
useRedirect(currentPath, true, !errorParam);
const router = useRouter();

useEffect(() => {
if (!errorParam && currentPath === "/auth") {
// Redirect to /peers after a brief delay to ensure tokens are stored
const timer = setTimeout(() => {
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic number 100ms delay lacks explanation. Add a comment or constant explaining why this delay is necessary for token storage.

Copilot uses AI. Check for mistakes.
let queryParams = "";
try {
const stored = localStorage.getItem("netbird-query-params");
if (stored) {
queryParams = `?${stored}`;
localStorage.removeItem("netbird-query-params");
}
} catch (e) { }
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty catch block silently swallows errors. Consider logging the error or adding a comment explaining why it's safe to ignore.

Suggested change
} catch (e) { }
} catch (e) {
if (process.env.NODE_ENV !== "production") {
console.error("Error accessing localStorage in CallBackSuccess:", e);
}
}

Copilot uses AI. Check for mistakes.

router.replace(`/peers${queryParams}`);
}, 100);
return () => clearTimeout(timer);
}
}, [errorParam, currentPath, router]);

return <FullScreenLoading />;
};
4 changes: 2 additions & 2 deletions src/utils/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ interface Config {
*/
const loadConfig = (): Config => {
let configJson: any;
let redirectURI = "/#callback";
let silentRedirectURI = "/#silent-callback";
let redirectURI = "/auth";
let silentRedirectURI = "/silent-auth";
let tokenSource = "accessToken";

if (process.env.APP_ENV === "test") {
Expand Down