diff --git a/.changeset/fifty-cameras-shake.md b/.changeset/fifty-cameras-shake.md
index 49515502a3..b87d4e92c2 100644
--- a/.changeset/fifty-cameras-shake.md
+++ b/.changeset/fifty-cameras-shake.md
@@ -2,4 +2,4 @@
'@backstage/plugin-auth-react': patch
---
-Update the default cookie base path and create a experimental redirect to root and protected app components, the components should be used only when the public entry is enabled.
+Update the default cookie base path and create a experimental redirect to root and app mode components, the components authenticate and keep a cookie refresh loop on the client side.
diff --git a/.changeset/nine-rabbits-exist.md b/.changeset/nine-rabbits-exist.md
index 9d14a9f417..a26f1f59c3 100644
--- a/.changeset/nine-rabbits-exist.md
+++ b/.changeset/nine-rabbits-exist.md
@@ -2,4 +2,4 @@
'@backstage/core-app-api': patch
---
-Clear the app auth cookie when the when the user sign out.
+Clear the app auth cookie after a sign out.
diff --git a/.changeset/old-parents-try.md b/.changeset/old-parents-try.md
deleted file mode 100644
index 866cdaee21..0000000000
--- a/.changeset/old-parents-try.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@backstage/backend-plugin-api': patch
-'@backstage/backend-test-utils': patch
-'@backstage/backend-common': patch
----
-
-Add a `removeUserCookie` to the http auth service interface.
diff --git a/docs/tutorials/enable-public-entry.md b/docs/tutorials/enable-public-entry.md
index a25d1a8704..79c2d33e68 100644
--- a/docs/tutorials/enable-public-entry.md
+++ b/docs/tutorials/enable-public-entry.md
@@ -1,6 +1,6 @@
---
id: enable-public-entry
-title: Enabling public entry point
+title: Enabling a public entry point
description: A guide for how to experiment with public and protected Backstage app bundles
---
@@ -21,11 +21,14 @@ With that, Backstage's cli and backend will detect public entry point and serve
- The tutorial will only work for those using backstage-cli to build and serve their Backstage app.
-## Trying out this feature
+## Step-by-step
-1. Add a `index-public-experimental.tsx` to your app `src` folder;
+1. Create a `index-public-experimental.tsx` in your app `src` folder;
+ :::note
+ The filename is a convention, so it is not currently configurable.
+ :::
-2. Prepare an unauthenticated version of your application. This will be the public entry point for your site:
+2. This file is the public entry point for your application, and it should only contain what unauthenticated users should see:
```tsx title="in packages/app/src/index-public-experimental.tsx"
import React from 'react';
@@ -42,7 +45,7 @@ With that, Backstage's cli and backend will detect public entry point and serve
createApiFactory,
discoveryApiRef,
} from '@backstage/core-plugin-api';
- import { RedirectToRoot } from '@backstage/plugin-auth-react';
+ import { CookieAuthRootRedirect } from '@backstage/plugin-auth-react';
import { providers } from '../src/identityProviders';
import { AuthProxyDiscoveryApi } from '../src/AuthProxyDiscoveryApi';
@@ -76,7 +79,7 @@ With that, Backstage's cli and backend will detect public entry point and serve
{/* This is a special component that does the magic to redirect users to access the home page of your authenticated application version */}
-
+
>,
);
@@ -84,35 +87,12 @@ With that, Backstage's cli and backend will detect public entry point and serve
ReactDOM.createRoot(document.getElementById('root')!).render();
```
-3) Then ensure your main entry is also protected by the experimental app provider, as shown in the following example:
+3. The frontend will handle cookie refreshing automatically, so you don't have to worry about it;
-```tsx title="in packages/app/src/index-public-experimental.tsx"
-// ...
-export default app.createRoot(
- <>
-
-
-
- {/* For now, this is just a temporary solution to ensure the user remains logged in properly and has access to the main app content. */}
-
-
- {routes}
-
-
- >,
-);
-```
+4. You're now ready to build and serve your frontend and backend as usual;
-4. You're now ready to build your front-end app:
+5. After that, access your backend index endpoint to see the public app being served (note that only a minimal app is being served).
-```sh
-yarn workspace example-app build
-```
+6. Log in and you will be redirected to the main app home page (check the protected bundle being served from the app-backend after the redirect).
-5. And also serve it from your Backstage app backend:
-
-```sh
-yarn start-backend:next
-```
-
-6. Finally, access http://localhost:7007 to see the public app being served (note that only a minimal app is being served). Log in and you will be redirected to the main app home page (check the protected bundle being served from the app-backend after the redirect).
+That's it!
diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx
index 2639f2add8..3d8bd45e5a 100644
--- a/packages/app/src/App.tsx
+++ b/packages/app/src/App.tsx
@@ -108,7 +108,6 @@ import { DevToolsPage } from '@backstage/plugin-devtools';
import { customDevToolsPage } from './components/devtools/CustomDevToolsPage';
import { CatalogUnprocessedEntitiesPage } from '@backstage/plugin-catalog-unprocessed-entities';
import { NotificationsPage } from '@backstage/plugin-notifications';
-import { ExperimentalAppProtection } from '@backstage/plugin-auth-react';
const app = createApp({
apis,
@@ -283,10 +282,8 @@ export default app.createRoot(
-
-
- {routes}
-
+
+ {routes}
>,
);
diff --git a/packages/app/src/index-public-experimental.tsx b/packages/app/src/index-public-experimental.tsx
index 59f89c1157..3de164fb64 100644
--- a/packages/app/src/index-public-experimental.tsx
+++ b/packages/app/src/index-public-experimental.tsx
@@ -21,7 +21,7 @@ import {
OAuthRequestDialog,
SignInPage,
} from '@backstage/core-components';
-import { RedirectToRoot } from '@backstage/plugin-auth-react';
+import { CookieAuthRootRedirect } from '@backstage/plugin-auth-react';
import React from 'react';
import ReactDOM from 'react-dom/client';
import { providers } from '../src/identityProviders';
@@ -59,7 +59,7 @@ const App = app.createRoot(
-
+
>,
);
diff --git a/packages/backend-app-api/src/services/implementations/httpAuth/httpAuthServiceFactory.ts b/packages/backend-app-api/src/services/implementations/httpAuth/httpAuthServiceFactory.ts
index 807e31513e..fe927eaa5d 100644
--- a/packages/backend-app-api/src/services/implementations/httpAuth/httpAuthServiceFactory.ts
+++ b/packages/backend-app-api/src/services/implementations/httpAuth/httpAuthServiceFactory.ts
@@ -181,6 +181,13 @@ class DefaultHttpAuthService implements HttpAuthService {
let credentials: BackstageCredentials;
if (options?.credentials) {
+ if (this.#auth.isPrincipal(options.credentials, 'none')) {
+ res.clearCookie(
+ BACKSTAGE_AUTH_COOKIE,
+ await this.#getCookieOptions(res.req),
+ );
+ return { expiresAt: new Date() };
+ }
if (!this.#auth.isPrincipal(options.credentials, 'user')) {
throw new AuthenticationError(
'Refused to issue cookie for non-user principal',
@@ -196,16 +203,6 @@ class DefaultHttpAuthService implements HttpAuthService {
return { expiresAt: existingExpiresAt };
}
- const originHeader = res.req.headers.origin;
- const origin =
- !originHeader || originHeader === 'null' ? undefined : originHeader;
-
- // https://backstage.example.com/api/catalog
- const externalBaseUrlStr = await this.#discovery.getExternalBaseUrl(
- this.#pluginId,
- );
- const externalBaseUrl = new URL(origin ?? externalBaseUrlStr);
-
const { token, expiresAt } = await this.#auth.getLimitedUserToken(
credentials,
);
@@ -213,20 +210,41 @@ class DefaultHttpAuthService implements HttpAuthService {
throw new Error('User credentials is unexpectedly missing token');
}
+ res.cookie(BACKSTAGE_AUTH_COOKIE, token, {
+ ...(await this.#getCookieOptions(res.req)),
+ expires: expiresAt,
+ });
+
+ return { expiresAt };
+ }
+
+ async #getCookieOptions(req: Request): Promise<{
+ domain: string;
+ httpOnly: true;
+ secure: boolean;
+ priority: 'high';
+ sameSite: 'none' | 'lax';
+ }> {
+ const originHeader = req.headers.origin;
+ const origin =
+ !originHeader || originHeader === 'null' ? undefined : originHeader;
+
+ const externalBaseUrlStr = await this.#discovery.getExternalBaseUrl(
+ this.#pluginId,
+ );
+ const externalBaseUrl = new URL(origin ?? externalBaseUrlStr);
+
const secure =
externalBaseUrl.protocol === 'https:' ||
externalBaseUrl.hostname === 'localhost';
- res.cookie(BACKSTAGE_AUTH_COOKIE, token, {
+ return {
domain: externalBaseUrl.hostname,
httpOnly: true,
- expires: expiresAt,
secure,
priority: 'high',
sameSite: secure ? 'none' : 'lax',
- });
-
- return { expiresAt };
+ };
}
async #existingCookieExpiration(req: Request): Promise {
@@ -254,10 +272,6 @@ class DefaultHttpAuthService implements HttpAuthService {
throw error;
}
}
-
- removeUserCookie(res: Response): void {
- res.clearCookie(BACKSTAGE_AUTH_COOKIE);
- }
}
/** @public */
diff --git a/packages/backend-app-api/src/services/implementations/httpRouter/httpRouterServiceFactory.ts b/packages/backend-app-api/src/services/implementations/httpRouter/httpRouterServiceFactory.ts
index 5f32eb8d00..fe8cd0298b 100644
--- a/packages/backend-app-api/src/services/implementations/httpRouter/httpRouterServiceFactory.ts
+++ b/packages/backend-app-api/src/services/implementations/httpRouter/httpRouterServiceFactory.ts
@@ -93,7 +93,7 @@ export const httpRouterServiceFactory = createServiceFactory(
) {
// Only add the cookie refresh middleware once
hasRegistedCookieAuthRefreshMiddleware = true;
- router.use(createCookieAuthRefreshMiddleware({ httpAuth }));
+ router.use(createCookieAuthRefreshMiddleware({ auth, httpAuth }));
}
},
};
diff --git a/packages/backend-common/src/auth/createLegacyAuthAdapters.ts b/packages/backend-common/src/auth/createLegacyAuthAdapters.ts
index 7718f74ee8..0ba23661ef 100644
--- a/packages/backend-common/src/auth/createLegacyAuthAdapters.ts
+++ b/packages/backend-common/src/auth/createLegacyAuthAdapters.ts
@@ -259,10 +259,6 @@ class HttpAuthCompat implements HttpAuthService {
async issueUserCookie(_res: Response): Promise<{ expiresAt: Date }> {
return { expiresAt: new Date(Date.now() + 3600_000) };
}
-
- removeUserCookie(res: Response): void {
- res.clearCookie('backstage-auth');
- }
}
export class UserInfoCompat implements UserInfoService {
diff --git a/packages/backend-plugin-api/api-report.md b/packages/backend-plugin-api/api-report.md
index d91f4a2450..3d20b789c1 100644
--- a/packages/backend-plugin-api/api-report.md
+++ b/packages/backend-plugin-api/api-report.md
@@ -325,13 +325,11 @@ export interface HttpAuthService {
issueUserCookie(
res: Response_2,
options?: {
- credentials?: BackstageCredentials;
+ credentials?: BackstageCredentials;
},
): Promise<{
expiresAt: Date;
}>;
- // (undocumented)
- removeUserCookie(res: Response_2): void;
}
// @public (undocumented)
diff --git a/packages/backend-plugin-api/src/services/definitions/HttpAuthService.ts b/packages/backend-plugin-api/src/services/definitions/HttpAuthService.ts
index eb6ba944f5..5d1a2090d4 100644
--- a/packages/backend-plugin-api/src/services/definitions/HttpAuthService.ts
+++ b/packages/backend-plugin-api/src/services/definitions/HttpAuthService.ts
@@ -15,11 +15,7 @@
*/
import { Request, Response } from 'express';
-import {
- BackstageCredentials,
- BackstagePrincipalTypes,
- BackstageUserPrincipal,
-} from './AuthService';
+import { BackstageCredentials, BackstagePrincipalTypes } from './AuthService';
/** @public */
export interface HttpAuthService {
@@ -34,9 +30,7 @@ export interface HttpAuthService {
issueUserCookie(
res: Response,
options?: {
- credentials?: BackstageCredentials;
+ credentials?: BackstageCredentials;
},
): Promise<{ expiresAt: Date }>;
-
- removeUserCookie(res: Response): void;
}
diff --git a/packages/backend-test-utils/src/next/services/MockHttpAuthService.ts b/packages/backend-test-utils/src/next/services/MockHttpAuthService.ts
index 8934def5f0..9a69620473 100644
--- a/packages/backend-test-utils/src/next/services/MockHttpAuthService.ts
+++ b/packages/backend-test-utils/src/next/services/MockHttpAuthService.ts
@@ -140,8 +140,4 @@ export class MockHttpAuthService implements HttpAuthService {
return { expiresAt: new Date(Date.now() + 3600_000) };
}
-
- removeUserCookie(res: Response): void {
- res.clearCookie(MOCK_AUTH_COOKIE);
- }
}
diff --git a/packages/backend-test-utils/src/next/services/mockServices.ts b/packages/backend-test-utils/src/next/services/mockServices.ts
index 00f1aa901a..a858bf7dbf 100644
--- a/packages/backend-test-utils/src/next/services/mockServices.ts
+++ b/packages/backend-test-utils/src/next/services/mockServices.ts
@@ -275,7 +275,6 @@ export namespace mockServices {
export const mock = simpleMock(coreServices.httpAuth, () => ({
credentials: jest.fn(),
issueUserCookie: jest.fn(),
- removeUserCookie: jest.fn(),
}));
}
diff --git a/packages/cli/src/lib/bundler/bundle.ts b/packages/cli/src/lib/bundler/bundle.ts
index 06249774fd..b99891d70d 100644
--- a/packages/cli/src/lib/bundler/bundle.ts
+++ b/packages/cli/src/lib/bundler/bundle.ts
@@ -70,11 +70,7 @@ export async function buildBundle(options: BuildOptions) {
`⚠️ WARNING: The app /public entry point is an experimental feature that may receive immediate breaking changes.`,
),
);
- configs.push(
- await createConfig(publicPaths, {
- ...commonConfigOptions,
- }),
- );
+ configs.push(await createConfig(publicPaths, commonConfigOptions));
}
const isCi = yn(process.env.CI, { default: false });
diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts
index 2fe72cd471..2b7311a34c 100644
--- a/packages/cli/src/lib/bundler/server.ts
+++ b/packages/cli/src/lib/bundler/server.ts
@@ -216,12 +216,7 @@ DEPRECATION WARNING: React Router Beta is deprecated and support for it will be
);
}
const compiler = publicPaths
- ? webpack([
- config,
- await createConfig(publicPaths, {
- ...commonConfigOptions,
- }),
- ])
+ ? webpack([config, await createConfig(publicPaths, commonConfigOptions)])
: webpack(config);
webpackServer = new WebpackDevServer(
diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json
index bb3566062c..dcdb5a78f1 100644
--- a/packages/core-app-api/package.json
+++ b/packages/core-app-api/package.json
@@ -44,6 +44,7 @@
"dependencies": {
"@backstage/config": "workspace:^",
"@backstage/core-plugin-api": "workspace:^",
+ "@backstage/plugin-auth-react": "workspace:^",
"@backstage/types": "workspace:^",
"@backstage/version-bridge": "workspace:^",
"@types/prop-types": "^15.7.3",
diff --git a/packages/core-app-api/src/app/AppManager.test.tsx b/packages/core-app-api/src/app/AppManager.test.tsx
index 9f81d0b62e..9300e81567 100644
--- a/packages/core-app-api/src/app/AppManager.test.tsx
+++ b/packages/core-app-api/src/app/AppManager.test.tsx
@@ -864,7 +864,7 @@ describe('Integration Test', () => {
await waitFor(() =>
expect(fetchApiMock.fetch).toHaveBeenCalledWith(
- 'http://localhost:7007/app/.backstage/v1-cookie',
+ 'http://localhost:7007/app/.backstage/auth/v1/cookie',
{ method: 'DELETE' },
),
);
diff --git a/packages/core-app-api/src/app/AppRouter.tsx b/packages/core-app-api/src/app/AppRouter.tsx
index 7481f6662a..c4bedea4a7 100644
--- a/packages/core-app-api/src/app/AppRouter.tsx
+++ b/packages/core-app-api/src/app/AppRouter.tsx
@@ -29,6 +29,7 @@ import { isReactRouterBeta } from './isReactRouterBeta';
import { RouteTracker } from '../routing/RouteTracker';
import { Route, Routes } from 'react-router-dom';
import { AppIdentityProxy } from '../apis/implementations/IdentityApi/AppIdentityProxy';
+import { AppMode } from '@backstage/plugin-auth-react';
/**
* Get the app base path from the configured app baseUrl.
@@ -145,7 +146,10 @@ export function AppRouter(props: AppRouterProps) {
- {props.children}>} />
+ {props.children}}
+ />
);
@@ -154,7 +158,7 @@ export function AppRouter(props: AppRouterProps) {
return (
- {props.children}
+ {props.children}
);
}
@@ -168,7 +172,10 @@ export function AppRouter(props: AppRouterProps) {
appIdentityProxy={appIdentityProxy}
>
- {props.children}>} />
+ {props.children}}
+ />
@@ -182,7 +189,7 @@ export function AppRouter(props: AppRouterProps) {
component={SignInPageComponent}
appIdentityProxy={appIdentityProxy}
>
- {props.children}
+ {props.children}
);
diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json
index fb5e982058..097f5a3611 100644
--- a/packages/frontend-app-api/package.json
+++ b/packages/frontend-app-api/package.json
@@ -38,6 +38,7 @@
"@backstage/core-plugin-api": "workspace:^",
"@backstage/errors": "workspace:^",
"@backstage/frontend-plugin-api": "workspace:^",
+ "@backstage/plugin-auth-react": "workspace:^",
"@backstage/theme": "workspace:^",
"@backstage/types": "workspace:^",
"@backstage/version-bridge": "workspace:^",
diff --git a/packages/frontend-app-api/src/extensions/AppRoot.tsx b/packages/frontend-app-api/src/extensions/AppRoot.tsx
index f3a962dd10..a25953ed49 100644
--- a/packages/frontend-app-api/src/extensions/AppRoot.tsx
+++ b/packages/frontend-app-api/src/extensions/AppRoot.tsx
@@ -42,6 +42,7 @@ import { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations
import { BrowserRouter } from 'react-router-dom';
import { RouteTracker } from '../routing/RouteTracker';
import { getBasePath } from '../routing/getBasePath';
+import { AppMode } from '@backstage/plugin-auth-react';
export const AppRoot = createExtension({
namespace: 'app',
@@ -190,7 +191,7 @@ export function AppRouter(props: AppRouterProps) {
return (
- {children}
+ {children}
);
}
@@ -202,7 +203,7 @@ export function AppRouter(props: AppRouterProps) {
component={SignInPageComponent}
appIdentityProxy={appIdentityProxy}
>
- {children}
+ {children}
);
diff --git a/plugins/app-backend/src/service/appPlugin.test.ts b/plugins/app-backend/src/service/appPlugin.test.ts
index 647697c204..1507e43b46 100644
--- a/plugins/app-backend/src/service/appPlugin.test.ts
+++ b/plugins/app-backend/src/service/appPlugin.test.ts
@@ -62,9 +62,9 @@ describe('appPlugin', () => {
fetch(`http://localhost:${server.port()}/api/app/derp.html`).then(res =>
res.text(),
),
- ).resolves.toBe('winning');
+ ).resolves.toMatch('winning');
await expect(
fetch(`http://localhost:${server.port()}`).then(res => res.text()),
- ).resolves.toBe('winning');
+ ).resolves.toMatch('winning');
});
});
diff --git a/plugins/app-backend/src/service/router.ts b/plugins/app-backend/src/service/router.ts
index 70466da001..531fad8179 100644
--- a/plugins/app-backend/src/service/router.ts
+++ b/plugins/app-backend/src/service/router.ts
@@ -168,12 +168,13 @@ export async function createRouter(
const publicDistDir = resolvePath(appDistDir, 'public');
- const enablePublicEntryPoint = await fs.pathExists(publicDistDir);
+ const enablePublicEntryPoint =
+ (await fs.pathExists(publicDistDir)) && auth && httpAuth;
if (enablePublicEntryPoint && auth && httpAuth) {
const publicRouter = Router();
- publicRouter.use(createCookieAuthRefreshMiddleware({ httpAuth }));
+ publicRouter.use(createCookieAuthRefreshMiddleware({ auth, httpAuth }));
publicRouter.use(async (req, _res, next) => {
const credentials = await httpAuth.credentials(req, {
@@ -214,6 +215,7 @@ export async function createRouter(
publicRouter.use(
await createEntryPointRouter({
+ appMode: 'public',
logger: logger.child({ entry: 'public' }),
rootDir: publicDistDir,
assetStore: assetStore?.withNamespace('public'),
@@ -226,6 +228,7 @@ export async function createRouter(
router.use(
await createEntryPointRouter({
+ appMode: enablePublicEntryPoint ? 'protected' : 'public',
logger: logger.child({ entry: 'main' }),
rootDir: appDistDir,
assetStore,
@@ -243,6 +246,7 @@ async function createEntryPointRouter({
rootDir,
assetStore,
staticFallbackHandler,
+ appMode,
appConfigs,
injectedConfigPath,
}: {
@@ -250,6 +254,7 @@ async function createEntryPointRouter({
rootDir: string;
assetStore?: StaticAssetsStore;
staticFallbackHandler?: express.Handler;
+ appMode: 'public' | 'protected';
appConfigs?: AppConfig[];
injectedConfigPath?: string;
}) {
@@ -303,14 +308,21 @@ async function createEntryPointRouter({
},
}),
);
+
+ const indexHtmlContent = Buffer.from(
+ (await fs.readFile(resolvePath(rootDir, 'index.html'), 'utf8')).replace(
+ //,
+ ``,
+ ),
+ 'utf8',
+ );
+
router.get('/*', (_req, res) => {
- res.sendFile(resolvePath(rootDir, 'index.html'), {
- headers: {
- // The Cache-Control header instructs the browser to not cache the index.html since it might
- // link to static assets from recently deployed versions.
- 'cache-control': CACHE_CONTROL_NO_CACHE,
- },
- });
+ // The Cache-Control header instructs the browser to not cache the index.html since it might
+ // link to static assets from recently deployed versions.
+ res.setHeader('Cache-Control', CACHE_CONTROL_NO_CACHE);
+ res.setHeader('Content-Type', 'text/html;charset=utf-8');
+ res.send(indexHtmlContent);
});
return router;
diff --git a/plugins/auth-node/api-report.md b/plugins/auth-node/api-report.md
index 4cb9db70f3..f0696ecd9f 100644
--- a/plugins/auth-node/api-report.md
+++ b/plugins/auth-node/api-report.md
@@ -3,6 +3,7 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
+import { AuthService } from '@backstage/backend-plugin-api';
import { BackstageIdentityResponse as BackstageIdentityResponse_2 } from '@backstage/plugin-auth-node';
import { BackstageSignInResult as BackstageSignInResult_2 } from '@backstage/plugin-auth-node';
import { Config } from '@backstage/config';
@@ -152,6 +153,7 @@ export type CookieConfigurer = (ctx: {
// @public
export function createCookieAuthRefreshMiddleware(options: {
+ auth: AuthService;
httpAuth: HttpAuthService;
}): Router;
diff --git a/plugins/auth-node/src/identity/createCookieAuthRefreshMiddleware.test.ts b/plugins/auth-node/src/identity/createCookieAuthRefreshMiddleware.test.ts
index 65426b4ca5..aa5f1a995a 100644
--- a/plugins/auth-node/src/identity/createCookieAuthRefreshMiddleware.test.ts
+++ b/plugins/auth-node/src/identity/createCookieAuthRefreshMiddleware.test.ts
@@ -23,8 +23,9 @@ describe('createCookieAuthRefreshMiddleware', () => {
let app: express.Express;
beforeAll(async () => {
+ const auth = mockServices.auth();
const httpAuth = mockServices.httpAuth();
- const router = createCookieAuthRefreshMiddleware({ httpAuth });
+ const router = createCookieAuthRefreshMiddleware({ auth, httpAuth });
app = express().use(router);
});
@@ -33,7 +34,7 @@ describe('createCookieAuthRefreshMiddleware', () => {
});
it('should issue the user cookie', async () => {
- const response = await request(app).get('/.backstage/v1-cookie');
+ const response = await request(app).get('/.backstage/auth/v1/cookie');
expect(response.status).toBe(200);
expect(response.header['set-cookie'][0]).toMatch(
`backstage-auth=${mockCredentials.limitedUser.token()}`,
@@ -41,7 +42,7 @@ describe('createCookieAuthRefreshMiddleware', () => {
});
it('should remove the user cookie', async () => {
- const response = await request(app).delete('/.backstage/v1-cookie');
+ const response = await request(app).delete('/.backstage/auth/v1/cookie');
expect(response.status).toBe(200);
expect(response.header['set-cookie'][0]).toMatch('backstage-auth=');
});
diff --git a/plugins/auth-node/src/identity/createCookieAuthRefreshMiddleware.ts b/plugins/auth-node/src/identity/createCookieAuthRefreshMiddleware.ts
index efb58345a1..939fa9197d 100644
--- a/plugins/auth-node/src/identity/createCookieAuthRefreshMiddleware.ts
+++ b/plugins/auth-node/src/identity/createCookieAuthRefreshMiddleware.ts
@@ -14,19 +14,20 @@
* limitations under the License.
*/
-import { HttpAuthService } from '@backstage/backend-plugin-api';
+import { AuthService, HttpAuthService } from '@backstage/backend-plugin-api';
import { Router } from 'express';
-const WELL_KNOWN_COOKIE_PATH_V1 = '/.backstage/v1-cookie';
+const WELL_KNOWN_COOKIE_PATH_V1 = '/.backstage/auth/v1/cookie';
/**
* @public
* Creates a middleware that can be used to refresh the cookie for the user.
*/
export function createCookieAuthRefreshMiddleware(options: {
+ auth: AuthService;
httpAuth: HttpAuthService;
}) {
- const { httpAuth } = options;
+ const { auth, httpAuth } = options;
const router = Router();
// Endpoint that sets the cookie for the user
@@ -37,7 +38,8 @@ export function createCookieAuthRefreshMiddleware(options: {
// Endpoint that removes the cookie for the user
router.delete(WELL_KNOWN_COOKIE_PATH_V1, async (_, res) => {
- httpAuth.removeUserCookie(res);
+ const credentials = await auth.getNoneCredentials();
+ await httpAuth.issueUserCookie(res, { credentials });
res.send(200);
});
diff --git a/plugins/auth-react/api-report.md b/plugins/auth-react/api-report.md
index d6b7d2e17f..ef8ae58267 100644
--- a/plugins/auth-react/api-report.md
+++ b/plugins/auth-react/api-report.md
@@ -6,6 +6,9 @@
import { default as React_2 } from 'react';
import { ReactNode } from 'react';
+// @public
+export function AppMode(props: { children: ReactNode }): JSX.Element;
+
// @public
export function CookieAuthRefreshProvider(
props: CookieAuthRefreshProviderProps,
@@ -19,12 +22,7 @@ export type CookieAuthRefreshProviderProps = {
};
// @public
-export function ExperimentalAppProtection(props: {
- children: ReactNode;
-}): JSX.Element;
-
-// @public
-export function RedirectToRoot(): React_2.JSX.Element | null;
+export function CookieAuthRootRedirect(): React_2.JSX.Element | null;
// @public
export function useCookieAuthRefresh(options: {
diff --git a/plugins/auth-react/package.json b/plugins/auth-react/package.json
index 9de3aade21..e5eb7db192 100644
--- a/plugins/auth-react/package.json
+++ b/plugins/auth-react/package.json
@@ -35,6 +35,8 @@
"@backstage/core-components": "workspace:^",
"@backstage/core-plugin-api": "workspace:^",
"@backstage/errors": "workspace:^",
+ "@backstage/frontend-plugin-api": "workspace:^",
+ "@backstage/version-bridge": "workspace:^",
"@material-ui/core": "^4.9.13",
"@react-hookz/web": "^24.0.0",
"@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0"
diff --git a/plugins/auth-react/src/components/AppMode/AppMode.test.tsx b/plugins/auth-react/src/components/AppMode/AppMode.test.tsx
new file mode 100644
index 0000000000..71f6ce9434
--- /dev/null
+++ b/plugins/auth-react/src/components/AppMode/AppMode.test.tsx
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2024 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import { render, screen, waitFor } from '@testing-library/react';
+import { AppMode } from './AppMode';
+import { discoveryApiRef, fetchApiRef } from '@backstage/core-plugin-api';
+import { componentsApiRef } from '@backstage/frontend-plugin-api';
+
+const now = 1710316886171;
+const tenMinutesInMilliseconds = 10 * 60 * 1000;
+const tenMinutesFromNowInMilliseconds = now + tenMinutesInMilliseconds;
+const expiresAt = new Date(tenMinutesFromNowInMilliseconds).toISOString();
+
+jest.mock('@backstage/core-plugin-api', () => {
+ return {
+ ...jest.requireActual('@backstage/core-plugin-api'),
+ useApp: jest.fn().mockReturnValue({
+ getComponents: () => ({ Progress: () => }),
+ }),
+ useApi: jest.fn(ref => {
+ if (ref === discoveryApiRef) {
+ return {
+ getBaseUrl: jest.fn().mockResolvedValue('http://localhost:7000/app'),
+ };
+ }
+
+ if (ref === fetchApiRef) {
+ return {
+ fetch: jest.fn().mockResolvedValue({
+ ok: true,
+ json: jest.fn().mockResolvedValue({ expiresAt }),
+ }),
+ };
+ }
+
+ if (ref === componentsApiRef) {
+ return {
+ getComponent: jest
+ .fn()
+ .mockReturnValue(() => ),
+ };
+ }
+
+ throw new Error(`Attempted to use an unmocked API reference: ${ref}`);
+ }),
+ };
+});
+
+describe('AppMode', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ jest.useFakeTimers({ now });
+ });
+
+ afterEach(() => {
+ jest.useRealTimers();
+ });
+
+ it('should render the children when app mode is undefined', async () => {
+ render(Test content);
+ await waitFor(() =>
+ expect(screen.getByText('Test content')).toBeInTheDocument(),
+ );
+ });
+
+ it('should render the children the app mode is public', async () => {
+ render(
+ <>
+
+ Test content
+ >,
+ );
+ await waitFor(() =>
+ expect(screen.getByText('Test content')).toBeInTheDocument(),
+ );
+ });
+
+ it('should render the children wrapped in the CookieAuthRefreshProvider', async () => {
+ render(
+ <>
+
+ Test content
+ >,
+ );
+ await waitFor(() =>
+ expect(screen.getByText('Test content')).toBeInTheDocument(),
+ );
+ });
+});
diff --git a/plugins/auth-react/src/components/AppMode/AppMode.tsx b/plugins/auth-react/src/components/AppMode/AppMode.tsx
new file mode 100644
index 0000000000..af99bcb23b
--- /dev/null
+++ b/plugins/auth-react/src/components/AppMode/AppMode.tsx
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2024 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React, { ReactNode, useEffect, useState } from 'react';
+import { CookieAuthRefreshProvider } from '@backstage/plugin-auth-react';
+import { CompatAppProgress } from '../CompatAppProgress';
+
+/**
+ * @public
+ * A provider that will protect the app when running in protected experimental mode.
+ */
+export function AppMode(props: { children: ReactNode }): JSX.Element {
+ const { children } = props;
+
+ const [appMode, setAppMode] = useState(null);
+
+ useEffect(() => {
+ const element = document.querySelector('meta[name="backstage-app-mode"]');
+ setAppMode(element?.getAttribute('content') ?? 'public');
+ }, [setAppMode]);
+
+ if (!appMode) {
+ return ;
+ }
+
+ if (appMode === 'protected') {
+ return (
+
+ {children}
+
+ );
+ }
+
+ return <>{children}>;
+}
diff --git a/plugins/auth-react/src/components/RedirectToRoot/index.ts b/plugins/auth-react/src/components/AppMode/index.ts
similarity index 92%
rename from plugins/auth-react/src/components/RedirectToRoot/index.ts
rename to plugins/auth-react/src/components/AppMode/index.ts
index 1e9e211adc..11d64b282a 100644
--- a/plugins/auth-react/src/components/RedirectToRoot/index.ts
+++ b/plugins/auth-react/src/components/AppMode/index.ts
@@ -14,4 +14,4 @@
* limitations under the License.
*/
-export { RedirectToRoot } from './RedirectToRoot';
+export { AppMode } from './AppMode';
diff --git a/plugins/auth-react/src/components/CompatAppProgress/CompatAppProgress.tsx b/plugins/auth-react/src/components/CompatAppProgress/CompatAppProgress.tsx
new file mode 100644
index 0000000000..e624d46304
--- /dev/null
+++ b/plugins/auth-react/src/components/CompatAppProgress/CompatAppProgress.tsx
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2024 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import { useApp } from '@backstage/core-plugin-api';
+import { useVersionedContext } from '@backstage/version-bridge';
+
+import {
+ coreComponentRefs,
+ useComponentRef,
+} from '@backstage/frontend-plugin-api';
+
+function LegacyAppProgress() {
+ const app = useApp();
+ const { Progress } = app.getComponents();
+ return ;
+}
+
+function NewAppProgress() {
+ const Progress = useComponentRef(coreComponentRefs.progress);
+ return ;
+}
+
+export function CompatAppProgress() {
+ const isInNewApp = !useVersionedContext<{ 1: unknown }>('app-context');
+ return isInNewApp ? : ;
+}
diff --git a/plugins/auth-react/src/components/ExperimentalAppProtection/index.ts b/plugins/auth-react/src/components/CompatAppProgress/index.ts
similarity index 89%
rename from plugins/auth-react/src/components/ExperimentalAppProtection/index.ts
rename to plugins/auth-react/src/components/CompatAppProgress/index.ts
index c29b1e438d..22fe51dd85 100644
--- a/plugins/auth-react/src/components/ExperimentalAppProtection/index.ts
+++ b/plugins/auth-react/src/components/CompatAppProgress/index.ts
@@ -14,4 +14,4 @@
* limitations under the License.
*/
-export { ExperimentalAppProtection } from './ExperimentalAppProtection';
+export { CompatAppProgress } from './CompatAppProgress';
diff --git a/plugins/auth-react/src/components/CookieAuthRefreshProvider/CookieAuthRefreshProvider.test.tsx b/plugins/auth-react/src/components/CookieAuthRefreshProvider/CookieAuthRefreshProvider.test.tsx
index ab88361773..77710dc7a4 100644
--- a/plugins/auth-react/src/components/CookieAuthRefreshProvider/CookieAuthRefreshProvider.test.tsx
+++ b/plugins/auth-react/src/components/CookieAuthRefreshProvider/CookieAuthRefreshProvider.test.tsx
@@ -119,7 +119,7 @@ describe('CookieAuthRefreshProvider', () => {
await waitFor(() =>
expect(fetchApiMock.fetch).toHaveBeenCalledWith(
- 'http://localhost:7000/techdocs/api/.backstage/v1-cookie',
+ 'http://localhost:7000/techdocs/api/.backstage/auth/v1/cookie',
{ credentials: 'include' },
),
);
diff --git a/plugins/auth-react/src/components/CookieAuthRefreshProvider/CookieAuthRefreshProvider.tsx b/plugins/auth-react/src/components/CookieAuthRefreshProvider/CookieAuthRefreshProvider.tsx
index 7919207692..18dd3c88a0 100644
--- a/plugins/auth-react/src/components/CookieAuthRefreshProvider/CookieAuthRefreshProvider.tsx
+++ b/plugins/auth-react/src/components/CookieAuthRefreshProvider/CookieAuthRefreshProvider.tsx
@@ -16,9 +16,9 @@
import React, { ReactNode } from 'react';
import { ErrorPanel } from '@backstage/core-components';
-import { useApp } from '@backstage/core-plugin-api';
import { Button } from '@material-ui/core';
import { useCookieAuthRefresh } from '../../hooks';
+import { CompatAppProgress } from '../CompatAppProgress/CompatAppProgress';
/**
* @public
@@ -41,13 +41,11 @@ export function CookieAuthRefreshProvider(
props: CookieAuthRefreshProviderProps,
): JSX.Element {
const { children, ...options } = props;
- const app = useApp();
- const { Progress } = app.getComponents();
const result = useCookieAuthRefresh(options);
if (result.status === 'loading') {
- return ;
+ return ;
}
if (result.status === 'error') {
diff --git a/plugins/auth-react/src/components/RedirectToRoot/RedirectToRoot.test.tsx b/plugins/auth-react/src/components/CookieAuthRootRedirect/CookieAuthRootRedirect.test.tsx
similarity index 91%
rename from plugins/auth-react/src/components/RedirectToRoot/RedirectToRoot.test.tsx
rename to plugins/auth-react/src/components/CookieAuthRootRedirect/CookieAuthRootRedirect.test.tsx
index d1171a4f81..109c65d4ed 100644
--- a/plugins/auth-react/src/components/RedirectToRoot/RedirectToRoot.test.tsx
+++ b/plugins/auth-react/src/components/CookieAuthRootRedirect/CookieAuthRootRedirect.test.tsx
@@ -18,9 +18,9 @@ import React from 'react';
import { screen, waitFor } from '@testing-library/react';
import { TestApiProvider, renderInTestApp } from '@backstage/test-utils';
import { identityApiRef } from '@backstage/core-plugin-api';
-import { RedirectToRoot } from './RedirectToRoot';
+import { CookieAuthRootRedirect } from './CookieAuthRootRedirect';
-describe('RedirectToRoot', () => {
+describe('CookieAuthRootRedirect', () => {
const identityApiMock = { getCredentials: jest.fn() };
beforeEach(() => {
@@ -32,7 +32,7 @@ describe('RedirectToRoot', () => {
await renderInTestApp(
-
+
,
);
@@ -50,7 +50,7 @@ describe('RedirectToRoot', () => {
await renderInTestApp(
-
+
,
);
diff --git a/plugins/auth-react/src/components/RedirectToRoot/RedirectToRoot.tsx b/plugins/auth-react/src/components/CookieAuthRootRedirect/CookieAuthRootRedirect.tsx
similarity index 97%
rename from plugins/auth-react/src/components/RedirectToRoot/RedirectToRoot.tsx
rename to plugins/auth-react/src/components/CookieAuthRootRedirect/CookieAuthRootRedirect.tsx
index 86b40b44e1..15a0f7a801 100644
--- a/plugins/auth-react/src/components/RedirectToRoot/RedirectToRoot.tsx
+++ b/plugins/auth-react/src/components/CookieAuthRootRedirect/CookieAuthRootRedirect.tsx
@@ -22,7 +22,7 @@ import { useAsync, useMountEffect } from '@react-hookz/web';
* @public
* A component that redirects to the root of the app after a successful sign-in.
*/
-export function RedirectToRoot() {
+export function CookieAuthRootRedirect() {
const identityApi = useApi(identityApiRef);
const [state, actions] = useAsync(async () => {
diff --git a/plugins/auth-react/src/components/CookieAuthRootRedirect/index.ts b/plugins/auth-react/src/components/CookieAuthRootRedirect/index.ts
new file mode 100644
index 0000000000..3e0bc26084
--- /dev/null
+++ b/plugins/auth-react/src/components/CookieAuthRootRedirect/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export { CookieAuthRootRedirect } from './CookieAuthRootRedirect';
diff --git a/plugins/auth-react/src/components/ExperimentalAppProtection/ExperimentalAppProtection.test.tsx b/plugins/auth-react/src/components/ExperimentalAppProtection/ExperimentalAppProtection.test.tsx
deleted file mode 100644
index 2884400ea9..0000000000
--- a/plugins/auth-react/src/components/ExperimentalAppProtection/ExperimentalAppProtection.test.tsx
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright 2024 The Backstage Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import React from 'react';
-import { screen, waitFor } from '@testing-library/react';
-import { rest } from 'msw';
-import { setupServer } from 'msw/node';
-import {
- MockConfigApi,
- TestApiProvider,
- renderInTestApp,
- setupRequestMockHandlers,
-} from '@backstage/test-utils';
-import { ExperimentalAppProtection } from './ExperimentalAppProtection';
-import {
- configApiRef,
- discoveryApiRef,
- fetchApiRef,
-} from '@backstage/core-plugin-api';
-
-describe('ExperimentalAppProtection', () => {
- const worker = setupServer();
- setupRequestMockHandlers(worker);
-
- const configApiMock = new MockConfigApi({
- backend: {
- baseUrl: 'http://localhost:7000',
- },
- });
-
- const fetchApiMock = {
- fetch: jest.fn(),
- };
-
- const discoveryApiMock = {
- getBaseUrl: jest.fn().mockResolvedValue('http://localhost:7000/app'),
- };
-
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
- it('should render the progress component while loading', async () => {
- fetchApiMock.fetch.mockReturnValueOnce(new Promise(() => {}));
- await renderInTestApp(
-
- Test content
- ,
- );
- expect(screen.getByTestId('progress')).toBeVisible();
- expect(screen.queryByText('Test Content')).not.toBeInTheDocument();
- });
-
- it('should render the children even when there is an error', async () => {
- const error = new Error('Failed to fetch');
- fetchApiMock.fetch.mockRejectedValueOnce(error);
- await renderInTestApp(
-
- Test content
- ,
- );
- await waitFor(() =>
- expect(screen.getByText('Test content')).toBeInTheDocument(),
- );
- expect(screen.queryByTestId('progress')).not.toBeInTheDocument();
- expect(screen.queryByText('Failed to fetch')).not.toBeInTheDocument();
- });
-
- it('should render the children even when the public index is not available', async () => {
- fetchApiMock.fetch.mockResolvedValueOnce({ ok: false });
- await renderInTestApp(
-
- Test content
- ,
- );
- await waitFor(() =>
- expect(screen.getByText('Test content')).toBeInTheDocument(),
- );
- expect(screen.queryByTestId('progress')).not.toBeInTheDocument();
- });
-
- it('should render the children also when the public index is available', async () => {
- fetchApiMock.fetch.mockResolvedValueOnce({ ok: true });
- await renderInTestApp(
- Test content,
- );
- await waitFor(() =>
- expect(screen.getByText('Test content')).toBeInTheDocument(),
- );
- expect(screen.queryByTestId('progress')).not.toBeInTheDocument();
- });
-
- it('should render the children wrapped in the CookieAuthRefreshProvider', async () => {
- worker.use(
- rest.get('http://localhost:7000/public/index.html', (_, res, ctx) => {
- return res(ctx.status(200));
- }),
- rest.get(
- 'http://localhost:7000/app/.backstage/v1-cookie',
- (_, res, ctx) => {
- return res(
- ctx.status(200),
- ctx.json({ expiresAt: Date.now() + 10 * 60 * 1000 }),
- );
- },
- ),
- );
- await renderInTestApp(
-
- Test content
- ,
- );
- await waitFor(() =>
- expect(screen.getByText('Test content')).toBeInTheDocument(),
- );
- });
-});
diff --git a/plugins/auth-react/src/components/ExperimentalAppProtection/ExperimentalAppProtection.tsx b/plugins/auth-react/src/components/ExperimentalAppProtection/ExperimentalAppProtection.tsx
deleted file mode 100644
index f76b287d97..0000000000
--- a/plugins/auth-react/src/components/ExperimentalAppProtection/ExperimentalAppProtection.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2024 The Backstage Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import React, { ReactNode } from 'react';
-import {
- configApiRef,
- fetchApiRef,
- useApi,
- useApp,
-} from '@backstage/core-plugin-api';
-import { CookieAuthRefreshProvider } from '@backstage/plugin-auth-react';
-import { useAsync, useMountEffect } from '@react-hookz/web';
-
-/**
- * @public
- * A provider that will protect the app when running in public experimental mode.
- */
-export function ExperimentalAppProtection(props: {
- children: ReactNode;
-}): JSX.Element {
- const { children } = props;
- const fetchApi = useApi(fetchApiRef);
- const configApi = useApi(configApiRef);
- const Components = useApp().getComponents();
-
- const [state, actions] = useAsync(async () => {
- const baseUrl = configApi.getString('backend.baseUrl');
- const response = await fetchApi.fetch(`${baseUrl}/public/index.html`);
- return response.ok;
- });
-
- useMountEffect(actions.execute);
-
- if (state.status === 'not-executed' || state.status === 'loading') {
- return ;
- }
-
- // Request failed, or the public index is not available
- if (state.status === 'error' || !state.result) {
- return <>{children}>;
- }
-
- // The public index is available
- // That means the app is running in public experimental mode
- return (
-
- {children}
-
- );
-}
diff --git a/plugins/auth-react/src/components/index.ts b/plugins/auth-react/src/components/index.ts
index a85fad95ae..03345df968 100644
--- a/plugins/auth-react/src/components/index.ts
+++ b/plugins/auth-react/src/components/index.ts
@@ -17,6 +17,6 @@
// The index file in ./components/ is typically responsible for selecting
// which components are public API and should be exported from the package.
-export * from './RedirectToRoot';
+export * from './CookieAuthRootRedirect';
export * from './CookieAuthRefreshProvider';
-export * from './ExperimentalAppProtection';
+export * from './AppMode';
diff --git a/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.test.tsx b/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.test.tsx
index aeff0be3b0..cd61b9d94b 100644
--- a/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.test.tsx
+++ b/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.test.tsx
@@ -236,7 +236,7 @@ describe('useCookieAuthRefresh', () => {
await waitFor(() =>
expect(fetchApiMock.fetch).toHaveBeenCalledWith(
- 'http://localhost:7000/techdocs/api/.backstage/v1-cookie',
+ 'http://localhost:7000/techdocs/api/.backstage/auth/v1/cookie',
{ credentials: 'include' },
),
);
diff --git a/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.tsx b/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.tsx
index 45753d7460..b26d45447e 100644
--- a/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.tsx
+++ b/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.tsx
@@ -31,13 +31,13 @@ import { ResponseError } from '@backstage/errors';
export function useCookieAuthRefresh(options: {
// The plugin id used for discovering the API origin
pluginId: string;
- // The path used for calling the refresh cookie endpoint, default to '/.backstage/v1-cookie'
+ // The path used for calling the refresh cookie endpoint, default to '/.backstage/auth/v1/cookie'
path?: string;
}):
| { status: 'loading' }
| { status: 'error'; error: Error; retry: () => void }
| { status: 'success'; data: { expiresAt: string } } {
- const { pluginId, path = '/.backstage/v1-cookie' } = options ?? {};
+ const { pluginId, path = '/.backstage/auth/v1/cookie' } = options ?? {};
const fetchApi = useApi(fetchApiRef);
const discoveryApi = useApi(discoveryApiRef);
diff --git a/yarn.lock b/yarn.lock
index 666548dd95..db7491ccf1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3834,6 +3834,7 @@ __metadata:
"@backstage/cli": "workspace:^"
"@backstage/config": "workspace:^"
"@backstage/core-plugin-api": "workspace:^"
+ "@backstage/plugin-auth-react": "workspace:^"
"@backstage/test-utils": "workspace:^"
"@backstage/types": "workspace:^"
"@backstage/version-bridge": "workspace:^"
@@ -4193,6 +4194,7 @@ __metadata:
"@backstage/core-plugin-api": "workspace:^"
"@backstage/errors": "workspace:^"
"@backstage/frontend-plugin-api": "workspace:^"
+ "@backstage/plugin-auth-react": "workspace:^"
"@backstage/test-utils": "workspace:^"
"@backstage/theme": "workspace:^"
"@backstage/types": "workspace:^"
@@ -5112,7 +5114,9 @@ __metadata:
"@backstage/core-components": "workspace:^"
"@backstage/core-plugin-api": "workspace:^"
"@backstage/errors": "workspace:^"
+ "@backstage/frontend-plugin-api": "workspace:^"
"@backstage/test-utils": "workspace:^"
+ "@backstage/version-bridge": "workspace:^"
"@material-ui/core": ^4.9.13
"@react-hookz/web": ^24.0.0
"@testing-library/jest-dom": ^6.0.0
@@ -25863,7 +25867,7 @@ __metadata:
languageName: node
linkType: hard
-"domhandler@npm:^5.0.1, domhandler@npm:^5.0.2":
+"domhandler@npm:^5.0.1, domhandler@npm:^5.0.2, domhandler@npm:^5.0.3":
version: 5.0.3
resolution: "domhandler@npm:5.0.3"
dependencies:
@@ -26269,7 +26273,7 @@ __metadata:
languageName: node
linkType: hard
-"entities@npm:^4.2.0, entities@npm:^4.3.0, entities@npm:^4.4.0":
+"entities@npm:^4.2.0, entities@npm:^4.4.0":
version: 4.4.0
resolution: "entities@npm:4.4.0"
checksum: 84d250329f4b56b40fa93ed067b194db21e8815e4eb9b59f43a086f0ecd342814f6bc483de8a77da5d64e0f626033192b1b4f1792232a7ea6b970ebe0f3187c2
@@ -30033,14 +30037,14 @@ __metadata:
linkType: hard
"htmlparser2@npm:^8.0.0":
- version: 8.0.1
- resolution: "htmlparser2@npm:8.0.1"
+ version: 8.0.2
+ resolution: "htmlparser2@npm:8.0.2"
dependencies:
domelementtype: ^2.3.0
- domhandler: ^5.0.2
+ domhandler: ^5.0.3
domutils: ^3.0.1
- entities: ^4.3.0
- checksum: 06d5c71e8313597722bc429ae2a7a8333d77bd3ab07ccb916628384b37332027b047f8619448d8f4a3312b6609c6ea3302a4e77435d859e9e686999e6699ca39
+ entities: ^4.4.0
+ checksum: 29167a0f9282f181da8a6d0311b76820c8a59bc9e3c87009e21968264c2987d2723d6fde5a964d4b7b6cba663fca96ffb373c06d8223a85f52a6089ced942700
languageName: node
linkType: hard