Merge pull request #34394 from backstage/benjdlambert/disable-experimental-public-entrypoint

Add config to disable experimental public entry point
This commit is contained in:
Ben Lambert
2026-05-26 11:12:20 +02:00
committed by GitHub
6 changed files with 124 additions and 2 deletions
@@ -0,0 +1,5 @@
---
'@backstage/plugin-app-backend': patch
---
Added a new `app.disablePublicEntryPoint` config option that allows you to opt out of the automatic public sign-in entry point. When set to `true`, the app backend will skip serving the public entry point to unauthenticated users, even if the app was bundled with an `index-public-experimental` entry point.
+9
View File
@@ -42,5 +42,14 @@ export interface Config {
* If you disable this, it is recommended to set a `staticFallbackHandler` instead.
*/
disableStaticFallbackCache?: boolean;
/**
* Disables the public entry point used for unauthenticated sign-in pages.
* When the app is bundled with an `index-public-experimental` entry point,
* the app backend will automatically serve it to unauthenticated users.
* Set this to `true` to skip that behavior and always serve the main
* entry point instead.
*/
disablePublicEntryPoint?: boolean;
};
}
@@ -0,0 +1 @@
this is public index.html
@@ -0,0 +1 @@
this is public main.txt
+103 -1
View File
@@ -21,7 +21,11 @@ import { resolve as resolvePath } from 'node:path';
import request from 'supertest';
import { createRouter } from './router';
import { loadConfigSchema } from '@backstage/config-loader';
import { mockServices, TestDatabases } from '@backstage/backend-test-utils';
import {
mockCredentials,
mockServices,
TestDatabases,
} from '@backstage/backend-test-utils';
jest.mock('../lib/config', () => ({
injectConfig: jest.fn(),
@@ -136,6 +140,104 @@ describe('createRouter with static fallback handler', () => {
});
});
describe('createRouter with public entry point', () => {
let app: express.Express;
beforeAll(async () => {
const router = await createRouter({
logger: mockServices.logger.mock(),
database: mockServices.database.mock(),
auth: mockServices.auth(),
httpAuth: mockServices.httpAuth({
defaultCredentials: mockCredentials.none(),
}),
config: mockServices.rootConfig({
data: {
app: { disableStaticFallbackCache: true },
},
}),
appPackageName: 'example-app',
});
app = express().use(router);
});
beforeEach(() => {
jest.resetAllMocks();
});
it('serves the public entry point to unauthenticated users', async () => {
const response = await request(app).get('/index.html');
expect(response.status).toBe(200);
expect(response.text.trim()).toBe('this is public index.html');
});
it('serves the main entry point to authenticated users', async () => {
const response = await request(app)
.get('/index.html')
.set('Cookie', mockCredentials.limitedUser.cookie());
expect(response.status).toBe(200);
expect(response.text.trim()).toBe('this is index.html');
});
it('handles sign-in and issues a user cookie', async () => {
const response = await request(app)
.post('/')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send(`type=sign-in&token=${mockCredentials.user.token()}`);
expect(response.status).toBe(200);
expect(response.header['set-cookie']).toBeDefined();
expect(response.text.trim()).toBe('this is index.html');
});
it('rejects POST requests without a sign-in type', async () => {
const response = await request(app)
.post('/')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send('type=something-else');
expect(response.status).toBe(500);
});
});
describe('createRouter with disablePublicEntryPoint', () => {
let app: express.Express;
beforeAll(async () => {
const router = await createRouter({
logger: mockServices.logger.mock(),
database: mockServices.database.mock(),
auth: mockServices.auth(),
httpAuth: mockServices.httpAuth({
defaultCredentials: mockCredentials.none(),
}),
config: mockServices.rootConfig({
data: {
app: {
disableStaticFallbackCache: true,
disablePublicEntryPoint: true,
},
},
}),
appPackageName: 'example-app',
});
app = express().use(router);
});
beforeEach(() => {
jest.resetAllMocks();
});
it('serves the main entry point to unauthenticated users', async () => {
const response = await request(app).get('/index.html');
expect(response.status).toBe(200);
expect(response.text.trim()).toBe('this is index.html');
});
});
describe('createRouter config schema test', () => {
const libConfigs = require('../lib/config');
const libConfigsActual = jest.requireActual('../lib/config');
+5 -1
View File
@@ -153,7 +153,11 @@ export async function createRouter(
const publicDistDir = resolvePath(appDistDir, 'public');
const enablePublicEntryPoint = await fs.pathExists(publicDistDir);
const disablePublicEntryPoint = config.getOptionalBoolean(
'app.disablePublicEntryPoint',
);
const enablePublicEntryPoint =
!disablePublicEntryPoint && (await fs.pathExists(publicDistDir));
if (enablePublicEntryPoint) {
logger.info(