Refactor initalization of Keycloak JS in Admin Console (#20100)
This commit is contained in:
parent
75ea22bad2
commit
228da0e29a
10 changed files with 72 additions and 79 deletions
|
@ -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<AdminClientProps>) => (
|
||||
<AdminClientContext.Provider value={{ keycloak, adminClient }}>
|
||||
const AppContexts = ({ children }: PropsWithChildren) => (
|
||||
<AdminClientProvider>
|
||||
<WhoAmIContextProvider>
|
||||
<RealmsProvider>
|
||||
<RealmContextProvider>
|
||||
|
@ -50,12 +39,12 @@ const AppContexts = ({
|
|||
</RealmContextProvider>
|
||||
</RealmsProvider>
|
||||
</WhoAmIContextProvider>
|
||||
</AdminClientContext.Provider>
|
||||
</AdminClientProvider>
|
||||
);
|
||||
|
||||
export const App = ({ keycloak, adminClient }: AdminClientProps) => {
|
||||
export const App = () => {
|
||||
return (
|
||||
<AppContexts keycloak={keycloak} adminClient={adminClient}>
|
||||
<AppContexts>
|
||||
<Page
|
||||
header={<Header />}
|
||||
isManagedSidebar
|
||||
|
|
|
@ -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 (
|
||||
<DropdownItem
|
||||
|
@ -38,7 +38,6 @@ const ManageAccountDropdownItem = () => {
|
|||
};
|
||||
|
||||
const SignOutDropdownItem = () => {
|
||||
const { keycloak } = useAdminClient();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<DropdownItem
|
||||
|
@ -134,8 +133,7 @@ export const Header = () => {
|
|||
const { realm } = useRealm();
|
||||
|
||||
const headerTools = () => {
|
||||
const adminClient = useAdminClient();
|
||||
const picture = adminClient.keycloak.tokenParsed?.picture;
|
||||
const picture = keycloak.tokenParsed?.picture;
|
||||
return (
|
||||
<PageHeaderTools>
|
||||
<PageHeaderToolsGroup
|
||||
|
|
20
js/apps/admin-ui/src/admin-client.ts
Normal file
20
js/apps/admin-ui/src/admin-client.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import KeycloakAdminClient from "@keycloak/keycloak-admin-client";
|
||||
|
||||
import environment from "./environment";
|
||||
import { keycloak } from "./keycloak";
|
||||
|
||||
export 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;
|
||||
},
|
||||
});
|
|
@ -2,8 +2,9 @@ import { NetworkError } from "@keycloak/keycloak-admin-client";
|
|||
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
|
||||
import { sortBy } from "lodash-es";
|
||||
import { PropsWithChildren, useCallback, useMemo, useState } from "react";
|
||||
|
||||
import { createNamedContext, useRequiredContext } from "ui-shared";
|
||||
|
||||
import { keycloak } from "../keycloak";
|
||||
import { useAdminClient, useFetch } from "./auth/AdminClient";
|
||||
|
||||
type RealmsContextProps = {
|
||||
|
@ -19,7 +20,7 @@ export const RealmsContext = createNamedContext<RealmsContextProps | undefined>(
|
|||
);
|
||||
|
||||
export const RealmsProvider = ({ children }: PropsWithChildren) => {
|
||||
const { keycloak, adminClient } = useAdminClient();
|
||||
const { adminClient } = useAdminClient();
|
||||
const [realms, setRealms] = useState<RealmRepresentation[]>([]);
|
||||
const [refreshCount, setRefreshCount] = useState(0);
|
||||
|
||||
|
|
|
@ -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<AdminClientProps | undefined>(
|
||||
"AdminClientContext",
|
||||
undefined
|
||||
);
|
||||
|
||||
export const AdminClientProvider = ({ children }: PropsWithChildren) => (
|
||||
<AdminClientContext.Provider value={{ adminClient }}>
|
||||
{children}
|
||||
</AdminClientContext.Provider>
|
||||
);
|
||||
|
||||
export const useAdminClient = () => useRequiredContext(AdminClientContext);
|
||||
|
||||
|
@ -58,33 +63,3 @@ export function useFetch<T>(
|
|||
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 };
|
||||
}
|
||||
|
|
|
@ -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<InitOptions> => {
|
||||
const initOptions = async (): Promise<InitOptions> => {
|
||||
const constructLoadPath: LoadPathOption = (_, namespaces) => {
|
||||
if (namespaces[0] === "overrides") {
|
||||
return `${addTrailingSlash(adminClient.baseUrl)}admin/realms/${
|
||||
|
|
11
js/apps/admin-ui/src/keycloak.ts
Normal file
11
js/apps/admin-ui/src/keycloak.ts
Normal file
|
@ -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",
|
||||
});
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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: <App keycloak={keycloak} adminClient={adminClient} />,
|
||||
element: <App />,
|
||||
children: routes,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue