Refactor initalization of Keycloak JS in Admin Console (#20100)

This commit is contained in:
Jon Koops 2023-05-03 11:27:27 +00:00 committed by GitHub
parent 75ea22bad2
commit 228da0e29a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 72 additions and 79 deletions

View file

@ -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

View file

@ -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

View 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;
},
});

View file

@ -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);

View file

@ -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 };
}

View file

@ -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/${

View 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",
});

View file

@ -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");

View file

@ -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,
};

View file

@ -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);