Skip to content

OpenAM UI JS SDK #941

Draft
maximthomas wants to merge 5 commits intoOpenIdentityPlatform:masterfrom
maximthomas:openam-js-sdk
Draft

OpenAM UI JS SDK #941
maximthomas wants to merge 5 commits intoOpenIdentityPlatform:masterfrom
maximthomas:openam-js-sdk

Conversation

@maximthomas
Copy link
Contributor

Experimental UI based on React JS library.
Allows to develop a custom OpenAM UI using pluggable components, see the README.md file

@maximthomas maximthomas requested a review from vharseko November 26, 2025 10:05
@maximthomas maximthomas added the javascript Pull requests that update Javascript code label Nov 26, 2025
@vharseko vharseko changed the title OpenAM UI JS SDK initial commit OpenAM UI JS SDK Nov 26, 2025
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an experimental React/Vite-based “OpenAM UI JS SDK” module to the OpenAM UI build, packages it as distributable ZIP artifacts (app + library), and wires the app bundle into the openam-server-only WAR under extui/ (with a small CORS schema update to allow Accept-API-Version).

Changes:

  • Introduce new openam-ui-js-sdk Maven module with Vite/Vitest/TypeScript project (app + library builds, plus assemblies).
  • Package and embed the generated app ZIP into openam-server-only WAR under extui/.
  • Bump frontend build toolchain versions (frontend-maven-plugin, Node, npm) and update CORS allowed headers.

Reviewed changes

Copilot reviewed 48 out of 50 changed files in this pull request and generated 23 comments.

Show a summary per file
File Description
pom.xml Adds openam-ui-js-sdk app ZIP as a dependency in the root build.
openam-ui/pom.xml Registers new UI module and bumps frontend-maven-plugin/Node/npm versions.
openam-ui/openam-ui-js-sdk/vitest.config.js Vitest configuration (jsdom + setup file).
openam-ui/openam-ui-js-sdk/vite.lib.config.ts Vite config for library build + DTS generation.
openam-ui/openam-ui-js-sdk/vite.config.ts Vite config for app build output to target/app.
openam-ui/openam-ui-js-sdk/tsconfig.node.json TS config for Vite config files.
openam-ui/openam-ui-js-sdk/tsconfig.lib.json TS config for library build typings/source scope.
openam-ui/openam-ui-js-sdk/tsconfig.json Project references for TS build mode.
openam-ui/openam-ui-js-sdk/tsconfig.app.json TS config for app compilation scope.
openam-ui/openam-ui-js-sdk/src/main.tsx App entry point rendering OpenAMUI.
openam-ui/openam-ui-js-sdk/src/lib/userService.ts Client-side calls for user/session/profile/password operations.
openam-ui/openam-ui-js-sdk/src/lib/types.ts SDK response/data type definitions.
openam-ui/openam-ui-js-sdk/src/lib/setupTests.ts Test environment setup for RTL matchers.
openam-ui/openam-ui-js-sdk/src/lib/router.tsx Hash router definition + service instantiation.
openam-ui/openam-ui-js-sdk/src/lib/loginService.ts Client-side authentication request/callback submission helpers.
openam-ui/openam-ui-js-sdk/src/lib/index.ts Library public exports (components + config/types).
openam-ui/openam-ui-js-sdk/src/lib/env.d.ts Vite env typings for OpenAM URL configuration variables.
openam-ui/openam-ui-js-sdk/src/lib/config.ts Global mutable config + default component implementations.
openam-ui/openam-ui-js-sdk/src/lib/components/types.ts Pluggable UI component type contracts.
openam-ui/openam-ui-js-sdk/src/lib/components/DefaultUserForm.tsx Default user profile + change password modal UI.
openam-ui/openam-ui-js-sdk/src/lib/components/DefaultUserForm.test.tsx Tests for default user form behavior.
openam-ui/openam-ui-js-sdk/src/lib/components/DefaultLoginForm.tsx Default login form rendering callbacks + actions.
openam-ui/openam-ui-js-sdk/src/lib/components/DefaultLoginForm.test.tsx Tests for default login form integration with config.
openam-ui/openam-ui-js-sdk/src/lib/components/DefaultErrorForm.tsx Default error UI component.
openam-ui/openam-ui-js-sdk/src/lib/components/DefaultErrorForm.test.tsx Tests for default error UI.
openam-ui/openam-ui-js-sdk/src/lib/components/DefaultCallbackElement.tsx Default rendering for OpenAM callback types.
openam-ui/openam-ui-js-sdk/src/lib/components/DefaultCallbackElement.test.tsx Tests for callback element rendering/value updates.
openam-ui/openam-ui-js-sdk/src/lib/components/DefaultActionElements.tsx Default submit/action button rendering for callbacks.
openam-ui/openam-ui-js-sdk/src/lib/components/DefaultActionElements.test.tsx Tests for action element behavior.
openam-ui/openam-ui-js-sdk/src/lib/__tests__/mocks.ts Mock auth/user payloads for component tests.
openam-ui/openam-ui-js-sdk/src/lib/User.tsx User route: session check, profile load/save, password update.
openam-ui/openam-ui-js-sdk/src/lib/OpenAMUI.tsx SDK root component mounting the router provider.
openam-ui/openam-ui-js-sdk/src/lib/NotFoundPage.tsx 404 page component.
openam-ui/openam-ui-js-sdk/src/lib/Login.tsx Login route: init, submit callbacks, redirect or route change.
openam-ui/openam-ui-js-sdk/src/lib/Home.tsx Home route: redirect to login or user based on session.
openam-ui/openam-ui-js-sdk/src/index.scss Basic styling for forms/modals.
openam-ui/openam-ui-js-sdk/pom.xml Maven build for npm test/build + assembly ZIPs (app + lib).
openam-ui/openam-ui-js-sdk/package.json React/React Router/Vite/Vitest/TS toolchain definitions and scripts.
openam-ui/openam-ui-js-sdk/index.html Vite HTML entry document.
openam-ui/openam-ui-js-sdk/eslint.config.js ESLint config for TS/React hooks/refresh.
openam-ui/openam-ui-js-sdk/assembly/lib-zip.xml Assembly descriptor for library ZIP artifact.
openam-ui/openam-ui-js-sdk/assembly/app-zip.xml Assembly descriptor for app ZIP artifact.
openam-ui/openam-ui-js-sdk/README.md Usage/customization docs for app and SDK consumption.
openam-ui/openam-ui-js-sdk/.gitignore Ignores for node/vite outputs and zips.
openam-ui/openam-ui-js-sdk/.env.development Dev env defaults for OpenAM server/context path.
openam-ui/openam-ui-js-sdk/.env Sample env variables (commented).
openam-server-only/src/main/resources/services/amCORS.xml Allows Accept-API-Version header; bumps revisionNumber.
openam-server-only/pom.xml Downloads app ZIP, stages under extui/, and includes it in WAR resources; adds ZIP dependency.
.github/workflows/deploy.yml Excludes openam-ui-js-sdk from javadoc aggregate build.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +34 to +43
<resources>
<resource>
<directory>${project.basedir}/target/app</directory>
<filtering>true</filtering>
<includes>
<include>**</include>
<include>*</include>
</includes>
</resource>
</resources>
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Vite build output under target/app is configured with Maven resource filtering enabled. Filtering can corrupt bundled JS/CSS (e.g., sequences like ${...} in minified output). openam-ui-api’s analogous resource configuration uses <filtering>false</filtering>; consider doing the same here and only filter explicit template files if needed.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +3
import type { ErrorForm } from "./types";

const DefaultErrorForm: ErrorForm = ({ error, resetError }) => {
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is missing the standard CDDL header block that is present in the other new JS SDK source files. Please add the project’s license header here as well for consistency/compliance.

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +74
const response = await fetch(userUrl, {
method: "GET",
mode: "cors",
credentials: "include",
headers: {
"Content-Type": "application/json"
},
})
return await response.json();
} catch (e) {
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getUserData returns response.json() without checking response.ok. For non-2xx responses, this can silently propagate error payloads as UserData and break callers. Handle non-OK responses (e.g., parse an error shape and throw) before returning data.

Copilot uses AI. Check for mistakes.
Comment on lines +96 to +108
const response = await fetch(userUrl, {
method: "PUT",
mode: "cors",
credentials: "include",
headers: {
"Accept-API-Version": "resource=2.0, protocol=1.0",
"Content-Type": "application/json"
},
body: JSON.stringify(dataToUpdate),
}
)
return await response.json();
}
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saveUserData returns response.json() even when the server responds with a non-2xx status. This can make the UI treat error payloads as successful updates. Add a response.ok check and surface a useful error (similar to savePassword).

Copilot uses AI. Check for mistakes.
- **Ease of Use**: Pre-configured React components ready for integration.
- **Modular & Flexible**: Easily swap components and customize the SDK to suit your needs.
- **TypeScript Support**: Enhance development experience with type safety and better code completion.
- **Seamless Integration**: Easily integrate OpenAM with minimal configura
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo/incomplete word: "minimal configura" should be "minimal configuration".

Suggested change
- **Seamless Integration**: Easily integrate OpenAM with minimal configura
- **Seamless Integration**: Easily integrate OpenAM with minimal configuration.

Copilot uses AI. Check for mistakes.
Comment on lines +87 to +99
import { setConfig } from 'openam-js-sdk'

setConfig({
openamServer: 'https://openam.example.org:443',
openamContextPath: '/am',
errorForm: ({ error, resetError }) => {
return <div>
<h1>An error occurred</h1>
<p>{error?.message}</p>
<input type="button" value="Retry" onClick={() => resetError()} />
</div>
})

Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The customization example uses errorForm, but the config interface property is ErrorForm (capital E) and the default config also uses ErrorForm. As written, the example won’t override the error UI. Update the README example to match the actual config API shape.

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +35
setConfig({
CallbackElement: mockCallbackElement,
ActionElements: mockActionElements,
})

Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test mutates the global singleton config via setConfig at module initialization time, which can leak state into other test files depending on execution order. Prefer setting config in beforeEach/beforeAll and restoring the previous config in afterEach/afterAll to keep tests isolated.

Copilot uses AI. Check for mistakes.

### As an Application

Copy the contents of the `dist/app` folder into your OpenAM WAR file (or the extracted WAR contents in your web container), e.g., into a directory like `extui`, so it could be accessible in your OpenAM context path, for example, http://openam.example.org:8080/openam/extui
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README refers to copying dist/app, but this project’s build output is configured to go to target/app (see vite.config.ts and the Maven assembly descriptors). Update the documentation to point to the correct output path/artifact so the instructions work.

Suggested change
Copy the contents of the `dist/app` folder into your OpenAM WAR file (or the extracted WAR contents in your web container), e.g., into a directory like `extui`, so it could be accessible in your OpenAM context path, for example, http://openam.example.org:8080/openam/extui
Copy the contents of the `target/app` folder into your OpenAM WAR file (or the extracted WAR contents in your web container), e.g., into a directory like `extui`, so it could be accessible in your OpenAM context path, for example, http://openam.example.org:8080/openam/extui

Copilot uses AI. Check for mistakes.
Comment on lines +86 to +104
//update the default configuration
import { setConfig } from 'openam-js-sdk'

setConfig({
openamServer: 'https://openam.example.org:443',
openamContextPath: '/am',
errorForm: ({ error, resetError }) => {
return <div>
<h1>An error occurred</h1>
<p>{error?.message}</p>
<input type="button" value="Retry" onClick={() => resetError()} />
</div>
})

createRoot(document.getElementById('root')!).render(
<StrictMode>
<OpenAMUI />
</StrictMode>,
)
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The configuration override example has multiple copy/paste issues: it uses errorForm instead of ErrorForm, and the snippet as written is not valid TSX/TS (unbalanced braces/parentheses and missing imports like createRoot, StrictMode, OpenAMUI). Please fix the snippet so it compiles and matches the actual exported API.

Suggested change
//update the default configuration
import { setConfig } from 'openam-js-sdk'
setConfig({
openamServer: 'https://openam.example.org:443',
openamContextPath: '/am',
errorForm: ({ error, resetError }) => {
return <div>
<h1>An error occurred</h1>
<p>{error?.message}</p>
<input type="button" value="Retry" onClick={() => resetError()} />
</div>
})
createRoot(document.getElementById('root')!).render(
<StrictMode>
<OpenAMUI />
</StrictMode>,
)
// update the default configuration
import React, { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { OpenAMUI, setConfig } from 'openam-js-sdk';
setConfig({
openamServer: 'https://openam.example.org:443',
openamContextPath: '/am',
ErrorForm: ({ error, resetError }) => {
return (
<div>
<h1>An error occurred</h1>
<p>{error?.message}</p>
<input
type="button"
value="Retry"
onClick={() => resetError()}
/>
</div>
);
},
});
createRoot(document.getElementById('root')!).render(
<StrictMode>
<OpenAMUI />
</StrictMode>,
);

Copilot uses AI. Check for mistakes.
Comment on lines +26 to +30
const config = getConfig();
const userService = new UserService(config.getOpenAmUrl());
const loginService = new LoginService(config.getOpenAmUrl());

const router = createHashRouter([
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config, userService, loginService, and the router are instantiated at module load time. Since consumers are expected to call setConfig(...) to customize openamServer/openamContextPath, importing OpenAMUI (via src/lib/index.ts) will run this file before setConfig is called, so the services/router can be locked to the default URL. Consider creating the router/services lazily (e.g., factory function or inside OpenAMUI with useMemo) so updates via setConfig take effect.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

javascript Pull requests that update Javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants