Skip to content

Commit 189f275

Browse files
feature: move oidc config to frontend-config (#77)
1 parent 8660c0b commit 189f275

File tree

4 files changed

+63
-48
lines changed

4 files changed

+63
-48
lines changed

frontend-config.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,10 @@
22
"backendUrl": "http://localhost:3000",
33
"gatewayUrl": "https://gateway.ui.dev-core.mcpd.shoot.canary.k8s-hana.ondemand.com/kubernetes/graphql",
44
"landscape": "LOCAL",
5-
"documentationBaseUrl": "http://localhost:3000"
5+
"documentationBaseUrl": "http://localhost:3000",
6+
"oidcConfig": {
7+
"clientId": "clientId",
8+
"issuerUrl": "issuer-url",
9+
"scopes": []
10+
}
611
}
Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,36 @@
1-
import { ReactNode, use } from 'react';
2-
import { AuthProvider, AuthProviderProps } from 'react-oidc-context';
3-
import { useFrontendConfig } from './FrontendConfigContext.tsx';
4-
import { LoadCrateKubeConfig } from '../lib/oidc/crate.ts';
1+
import { ReactNode } from 'react';
2+
import { AuthProvider } from 'react-oidc-context';
3+
import { OIDCConfig, useFrontendConfig } from './FrontendConfigContext.tsx';
4+
import { WebStorageStateStore } from "oidc-client-ts";
5+
import { AuthProviderProps } from "react-oidc-context";
6+
57

68
interface AuthProviderOnboardingProps {
79
children?: ReactNode;
810
}
911

10-
// Promise needs to be cached
11-
// https://react.dev/blog/2024/12/05/react-19#use-does-not-support-promises-created-in-render
12-
const fetchAuthPromiseCache = new Map<string, Promise<AuthProviderProps>>();
13-
1412
export function AuthProviderOnboarding({
1513
children,
1614
}: AuthProviderOnboardingProps) {
17-
const { backendUrl } = useFrontendConfig();
18-
19-
const fetchAuthConfigPromise =
20-
fetchAuthPromiseCache.get(backendUrl) ?? LoadCrateKubeConfig(backendUrl);
21-
fetchAuthPromiseCache.set(backendUrl, fetchAuthConfigPromise);
22-
23-
const authConfig = use(fetchAuthConfigPromise);
15+
const { oidcConfig } = useFrontendConfig();
2416

17+
const authConfig = buildAuthProviderConfig(oidcConfig);
2518
return <AuthProvider {...authConfig}>{children}</AuthProvider>;
2619
}
20+
21+
function buildAuthProviderConfig(oidcConfig: OIDCConfig) {
22+
const userStore = new WebStorageStateStore({ store: window.localStorage });
23+
24+
const props: AuthProviderProps = {
25+
authority: oidcConfig.issuerUrl,
26+
client_id: oidcConfig.clientId,
27+
redirect_uri: window.location.origin,
28+
scope: oidcConfig.scopes.join(' '),
29+
userStore: userStore,
30+
automaticSilentRenew: false, // we show a window instead that asks the user to renew the token
31+
onSigninCallback: () => {
32+
window.history.replaceState({}, document.title, window.location.pathname);
33+
},
34+
};
35+
return props;
36+
}
Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ReactNode, createContext, use } from 'react';
22
import { DocLinkCreator } from '../lib/shared/links';
3+
import { z } from 'zod';
34

45
export enum Landscape {
56
Live = 'LIVE',
@@ -9,18 +10,16 @@ export enum Landscape {
910
Local = 'LOCAL',
1011
}
1112

12-
interface FrontendConfigContextProps {
13-
backendUrl: string;
14-
gatewayUrl: string;
15-
landscape?: Landscape;
16-
documentationBaseUrl: string;
13+
14+
15+
interface FrontendConfigContextType extends FrontendConfig {
1716
links: DocLinkCreator;
1817
}
1918

2019
export const FrontendConfigContext =
21-
createContext<FrontendConfigContextProps | null>(null);
20+
createContext<FrontendConfigContextType | null>(null);
2221

23-
const fetchPromise = fetch('/frontend-config.json').then((res) => res.json());
22+
const fetchPromise = fetch('/frontend-config.json').then((res) => res.json()).then((data) => validateAndCastFrontendConfig(data));
2423

2524
interface FrontendConfigProviderProps {
2625
children: ReactNode;
@@ -31,14 +30,10 @@ export function FrontendConfigProvider({
3130
}: FrontendConfigProviderProps) {
3231
const config = use(fetchPromise);
3332
const docLinks = new DocLinkCreator(config.documentationBaseUrl);
34-
const value: FrontendConfigContextProps = {
33+
const value: FrontendConfigContextType = {
3534
links: docLinks,
36-
backendUrl: config.backendUrl,
37-
gatewayUrl: config.gatewayUrl,
38-
landscape: config.landscape,
39-
documentationBaseUrl: config.documentationBaseUrl,
35+
...config,
4036
};
41-
4237
return (
4338
<FrontendConfigContext value={value}>{children}</FrontendConfigContext>
4439
);
@@ -54,3 +49,27 @@ export const useFrontendConfig = () => {
5449
}
5550
return context;
5651
};
52+
53+
const OidcConfigSchema = z.object({
54+
clientId: z.string(),
55+
issuerUrl: z.string(),
56+
scopes: z.array(z.string()),
57+
});
58+
export type OIDCConfig = z.infer<typeof OidcConfigSchema>;
59+
60+
const FrontendConfigSchema = z.object({
61+
backendUrl: z.string(),
62+
gatewayUrl: z.string(),
63+
documentationBaseUrl: z.string(),
64+
oidcConfig: OidcConfigSchema,
65+
landscape: z.optional(z.nativeEnum(Landscape)),
66+
});
67+
type FrontendConfig = z.infer<typeof FrontendConfigSchema>;
68+
69+
function validateAndCastFrontendConfig(config: unknown): FrontendConfig {
70+
try {
71+
return FrontendConfigSchema.parse(config);
72+
} catch (error) {
73+
throw new Error(`Invalid frontend config: ${error}`);
74+
}
75+
}

src/lib/oidc/crate.ts

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)