diff --git a/devfront/src/components/layout/AppLayout.tsx b/devfront/src/components/layout/AppLayout.tsx index 906c1d51..25939d79 100644 --- a/devfront/src/components/layout/AppLayout.tsx +++ b/devfront/src/components/layout/AppLayout.tsx @@ -14,13 +14,6 @@ import { import { useEffect, useRef, useState } from "react"; import { useAuth } from "react-oidc-context"; import { NavLink, Outlet, useLocation, useNavigate } from "react-router-dom"; -import { fetchMe } from "../../features/auth/authApi"; -import { t } from "../../lib/i18n"; -import { resolveProfileRole } from "../../lib/role"; -import { - shouldAttemptSlidingSessionRenew, - shouldAttemptUnlimitedSessionRenew, -} from "../../lib/sessionSliding"; import { applyShellTheme, buildShellProfileSummary, @@ -30,6 +23,13 @@ import { shellLayoutClasses, writeShellSessionExpiryEnabled, } from "../../../../common/shell"; +import { fetchMe } from "../../features/auth/authApi"; +import { t } from "../../lib/i18n"; +import { resolveProfileRole } from "../../lib/role"; +import { + shouldAttemptSlidingSessionRenew, + shouldAttemptUnlimitedSessionRenew, +} from "../../lib/sessionSliding"; import LanguageSelector from "../common/LanguageSelector"; import { Toaster } from "../ui/toaster"; diff --git a/devfront/src/features/clients/ClientsPage.tsx b/devfront/src/features/clients/ClientsPage.tsx index 78f670b0..866a7a1c 100644 --- a/devfront/src/features/clients/ClientsPage.tsx +++ b/devfront/src/features/clients/ClientsPage.tsx @@ -14,10 +14,10 @@ import { useEffect, useMemo, useState } from "react"; import { useAuth } from "react-oidc-context"; import { Link, useNavigate } from "react-router-dom"; import { - sortItems, - toggleSort, type SortConfig, type SortResolverMap, + sortItems, + toggleSort, } from "../../../../common/core/utils"; import { ForbiddenMessage } from "../../components/common/ForbiddenMessage"; import { @@ -47,12 +47,12 @@ import { } from "../../components/ui/table"; import { Textarea } from "../../components/ui/textarea"; import { + type ClientSummary, fetchClients, fetchDevStats, fetchDeveloperRequestStatus, fetchMyTenants, requestDeveloperAccess, - type ClientSummary, } from "../../lib/devApi"; import { t } from "../../lib/i18n"; import { resolveProfileRole } from "../../lib/role"; diff --git a/devfront/src/features/dashboard/DashboardPage.tsx b/devfront/src/features/dashboard/DashboardPage.tsx index 70062067..92638e0f 100644 --- a/devfront/src/features/dashboard/DashboardPage.tsx +++ b/devfront/src/features/dashboard/DashboardPage.tsx @@ -13,12 +13,12 @@ import { useAuth } from "react-oidc-context"; import { useNavigate } from "react-router-dom"; import { type ClientSummary, - fetchClients, - fetchDeveloperRequestStatus, - fetchDevRPUsageDaily, - fetchDevStats, type RPUsageDailyMetric, type RPUsagePeriod, + fetchClients, + fetchDevRPUsageDaily, + fetchDevStats, + fetchDeveloperRequestStatus, } from "../../lib/devApi"; import { t } from "../../lib/i18n"; import { resolveProfileRole } from "../../lib/role"; diff --git a/devfront/src/lib/authConfig.test.ts b/devfront/src/lib/authConfig.test.ts index e5abdb1a..1974d6d7 100644 --- a/devfront/src/lib/authConfig.test.ts +++ b/devfront/src/lib/authConfig.test.ts @@ -29,6 +29,13 @@ describe("devfront auth config", () => { }); it("blocks browser PKCE login in an insecure context", () => { + expect( + canStartBrowserPkceLogin({ + isSecureContext: false, + origin: "http://localhost:5174", + cryptoSubtleAvailable: false, + }), + ).toBe(false); expect( canStartBrowserPkceLogin({ isSecureContext: false, diff --git a/devfront/tests/devfront-login.spec.ts b/devfront/tests/devfront-login.spec.ts index 6cb081fa..7b5a248f 100644 --- a/devfront/tests/devfront-login.spec.ts +++ b/devfront/tests/devfront-login.spec.ts @@ -9,31 +9,39 @@ test.describe("DevFront login", () => { configurable: true, value: false, }); + Object.defineProperty(window.crypto, "subtle", { + configurable: true, + value: undefined, + }); }); let authorizeRequested = false; - await page.route("**/oidc/.well-known/openid-configuration", async (route) => { - await route.fulfill({ - json: { - issuer: "http://localhost:5000/oidc", - authorization_endpoint: "http://localhost:5000/oidc/oauth2/auth", - token_endpoint: "http://localhost:5000/oidc/oauth2/token", - jwks_uri: "http://localhost:5000/oidc/.well-known/jwks.json", - }, - headers: { "Access-Control-Allow-Origin": "*" }, - }); - }); + await page.route( + "**/oidc/.well-known/openid-configuration", + async (route) => { + await route.fulfill({ + json: { + issuer: "http://localhost:5000/oidc", + authorization_endpoint: "http://localhost:5000/oidc/oauth2/auth", + token_endpoint: "http://localhost:5000/oidc/oauth2/token", + jwks_uri: "http://localhost:5000/oidc/.well-known/jwks.json", + }, + headers: { "Access-Control-Allow-Origin": "*" }, + }); + }, + ); await page.route("**/oidc/oauth2/auth**", async (route) => { authorizeRequested = true; - await route.fulfill({ status: 500, body: "unexpected authorize request" }); + await route.fulfill({ + status: 500, + body: "unexpected authorize request", + }); }); await page.goto("/login"); await page.getByRole("button", { name: "SSO 계정으로 로그인" }).click(); - await expect(page.getByRole("alert")).toContainText( - "HTTPS 또는 localhost", - ); + await expect(page.getByRole("alert")).toContainText("HTTPS 또는 localhost"); expect(authorizeRequested).toBe(false); }); });