forked from baron/baron-sso
callback 검증 보강. seed-tenant 추가보강
This commit is contained in:
18
adminfront/src/app/routes.test.tsx
Normal file
18
adminfront/src/app/routes.test.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { matchRoutes } from "react-router-dom";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildAdminAuthRedirectUris } from "../lib/authConfig";
|
||||
import { adminRoutes } from "./routes";
|
||||
|
||||
describe("admin routes", () => {
|
||||
it("accepts the auth callback path generated from the public admin URL", () => {
|
||||
const { redirectUri } = buildAdminAuthRedirectUris(
|
||||
"https://sadmin.hmac.kr",
|
||||
);
|
||||
const callbackPath = new URL(redirectUri).pathname;
|
||||
|
||||
const matches = matchRoutes(adminRoutes, callbackPath);
|
||||
|
||||
expect(callbackPath).toBe("/auth/callback");
|
||||
expect(matches?.at(-1)?.route.path).toBe("/auth/callback");
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createBrowserRouter } from "react-router-dom";
|
||||
import type { RouteObject } from "react-router-dom";
|
||||
import AppLayout from "../components/layout/AppLayout";
|
||||
import ApiKeyCreatePage from "../features/api-keys/ApiKeyCreatePage";
|
||||
import ApiKeyListPage from "../features/api-keys/ApiKeyListPage";
|
||||
@@ -19,48 +20,51 @@ import { UserGroupDetailPage } from "../features/user-groups/routes/UserGroupDet
|
||||
import UserCreatePage from "../features/users/UserCreatePage";
|
||||
import UserDetailPage from "../features/users/UserDetailPage";
|
||||
import UserListPage from "../features/users/UserListPage";
|
||||
import { ADMIN_AUTH_CALLBACK_PATH } from "../lib/authConfig";
|
||||
|
||||
export const adminRoutes: RouteObject[] = [
|
||||
{
|
||||
path: "/login",
|
||||
element: <LoginPage />,
|
||||
},
|
||||
{
|
||||
path: ADMIN_AUTH_CALLBACK_PATH,
|
||||
element: <AuthCallbackPage />,
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
element: <AppLayout />,
|
||||
children: [
|
||||
{ index: true, element: <GlobalOverviewPage /> },
|
||||
{ path: "audit-logs", element: <AuditLogsPage /> },
|
||||
{ path: "auth", element: <AuthPage /> },
|
||||
{ path: "users", element: <UserListPage /> },
|
||||
{ path: "users/new", element: <UserCreatePage /> },
|
||||
{ path: "users/:id", element: <UserDetailPage /> },
|
||||
{ path: "tenants", element: <TenantListPage /> },
|
||||
{ path: "tenants/new", element: <TenantCreatePage /> },
|
||||
{
|
||||
path: "tenants/:tenantId",
|
||||
element: <TenantDetailPage />,
|
||||
children: [
|
||||
{ index: true, element: <TenantProfilePage /> },
|
||||
{ path: "permissions", element: <TenantAdminsAndOwnersTab /> },
|
||||
{ path: "organization", element: <TenantUserGroupsTab /> },
|
||||
{ path: "schema", element: <TenantSchemaPage /> },
|
||||
{ path: "worksmobile", element: <TenantWorksmobilePage /> },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "tenants/:tenantId/organization/:id",
|
||||
element: <TenantUserGroupsTab />,
|
||||
},
|
||||
{ path: "api-keys", element: <ApiKeyListPage /> },
|
||||
{ path: "api-keys/new", element: <ApiKeyCreatePage /> },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const router = createBrowserRouter(
|
||||
[
|
||||
{
|
||||
path: "/login",
|
||||
element: <LoginPage />,
|
||||
},
|
||||
{
|
||||
path: "/auth/callback",
|
||||
element: <AuthCallbackPage />,
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
element: <AppLayout />,
|
||||
children: [
|
||||
{ index: true, element: <GlobalOverviewPage /> },
|
||||
{ path: "audit-logs", element: <AuditLogsPage /> },
|
||||
{ path: "auth", element: <AuthPage /> },
|
||||
{ path: "users", element: <UserListPage /> },
|
||||
{ path: "users/new", element: <UserCreatePage /> },
|
||||
{ path: "users/:id", element: <UserDetailPage /> },
|
||||
{ path: "tenants", element: <TenantListPage /> },
|
||||
{ path: "tenants/new", element: <TenantCreatePage /> },
|
||||
{
|
||||
path: "tenants/:tenantId",
|
||||
element: <TenantDetailPage />,
|
||||
children: [
|
||||
{ index: true, element: <TenantProfilePage /> },
|
||||
{ path: "permissions", element: <TenantAdminsAndOwnersTab /> },
|
||||
{ path: "organization", element: <TenantUserGroupsTab /> },
|
||||
{ path: "schema", element: <TenantSchemaPage /> },
|
||||
{ path: "worksmobile", element: <TenantWorksmobilePage /> },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "tenants/:tenantId/organization/:id",
|
||||
element: <TenantUserGroupsTab />,
|
||||
},
|
||||
{ path: "api-keys", element: <ApiKeyListPage /> },
|
||||
{ path: "api-keys/new", element: <ApiKeyCreatePage /> },
|
||||
],
|
||||
},
|
||||
],
|
||||
adminRoutes,
|
||||
// React Router v7 플래그는 Provider에서 적용합니다.
|
||||
);
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
import { UserManager, WebStorageStateStore } from "oidc-client-ts";
|
||||
import type { AuthProviderProps } from "react-oidc-context";
|
||||
import {
|
||||
buildAdminAuthRedirectUris,
|
||||
resolveAdminPublicOrigin,
|
||||
} from "./authConfig";
|
||||
|
||||
const adminPublicOrigin = resolveAdminPublicOrigin(
|
||||
import.meta.env.VITE_ADMIN_PUBLIC_URL,
|
||||
window.location.origin,
|
||||
);
|
||||
const adminRedirectUris = buildAdminAuthRedirectUris(adminPublicOrigin);
|
||||
|
||||
export const oidcConfig: AuthProviderProps = {
|
||||
authority: import.meta.env.VITE_OIDC_AUTHORITY || "https://sso.hmac.kr/oidc", // Gateway Proxy URL
|
||||
client_id: import.meta.env.VITE_OIDC_CLIENT_ID || "adminfront",
|
||||
redirect_uri: `${window.location.origin}/auth/callback`,
|
||||
redirect_uri: adminRedirectUris.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: adminRedirectUris.postLogoutRedirectUri,
|
||||
popup_redirect_uri: adminRedirectUris.popupRedirectUri,
|
||||
userStore: new WebStorageStateStore({ store: window.localStorage }),
|
||||
automaticSilentRenew: false,
|
||||
};
|
||||
|
||||
27
adminfront/src/lib/authConfig.test.ts
Normal file
27
adminfront/src/lib/authConfig.test.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildAdminAuthRedirectUris,
|
||||
resolveAdminPublicOrigin,
|
||||
} from "./authConfig";
|
||||
|
||||
describe("admin auth config", () => {
|
||||
it("uses the explicit public admin origin for staging callback URLs", () => {
|
||||
const publicOrigin = resolveAdminPublicOrigin(
|
||||
"https://sadmin.hmac.kr",
|
||||
"http://127.0.0.1:5173",
|
||||
);
|
||||
|
||||
expect(publicOrigin).toBe("https://sadmin.hmac.kr");
|
||||
expect(buildAdminAuthRedirectUris(publicOrigin)).toEqual({
|
||||
redirectUri: "https://sadmin.hmac.kr/auth/callback",
|
||||
postLogoutRedirectUri: "https://sadmin.hmac.kr",
|
||||
popupRedirectUri: "https://sadmin.hmac.kr/auth/callback",
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to the browser origin when no explicit public origin is set", () => {
|
||||
expect(resolveAdminPublicOrigin("", "http://localhost:5173")).toBe(
|
||||
"http://localhost:5173",
|
||||
);
|
||||
});
|
||||
});
|
||||
33
adminfront/src/lib/authConfig.ts
Normal file
33
adminfront/src/lib/authConfig.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
export interface AdminAuthRedirectUris {
|
||||
redirectUri: string;
|
||||
postLogoutRedirectUri: string;
|
||||
popupRedirectUri: string;
|
||||
}
|
||||
|
||||
export const ADMIN_AUTH_CALLBACK_PATH = "/auth/callback";
|
||||
|
||||
export function resolveAdminPublicOrigin(
|
||||
configuredOrigin: string | undefined,
|
||||
browserOrigin: string,
|
||||
) {
|
||||
const trimmed = configuredOrigin?.trim();
|
||||
if (!trimmed) {
|
||||
return browserOrigin;
|
||||
}
|
||||
|
||||
try {
|
||||
return new URL(trimmed).origin;
|
||||
} catch {
|
||||
return browserOrigin;
|
||||
}
|
||||
}
|
||||
|
||||
export function buildAdminAuthRedirectUris(
|
||||
publicOrigin: string,
|
||||
): AdminAuthRedirectUris {
|
||||
return {
|
||||
redirectUri: `${publicOrigin}${ADMIN_AUTH_CALLBACK_PATH}`,
|
||||
postLogoutRedirectUri: publicOrigin,
|
||||
popupRedirectUri: `${publicOrigin}${ADMIN_AUTH_CALLBACK_PATH}`,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user