diff --git a/.changeset/prepare-specialized-app-signin-flow.md b/.changeset/prepare-specialized-app-signin-flow.md
index 31d5bb1b8d..746df0d4fe 100644
--- a/.changeset/prepare-specialized-app-signin-flow.md
+++ b/.changeset/prepare-specialized-app-signin-flow.md
@@ -3,4 +3,4 @@
'@backstage/frontend-defaults': patch
---
-Adds `prepareSpecializedApp` as a new two-phase app wiring API for rendering a sign-in page before full app finalization. Session preparation now resolves to an opaque reusable `sessionState`, which is returned from `getSignIn().ready` and from `finalize()`, and can be passed into a future `prepareSpecializedApp` call to skip sign-in and reuse the prepared session. The existing `createSpecializedApp` API is now deprecated and backed by `prepareSpecializedApp().finalize()`, while `createApp` has been updated to use the same prepare/finalize flow.
+Adds `prepareSpecializedApp` as a new two-phase app wiring API for rendering a sign-in page before full app finalization. The sign-in step is now exposed as a bootstrap component whose `onReady` callback signals that the prepared app can now be finalized, while the opaque reusable `sessionState` is stored internally on the prepared app and returned from `tryFinalize()` and `finalize()`. That session state can also be passed into a future `prepareSpecializedApp` call to skip sign-in and reuse the prepared session. The existing `createSpecializedApp` API is now deprecated and backed by `prepareSpecializedApp().finalize()`, while `createApp` has been updated to use the same prepare/finalize flow.
diff --git a/packages/frontend-app-api/src/wiring/index.ts b/packages/frontend-app-api/src/wiring/index.ts
index 56851ac88a..d4db40bfee 100644
--- a/packages/frontend-app-api/src/wiring/index.ts
+++ b/packages/frontend-app-api/src/wiring/index.ts
@@ -15,8 +15,11 @@
*/
export {
+ type FinalizedSpecializedApp,
prepareSpecializedApp,
type PreparedSpecializedApp,
+ type PreparedSpecializedAppSignInProps,
+ type SpecializedAppSessionState,
createSpecializedApp,
type CreateSpecializedAppOptions,
} from './createSpecializedApp';
diff --git a/packages/frontend-defaults/src/createApp.tsx b/packages/frontend-defaults/src/createApp.tsx
index 75d33282e7..38eadf6509 100644
--- a/packages/frontend-defaults/src/createApp.tsx
+++ b/packages/frontend-defaults/src/createApp.tsx
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { JSX, lazy, ReactNode, Suspense, useEffect, useState } from 'react';
+import { JSX, lazy, ReactNode, Suspense, useReducer, useState } from 'react';
import {
ConfigApi,
coreExtensionData,
@@ -30,6 +30,7 @@ import { overrideBaseUrlConfigs } from '../../core-app-api/src/app/overrideBaseU
import { ConfigReader } from '@backstage/config';
import {
CreateAppRouteBinder,
+ FinalizedSpecializedApp,
prepareSpecializedApp,
PreparedSpecializedApp,
FrontendPluginInfoResolver,
@@ -126,18 +127,9 @@ export function createApp(options?: CreateAppOptions): {
bindRoutes: options?.bindRoutes,
advanced: options?.advanced,
});
- const signIn = preparedApp.getSignIn();
-
- if (signIn.element) {
- return {
- default: () => ,
- };
- }
-
- const { sessionState } = await signIn.ready;
return {
- default: () => renderFinalizedApp(preparedApp.finalize(sessionState)),
+ default: () => ,
};
}
@@ -158,51 +150,34 @@ function PreparedAppRoot(props: {
preparedApp: PreparedSpecializedApp;
}): JSX.Element {
const signIn = props.preparedApp.getSignIn();
+ const SignIn = signIn.Component;
const [finalizeError, setFinalizeError] = useState();
- const [finalizedApp, setFinalizedApp] = useState<
- ReturnType | undefined
- >(undefined);
-
- useEffect(() => {
- let cancelled = false;
- const runFinalize = async () => {
- try {
- const { sessionState } = await signIn.ready;
- if (cancelled) {
- return;
- }
- setFinalizedApp(props.preparedApp.finalize(sessionState));
- } catch (error) {
- if (cancelled) {
- return;
- }
- setFinalizeError(error as Error);
- }
- };
- void runFinalize();
- return () => {
- cancelled = true;
- };
- }, [props.preparedApp, signIn]);
+ const [, triggerRerender] = useReducer((count: number) => count + 1, 0);
if (finalizeError) {
throw finalizeError;
}
+ const finalizedApp: FinalizedSpecializedApp | undefined =
+ props.preparedApp.tryFinalize();
+
if (!finalizedApp) {
- return signIn.element ?? <>>;
+ return (
+ {
+ triggerRerender();
+ }}
+ onError={setFinalizeError}
+ />
+ );
}
- return renderFinalizedApp(finalizedApp);
-}
-
-function renderFinalizedApp(
- app: ReturnType,
-) {
- const errorPage = maybeCreateErrorPage(app);
+ const errorPage = maybeCreateErrorPage(finalizedApp);
if (errorPage) {
return errorPage;
}
- return app.tree.root.instance!.getData(coreExtensionData.reactElement)!;
+ return finalizedApp.tree.root.instance!.getData(
+ coreExtensionData.reactElement,
+ )!;
}