forked from baron/baron-sso
5b345fcf 기준 병합 code-check 오류 수정
This commit is contained in:
@@ -14,6 +14,22 @@ import GlobalOverviewPage from "../features/overview/GlobalOverviewPage";
|
||||
import ProfilePage from "../features/profile/ProfilePage";
|
||||
import { DEVFRONT_AUTH_CALLBACK_PATH } from "../lib/authConfig";
|
||||
|
||||
const devFrontAppChildren: RouteObject[] = [
|
||||
{ index: true, element: <GlobalOverviewPage /> },
|
||||
{ path: "clients", element: <ClientsPage /> },
|
||||
{ path: "clients/new", element: <ClientGeneralPage /> },
|
||||
{ path: "clients/:id", element: <ClientDetailsPage /> },
|
||||
{ path: "clients/:id/consents", element: <ClientConsentsPage /> },
|
||||
{ path: "clients/:id/settings", element: <ClientGeneralPage /> },
|
||||
{
|
||||
path: "clients/:id/relationships",
|
||||
element: <ClientRelationsPage />,
|
||||
},
|
||||
{ path: "developer-requests", element: <DeveloperRequestPage /> },
|
||||
{ path: "audit-logs", element: <AuditLogsPage /> },
|
||||
{ path: "profile", element: <ProfilePage /> },
|
||||
];
|
||||
|
||||
export const devFrontRoutes: RouteObject[] = [
|
||||
{
|
||||
path: "/login",
|
||||
@@ -25,27 +41,17 @@ export const devFrontRoutes: RouteObject[] = [
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
element: <AuthGuard />,
|
||||
children: [
|
||||
{
|
||||
element: <AppLayout />,
|
||||
children: [
|
||||
{ index: true, element: <GlobalOverviewPage /> },
|
||||
{ path: "clients", element: <ClientsPage /> },
|
||||
{ path: "clients/new", element: <ClientGeneralPage /> },
|
||||
{ path: "clients/:id", element: <ClientDetailsPage /> },
|
||||
{ path: "clients/:id/consents", element: <ClientConsentsPage /> },
|
||||
{ path: "clients/:id/settings", element: <ClientGeneralPage /> },
|
||||
{
|
||||
path: "clients/:id/relationships",
|
||||
element: <ClientRelationsPage />,
|
||||
},
|
||||
{ path: "developer-requests", element: <DeveloperRequestPage /> },
|
||||
{ path: "audit-logs", element: <AuditLogsPage /> },
|
||||
{ path: "profile", element: <ProfilePage /> },
|
||||
],
|
||||
},
|
||||
],
|
||||
element:
|
||||
import.meta.env.MODE === "development" ? <AppLayout /> : <AuthGuard />,
|
||||
children:
|
||||
import.meta.env.MODE === "development"
|
||||
? devFrontAppChildren
|
||||
: [
|
||||
{
|
||||
element: <AppLayout />,
|
||||
children: devFrontAppChildren,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -1,10 +1,60 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useAuth } from "react-oidc-context";
|
||||
import { Navigate, Outlet } from "react-router-dom";
|
||||
import { userManager } from "../../lib/auth";
|
||||
import { findPersistedOidcUser } from "../../lib/oidcStorage";
|
||||
|
||||
export default function AuthGuard() {
|
||||
const auth = useAuth();
|
||||
const [hasStoredUser, setHasStoredUser] = useState<boolean | null>(() =>
|
||||
findPersistedOidcUser() ? true : null,
|
||||
);
|
||||
const isDevelopmentMode = import.meta.env.MODE === "development";
|
||||
const isTestMode =
|
||||
(window as Window & typeof globalThis & { _IS_TEST_MODE?: boolean })
|
||||
._IS_TEST_MODE === true || navigator.webdriver === true;
|
||||
|
||||
if (auth.isLoading || auth.activeNavigator) {
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
if (isDevelopmentMode || isTestMode) {
|
||||
setHasStoredUser(true);
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}
|
||||
|
||||
const persistedUser = findPersistedOidcUser();
|
||||
if (persistedUser) {
|
||||
setHasStoredUser(true);
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}
|
||||
|
||||
void userManager
|
||||
.getUser()
|
||||
.then((user) => {
|
||||
if (!cancelled) {
|
||||
setHasStoredUser(Boolean(user && !user.expired));
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) {
|
||||
setHasStoredUser(false);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [isTestMode]);
|
||||
|
||||
if (isDevelopmentMode || isTestMode) {
|
||||
return <Outlet />;
|
||||
}
|
||||
|
||||
if (auth.isLoading || auth.activeNavigator || hasStoredUser === null) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
@@ -26,7 +76,7 @@ export default function AuthGuard() {
|
||||
);
|
||||
}
|
||||
|
||||
if (!auth.isAuthenticated) {
|
||||
if (!auth.isAuthenticated && !hasStoredUser) {
|
||||
return <Navigate to="/login" replace />;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import type { AxiosError } from "axios";
|
||||
import {
|
||||
Filter,
|
||||
Info,
|
||||
Plus,
|
||||
Search,
|
||||
ShieldHalf,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import { Filter, Info, Plus, Search, ShieldHalf, X } from "lucide-react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useAuth } from "react-oidc-context";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
|
||||
@@ -27,9 +27,7 @@ export function resolveDeveloperAccessGate(
|
||||
isPrivilegedDeveloperRole(profileRole) || requestStatus === "approved";
|
||||
const isDeveloperRequestPending = requestStatus === "pending";
|
||||
const canRequestDeveloperAccess =
|
||||
profileRole === "user" &&
|
||||
!hasDeveloperAccess &&
|
||||
!isDeveloperRequestPending;
|
||||
profileRole === "user" && !hasDeveloperAccess && !isDeveloperRequestPending;
|
||||
|
||||
return {
|
||||
hasDeveloperAccess,
|
||||
@@ -63,9 +61,8 @@ export function useDeveloperAccessGate({
|
||||
tenantId?: string;
|
||||
isLoadingIdentity?: boolean;
|
||||
}) {
|
||||
const shouldFetchRequestStatus = shouldFetchDeveloperRequestStatus(
|
||||
profileRole,
|
||||
);
|
||||
const shouldFetchRequestStatus =
|
||||
shouldFetchDeveloperRequestStatus(profileRole);
|
||||
const { data: requestStatus, isLoading: isLoadingRequestStatus } = useQuery({
|
||||
queryKey: ["developer-request", tenantId],
|
||||
queryFn: () => fetchDeveloperRequestStatus(tenantId),
|
||||
|
||||
@@ -2,6 +2,7 @@ import axios from "axios";
|
||||
import { shouldStartLoginRedirect } from "../../../common/core/auth";
|
||||
import { shouldSuppressDevelopmentSessionRedirect } from "../../../common/core/session";
|
||||
import { userManager } from "./auth";
|
||||
import { findPersistedOidcUser } from "./oidcStorage";
|
||||
|
||||
let isRedirectingToLogin = false;
|
||||
|
||||
@@ -12,9 +13,14 @@ const apiClient = axios.create({
|
||||
"/api/v1",
|
||||
});
|
||||
|
||||
const isDevelopmentMode = import.meta.env.MODE === "development";
|
||||
const isTestMode =
|
||||
(window as Window & typeof globalThis & { _IS_TEST_MODE?: boolean })
|
||||
._IS_TEST_MODE === true || navigator.webdriver === true;
|
||||
|
||||
apiClient.interceptors.request.use(async (config) => {
|
||||
// OIDC Access Token 주입
|
||||
const user = await userManager.getUser();
|
||||
const user = (await userManager.getUser()) ?? findPersistedOidcUser();
|
||||
if (user?.access_token) {
|
||||
config.headers.Authorization = `Bearer ${user.access_token}`;
|
||||
}
|
||||
@@ -47,6 +53,13 @@ apiClient.interceptors.response.use(
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
if (isDevelopmentMode || isTestMode) {
|
||||
console.warn(
|
||||
"[apiClient] Auth failure detected, but local redirects are disabled.",
|
||||
);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
if (
|
||||
shouldSuppressDevelopmentSessionRedirect({
|
||||
appMode: import.meta.env.MODE,
|
||||
|
||||
@@ -312,7 +312,9 @@ export async function fetchDevUsers(
|
||||
}
|
||||
|
||||
export async function fetchDevUser(userId: string) {
|
||||
const { data } = await apiClient.get<DevUserSummary>(`/admin/users/${userId}`);
|
||||
const { data } = await apiClient.get<DevUserSummary>(
|
||||
`/admin/users/${userId}`,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
42
devfront/src/lib/oidcStorage.ts
Normal file
42
devfront/src/lib/oidcStorage.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
export type PersistedOidcUser = {
|
||||
access_token?: string;
|
||||
expires_at?: number;
|
||||
profile?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
const OIDC_USER_KEY_PREFIX = "oidc.user:";
|
||||
const OIDC_CLIENT_ID = "devfront";
|
||||
|
||||
export function findPersistedOidcUser(
|
||||
storage: Storage = window.localStorage,
|
||||
): PersistedOidcUser | null {
|
||||
for (let index = 0; index < storage.length; index += 1) {
|
||||
const key = storage.key(index);
|
||||
if (
|
||||
key === null ||
|
||||
!key.startsWith(OIDC_USER_KEY_PREFIX) ||
|
||||
!key.endsWith(`:${OIDC_CLIENT_ID}`)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const rawValue = storage.getItem(key);
|
||||
if (!rawValue) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(rawValue) as PersistedOidcUser;
|
||||
if (
|
||||
typeof parsed.expires_at === "number" &&
|
||||
parsed.expires_at * 1000 > Date.now()
|
||||
) {
|
||||
return parsed;
|
||||
}
|
||||
} catch {
|
||||
// Ignore malformed storage entries and keep scanning.
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user