forked from baron/baron-sso
callback 검증 보강. seed-tenant 추가보강
This commit is contained in:
13
orgfront/src/app/routes.test.tsx
Normal file
13
orgfront/src/app/routes.test.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { matchRoutes } from "react-router-dom";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { ORGFRONT_AUTH_CALLBACK_PATH } from "../lib/authConfig";
|
||||
import { orgFrontRoutes } from "./routes";
|
||||
|
||||
describe("orgfront routes", () => {
|
||||
it("accepts the auth callback path used by the OIDC redirect URI", () => {
|
||||
const matches = matchRoutes(orgFrontRoutes, ORGFRONT_AUTH_CALLBACK_PATH);
|
||||
|
||||
expect(matches).not.toBeNull();
|
||||
expect(matches?.at(-1)?.route.path).toBe(ORGFRONT_AUTH_CALLBACK_PATH);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,8 @@
|
||||
import { Navigate, createBrowserRouter } from "react-router-dom";
|
||||
import {
|
||||
Navigate,
|
||||
type RouteObject,
|
||||
createBrowserRouter,
|
||||
} from "react-router-dom";
|
||||
import AuthCallbackPage from "../features/auth/AuthCallbackPage";
|
||||
import AuthGuard from "../features/auth/AuthGuard";
|
||||
import LoginPage from "../features/auth/LoginPage";
|
||||
@@ -9,38 +13,38 @@ import {
|
||||
OrgPickerEmbedPage,
|
||||
OrgPickerPage,
|
||||
} from "../features/orgchart/routes/OrgPickerPage";
|
||||
import { ORGFRONT_AUTH_CALLBACK_PATH } from "../lib/authConfig";
|
||||
|
||||
export const router = createBrowserRouter(
|
||||
[
|
||||
{
|
||||
path: "/login",
|
||||
element: <LoginPage />,
|
||||
},
|
||||
{
|
||||
path: "/auth/callback",
|
||||
element: <AuthCallbackPage />,
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
element: <AuthGuard />,
|
||||
children: [
|
||||
{ index: true, element: <Navigate to="/chart" replace /> },
|
||||
{
|
||||
element: <OrgFrontLayout />,
|
||||
children: [
|
||||
{ path: "chart", element: <TenantOrgChartPage /> },
|
||||
{ path: "chart/:tenantId", element: <TenantOrgChartPage /> },
|
||||
{ path: "picker", element: <OrgPickerPage /> },
|
||||
{ path: "embed-preview", element: <OrgPickerEmbedPreviewPage /> },
|
||||
],
|
||||
},
|
||||
{ path: "embed/picker", element: <OrgPickerEmbedPage /> },
|
||||
],
|
||||
},
|
||||
],
|
||||
export const orgFrontRoutes: RouteObject[] = [
|
||||
{
|
||||
future: {
|
||||
v7_startTransition: true,
|
||||
},
|
||||
} as unknown as Parameters<typeof createBrowserRouter>[1],
|
||||
);
|
||||
path: "/login",
|
||||
element: <LoginPage />,
|
||||
},
|
||||
{
|
||||
path: ORGFRONT_AUTH_CALLBACK_PATH,
|
||||
element: <AuthCallbackPage />,
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
element: <AuthGuard />,
|
||||
children: [
|
||||
{ index: true, element: <Navigate to="/chart" replace /> },
|
||||
{
|
||||
element: <OrgFrontLayout />,
|
||||
children: [
|
||||
{ path: "chart", element: <TenantOrgChartPage /> },
|
||||
{ path: "chart/:tenantId", element: <TenantOrgChartPage /> },
|
||||
{ path: "picker", element: <OrgPickerPage /> },
|
||||
{ path: "embed-preview", element: <OrgPickerEmbedPreviewPage /> },
|
||||
],
|
||||
},
|
||||
{ path: "embed/picker", element: <OrgPickerEmbedPage /> },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const router = createBrowserRouter(orgFrontRoutes, {
|
||||
future: {
|
||||
v7_startTransition: true,
|
||||
},
|
||||
} as unknown as Parameters<typeof createBrowserRouter>[1]);
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
import { UserManager, WebStorageStateStore } from "oidc-client-ts";
|
||||
import type { AuthProviderProps } from "react-oidc-context";
|
||||
import {
|
||||
buildOrgFrontAuthRedirectUris,
|
||||
resolveOrgFrontPublicOrigin,
|
||||
} from "./authConfig";
|
||||
|
||||
const orgFrontPublicOrigin = resolveOrgFrontPublicOrigin(
|
||||
import.meta.env.VITE_ORGFRONT_PUBLIC_URL,
|
||||
window.location.origin,
|
||||
);
|
||||
const orgFrontRedirectUris =
|
||||
buildOrgFrontAuthRedirectUris(orgFrontPublicOrigin);
|
||||
|
||||
export const oidcConfig: AuthProviderProps = {
|
||||
authority:
|
||||
import.meta.env.VITE_OIDC_AUTHORITY || "http://localhost:5000/oidc", // Gateway Proxy URL
|
||||
client_id: import.meta.env.VITE_OIDC_CLIENT_ID || "orgfront",
|
||||
redirect_uri: `${window.location.origin}/auth/callback`,
|
||||
redirect_uri: orgFrontRedirectUris.redirectUri,
|
||||
response_type: "code",
|
||||
scope: "openid offline_access profile email", // offline_access for refresh token
|
||||
post_logout_redirect_uri: window.location.origin,
|
||||
popup_redirect_uri: `${window.location.origin}/auth/callback`,
|
||||
post_logout_redirect_uri: orgFrontRedirectUris.postLogoutRedirectUri,
|
||||
popup_redirect_uri: orgFrontRedirectUris.popupRedirectUri,
|
||||
userStore: new WebStorageStateStore({ store: window.localStorage }),
|
||||
automaticSilentRenew: false,
|
||||
};
|
||||
|
||||
29
orgfront/src/lib/authConfig.test.ts
Normal file
29
orgfront/src/lib/authConfig.test.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
ORGFRONT_AUTH_CALLBACK_PATH,
|
||||
buildOrgFrontAuthRedirectUris,
|
||||
resolveOrgFrontPublicOrigin,
|
||||
} from "./authConfig";
|
||||
|
||||
describe("orgfront auth config", () => {
|
||||
it("builds callback URLs from the public origin", () => {
|
||||
expect(buildOrgFrontAuthRedirectUris("https://sorg.hmac.kr")).toEqual({
|
||||
redirectUri: "https://sorg.hmac.kr/auth/callback",
|
||||
postLogoutRedirectUri: "https://sorg.hmac.kr",
|
||||
popupRedirectUri: "https://sorg.hmac.kr/auth/callback",
|
||||
});
|
||||
});
|
||||
|
||||
it("uses the browser origin when the configured origin is empty or invalid", () => {
|
||||
expect(resolveOrgFrontPublicOrigin("", "http://localhost:5174")).toBe(
|
||||
"http://localhost:5174",
|
||||
);
|
||||
expect(
|
||||
resolveOrgFrontPublicOrigin("not a url", "http://localhost:5174"),
|
||||
).toBe("http://localhost:5174");
|
||||
});
|
||||
|
||||
it("keeps the callback path aligned with the registered redirect path", () => {
|
||||
expect(ORGFRONT_AUTH_CALLBACK_PATH).toBe("/auth/callback");
|
||||
});
|
||||
});
|
||||
33
orgfront/src/lib/authConfig.ts
Normal file
33
orgfront/src/lib/authConfig.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
export interface OrgFrontAuthRedirectUris {
|
||||
redirectUri: string;
|
||||
postLogoutRedirectUri: string;
|
||||
popupRedirectUri: string;
|
||||
}
|
||||
|
||||
export const ORGFRONT_AUTH_CALLBACK_PATH = "/auth/callback";
|
||||
|
||||
export function resolveOrgFrontPublicOrigin(
|
||||
configuredOrigin: string | undefined,
|
||||
browserOrigin: string,
|
||||
) {
|
||||
const trimmed = configuredOrigin?.trim();
|
||||
if (!trimmed) {
|
||||
return browserOrigin;
|
||||
}
|
||||
|
||||
try {
|
||||
return new URL(trimmed).origin;
|
||||
} catch {
|
||||
return browserOrigin;
|
||||
}
|
||||
}
|
||||
|
||||
export function buildOrgFrontAuthRedirectUris(
|
||||
publicOrigin: string,
|
||||
): OrgFrontAuthRedirectUris {
|
||||
return {
|
||||
redirectUri: `${publicOrigin}${ORGFRONT_AUTH_CALLBACK_PATH}`,
|
||||
postLogoutRedirectUri: publicOrigin,
|
||||
popupRedirectUri: `${publicOrigin}${ORGFRONT_AUTH_CALLBACK_PATH}`,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user