diff --git a/js/apps/admin-ui/src/App.tsx b/js/apps/admin-ui/src/App.tsx index 2762f8bd06..6ad104fe9c 100644 --- a/js/apps/admin-ui/src/App.tsx +++ b/js/apps/admin-ui/src/App.tsx @@ -1,6 +1,4 @@ -import type KeycloakAdminClient from "@keycloak/keycloak-admin-client"; import { Page } from "@patternfly/react-core"; -import type Keycloak from "keycloak-js"; import { PropsWithChildren, Suspense } from "react"; import { ErrorBoundary } from "react-error-boundary"; import { Outlet } from "react-router-dom"; @@ -15,7 +13,7 @@ import { KeycloakSpinner } from "./components/keycloak-spinner/KeycloakSpinner"; import { RealmsProvider } from "./context/RealmsContext"; import { RecentRealmsProvider } from "./context/RecentRealms"; import { AccessContextProvider } from "./context/access/Access"; -import { AdminClientContext } from "./context/auth/AdminClient"; +import { AdminClientProvider } from "./context/auth/AdminClient"; import { RealmContextProvider } from "./context/realm-context/RealmContext"; import { ServerInfoProvider } from "./context/server-info/ServerInfoProvider"; import { WhoAmIContextProvider } from "./context/whoami/WhoAmI"; @@ -24,17 +22,8 @@ import { AuthWall } from "./root/AuthWall"; export const mainPageContentId = "kc-main-content-page-container"; -export type AdminClientProps = { - keycloak: Keycloak; - adminClient: KeycloakAdminClient; -}; - -const AppContexts = ({ - children, - keycloak, - adminClient, -}: PropsWithChildren) => ( - +const AppContexts = ({ children }: PropsWithChildren) => ( + @@ -50,12 +39,12 @@ const AppContexts = ({ - + ); -export const App = ({ keycloak, adminClient }: AdminClientProps) => { +export const App = () => { return ( - + } isManagedSidebar diff --git a/js/apps/admin-ui/src/PageHeader.tsx b/js/apps/admin-ui/src/PageHeader.tsx index 9c1560ac5d..dff094f89f 100644 --- a/js/apps/admin-ui/src/PageHeader.tsx +++ b/js/apps/admin-ui/src/PageHeader.tsx @@ -15,16 +15,16 @@ import { HelpIcon } from "@patternfly/react-icons"; import { ReactNode, useState } from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; -import { HelpHeader } from "./components/help-enabler/HelpHeader"; import { useHelp } from "ui-shared"; -import { useAdminClient } from "./context/auth/AdminClient"; + +import { HelpHeader } from "./components/help-enabler/HelpHeader"; import { useRealm } from "./context/realm-context/RealmContext"; import { useWhoAmI } from "./context/whoami/WhoAmI"; import { toDashboard } from "./dashboard/routes/Dashboard"; import environment from "./environment"; +import { keycloak } from "./keycloak"; const ManageAccountDropdownItem = () => { - const { keycloak } = useAdminClient(); const { t } = useTranslation(); return ( { }; const SignOutDropdownItem = () => { - const { keycloak } = useAdminClient(); const { t } = useTranslation(); return ( { const { realm } = useRealm(); const headerTools = () => { - const adminClient = useAdminClient(); - const picture = adminClient.keycloak.tokenParsed?.picture; + const picture = keycloak.tokenParsed?.picture; return ( ( ); export const RealmsProvider = ({ children }: PropsWithChildren) => { - const { keycloak, adminClient } = useAdminClient(); + const { adminClient } = useAdminClient(); const [realms, setRealms] = useState([]); const [refreshCount, setRefreshCount] = useState(0); diff --git a/js/apps/admin-ui/src/context/auth/AdminClient.tsx b/js/apps/admin-ui/src/context/auth/AdminClient.tsx index e4a95a55f1..f61fcec9c5 100644 --- a/js/apps/admin-ui/src/context/auth/AdminClient.tsx +++ b/js/apps/admin-ui/src/context/auth/AdminClient.tsx @@ -1,19 +1,24 @@ import KeycloakAdminClient from "@keycloak/keycloak-admin-client"; -import Keycloak from "keycloak-js"; -import { DependencyList, useEffect } from "react"; +import { DependencyList, PropsWithChildren, useEffect } from "react"; import { useErrorHandler } from "react-error-boundary"; - -import environment from "../../environment"; import { createNamedContext, useRequiredContext } from "ui-shared"; +import { adminClient } from "../../admin-client"; + export type AdminClientProps = { - keycloak: Keycloak; adminClient: KeycloakAdminClient; }; -export const AdminClientContext = createNamedContext< - AdminClientProps | undefined ->("AdminClientContext", undefined); +const AdminClientContext = createNamedContext( + "AdminClientContext", + undefined +); + +export const AdminClientProvider = ({ children }: PropsWithChildren) => ( + + {children} + +); export const useAdminClient = () => useRequiredContext(AdminClientContext); @@ -58,33 +63,3 @@ export function useFetch( return () => controller.abort(); }, deps); } - -export async function initAdminClient() { - const keycloak = new Keycloak({ - url: environment.authServerUrl, - realm: environment.loginRealm, - clientId: environment.isRunningAsTheme - ? "security-admin-console" - : "security-admin-console-v2", - }); - - await keycloak.init({ onLoad: "check-sso", pkceMethod: "S256" }); - - const adminClient = new KeycloakAdminClient(); - - adminClient.setConfig({ realmName: environment.loginRealm }); - adminClient.baseUrl = environment.authUrl; - adminClient.registerTokenProvider({ - async getAccessToken() { - try { - await keycloak.updateToken(5); - } catch (error) { - keycloak.login(); - } - - return keycloak.token; - }, - }); - - return { keycloak, adminClient }; -} diff --git a/js/apps/admin-ui/src/i18n.ts b/js/apps/admin-ui/src/i18n.ts index b939c3f103..20df08410e 100644 --- a/js/apps/admin-ui/src/i18n.ts +++ b/js/apps/admin-ui/src/i18n.ts @@ -1,22 +1,20 @@ -import { init, use, InitOptions, TOptions } from "i18next"; +import { InitOptions, TOptions, init, use } from "i18next"; import HttpBackend, { LoadPathOption } from "i18next-http-backend"; import { initReactI18next } from "react-i18next"; -import type KeycloakAdminClient from "@keycloak/keycloak-admin-client"; +import { adminClient } from "./admin-client"; import environment from "./environment"; -import { getAuthorizationHeaders } from "./utils/getAuthorizationHeaders"; import { addTrailingSlash } from "./util"; +import { getAuthorizationHeaders } from "./utils/getAuthorizationHeaders"; export const DEFAULT_LOCALE = "en"; -export async function initI18n(adminClient: KeycloakAdminClient) { - const options = await initOptions(adminClient); +export async function initI18n() { + const options = await initOptions(); await init(options); } -const initOptions = async ( - adminClient: KeycloakAdminClient -): Promise => { +const initOptions = async (): Promise => { const constructLoadPath: LoadPathOption = (_, namespaces) => { if (namespaces[0] === "overrides") { return `${addTrailingSlash(adminClient.baseUrl)}admin/realms/${ diff --git a/js/apps/admin-ui/src/keycloak.ts b/js/apps/admin-ui/src/keycloak.ts new file mode 100644 index 0000000000..7fe93eecd1 --- /dev/null +++ b/js/apps/admin-ui/src/keycloak.ts @@ -0,0 +1,11 @@ +import Keycloak from "keycloak-js"; + +import environment from "./environment"; + +export const keycloak = new Keycloak({ + url: environment.authServerUrl, + realm: environment.loginRealm, + clientId: environment.isRunningAsTheme + ? "security-admin-console" + : "security-admin-console-v2", +}); diff --git a/js/apps/admin-ui/src/main.tsx b/js/apps/admin-ui/src/main.tsx index 24e0acbeea..e69cc9bb77 100644 --- a/js/apps/admin-ui/src/main.tsx +++ b/js/apps/admin-ui/src/main.tsx @@ -5,10 +5,16 @@ import { StrictMode } from "react"; import { render } from "react-dom"; import { createHashRouter, RouterProvider } from "react-router-dom"; +import { initI18n } from "./i18n"; +import { keycloak } from "./keycloak"; import { RootRoute } from "./routes"; import "./index.css"; +// Initialize required components before rendering app. +await keycloak.init({ onLoad: "check-sso", pkceMethod: "S256" }); +await initI18n(); + const router = createHashRouter([RootRoute]); const container = document.getElementById("app"); diff --git a/js/apps/admin-ui/src/routes.tsx b/js/apps/admin-ui/src/routes.tsx index f9275f1f15..2a76ca999d 100644 --- a/js/apps/admin-ui/src/routes.tsx +++ b/js/apps/admin-ui/src/routes.tsx @@ -2,8 +2,6 @@ import type { AccessType } from "@keycloak/keycloak-admin-client/lib/defs/whoAmI import type { TFunction } from "i18next"; import type { ComponentType } from "react"; import type { NonIndexRouteObject, RouteObject } from "react-router"; -import { initAdminClient } from "./context/auth/AdminClient"; -import { initI18n } from "./i18n"; import { App } from "./App"; import { PageNotFoundSection } from "./PageNotFoundSection"; @@ -56,12 +54,8 @@ export const routes: AppRouteObject[] = [ NotFoundRoute, ]; -const { keycloak, adminClient } = await initAdminClient(); - -await initI18n(adminClient); - export const RootRoute: RouteObject = { path: "/", - element: , + element: , children: routes, }; diff --git a/js/apps/admin-ui/src/sessions/SessionsTable.tsx b/js/apps/admin-ui/src/sessions/SessionsTable.tsx index 1aca739a15..00ef035383 100644 --- a/js/apps/admin-ui/src/sessions/SessionsTable.tsx +++ b/js/apps/admin-ui/src/sessions/SessionsTable.tsx @@ -24,6 +24,7 @@ import { import { useAdminClient } from "../context/auth/AdminClient"; import { useRealm } from "../context/realm-context/RealmContext"; import { useWhoAmI } from "../context/whoami/WhoAmI"; +import { keycloak } from "../keycloak"; import { toUser } from "../user/routes/User"; import useFormatDate from "../utils/useFormatDate"; @@ -78,7 +79,7 @@ export default function SessionsTable({ const { realm } = useRealm(); const { whoAmI } = useWhoAmI(); const { t } = useTranslation("sessions"); - const { keycloak, adminClient } = useAdminClient(); + const { adminClient } = useAdminClient(); const { addError } = useAlerts(); const formatDate = useFormatDate(); const [key, setKey] = useState(0);