diff --git a/ui/.dockerignore b/ui/.dockerignore new file mode 100644 index 0000000000..dcb7653c9c --- /dev/null +++ b/ui/.dockerignore @@ -0,0 +1,17 @@ +# The image serves static files only: the Dockerfile copies index.html, +# silent-callback.html and dist/, and nothing else is needed at build time. +# +# Without this file the whole ui/ tree is sent to the daemon as build context - +# node_modules alone dwarfs everything the image actually contains, which makes a +# three-COPY build take minutes. +# +# Allowlist rather than denylist: adding a COPY without extending this list fails +# the build instead of silently depending on whatever happens to be lying around. +* +!index.html +!silent-callback.html +!dist + +# Source maps are only produced by `npm start` (esbuild serve); keep stale ones left in a +# local dist/ out of locally built images. +dist/**/*.map diff --git a/ui/Dockerfile b/ui/Dockerfile index aae579baa6..7bc9fbc23b 100644 --- a/ui/Dockerfile +++ b/ui/Dockerfile @@ -14,4 +14,5 @@ FROM nginxinc/nginx-unprivileged:alpine WORKDIR /usr/share/nginx/html COPY ./index.html . +COPY ./silent-callback.html . COPY ./dist ./dist diff --git a/ui/build.mjs b/ui/build.mjs index 9c504936a0..e2fe3b3c3b 100644 --- a/ui/build.mjs +++ b/ui/build.mjs @@ -3,7 +3,7 @@ import * as esbuild from 'esbuild'; import {sassPlugin} from 'esbuild-sass-plugin'; const config = { - entryPoints: ['main.ts'], + entryPoints: ['main.ts', 'silent-callback.ts'], bundle: true, outdir: 'dist', loader: { diff --git a/ui/silent-callback.html b/ui/silent-callback.html index 1cf061774a..e77fde2ec5 100644 --- a/ui/silent-callback.html +++ b/ui/silent-callback.html @@ -4,28 +4,6 @@ Silent Refresh Callback - + - - diff --git a/ui/silent-callback.ts b/ui/silent-callback.ts new file mode 100644 index 0000000000..11918ba5b8 --- /dev/null +++ b/ui/silent-callback.ts @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +import { UserManager, UserManagerSettings } from 'oidc-client-ts'; + +/* + * Entry point for silent-callback.html, the page oidc-client-ts loads in a hidden iframe as + * `silent_redirect_uri` when renewing an access token without a refresh token. + * + * The page's only job is to hand the response URL back to the parent frame; + * `signinSilentCallback()` delegates to IFrameNavigator.callback(), which reads nothing from + * the settings except the optional `iframeNotifyParentOrigin` (defaulting to this page's own + * origin). Hence the placeholder settings below: `authority`, `client_id` and `redirect_uri` are + * required by UserManagerSettings but are never touched on this path. + * + * Because the settings are hard-coded, a provider's configured `iframeNotifyParentOrigin` is not + * applied here, so this page only works when served from the same origin as the UI itself (as + * the Docker image does). A `silent_redirect_uri` on a different origin is not supported. + * + * Passing an object at all is what matters - `new UserManager()` throws, because + * UserManagerSettingsStore dereferences `args.redirect_uri` before any defaulting. + */ +const callbackOnlySettings: UserManagerSettings = { + authority: '', + client_id: '', + redirect_uri: '', +}; + +new UserManager(callbackOnlySettings) + .signinSilentCallback() + .catch((error) => { + // Nothing is recoverable from inside the iframe: oidc-client-ts times the silent + // request out and raises a SilentRenewError on the UserManager that started it. + console.error('Silent refresh callback failed:', error); + }); diff --git a/ui/tsconfig.json b/ui/tsconfig.json index c53c0bf984..8c542e703b 100644 --- a/ui/tsconfig.json +++ b/ui/tsconfig.json @@ -13,5 +13,5 @@ // TypeScript 6.0 no longer auto-includes all of node_modules/@types "types": ["jest"] }, - "include": ["main.ts", "./custom.d.ts", "./modules/**/*", "__tests__/utils"] + "include": ["main.ts", "silent-callback.ts", "./custom.d.ts", "./modules/**/*", "__tests__/utils"] }