big improvements

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2024-03-04 18:31:03 +01:00
parent 97b9d2bd35
commit a8c7b0da6a
7 changed files with 122 additions and 17 deletions
+29 -12
View File
@@ -22,26 +22,18 @@ import {
SignInPage,
} from '@backstage/core-components';
import React from 'react';
import useAsync from 'react-use/lib/useAsync';
import ReactDOM from 'react-dom/client';
import { providers } from '../src/identityProviders';
import {
configApiRef,
createApiFactory,
discoveryApiRef,
identityApiRef,
useApi,
} from '@backstage/core-plugin-api';
import { AuthProxyDiscoveryApi } from '../src/AuthProxyDiscoveryApi';
// TODO(Rugvip): make this available via some util, or maybe Utility API?
function readBasePath(configApi: typeof configApiRef.T) {
let { pathname } = new URL(
configApi.getOptionalString('app.baseUrl') ?? '/',
'http://sample.dev', // baseUrl can be specified as just a path
);
pathname = pathname.replace(/\/*$/, '');
return pathname;
}
const app = createApp({
apis: [
createApiFactory({
@@ -65,8 +57,33 @@ const app = createApp({
});
function RedirectToRoot() {
window.location.pathname = readBasePath(useApi(configApiRef));
return <div />;
const identityApi = useApi(identityApiRef);
const { value, loading, error } = useAsync(async () => {
const { token } = await identityApi.getCredentials();
if (!token) {
throw new Error('Expected Backstage token in sign-in response');
}
return token;
}, [identityApi]);
if (loading) {
return null;
} else if (error) {
return <>An error occurred: {error}</>;
}
return (
<form
ref={form => form?.submit()}
action={window.location.href}
method="POST"
>
<input type="hidden" name="type" value="sign-in" />
<input type="hidden" name="token" value={value} />
<input type="submit" value="Continue" />
</form>
);
}
const App = app.createRoot(
-1
View File
@@ -73,7 +73,6 @@ export async function buildBundle(options: BuildOptions) {
configs.push(
await createConfig(publicPaths, {
...commonConfigOptions,
publicSubPath: '/public',
}),
);
}
-1
View File
@@ -220,7 +220,6 @@ DEPRECATION WARNING: React Router Beta is deprecated and support for it will be
config,
await createConfig(publicPaths, {
...commonConfigOptions,
publicSubPath: '/public',
}),
])
: webpack(config);
+1
View File
@@ -49,6 +49,7 @@
"@backstage/backend-plugin-api": "workspace:^",
"@backstage/config": "workspace:^",
"@backstage/config-loader": "workspace:^",
"@backstage/errors": "workspace:^",
"@backstage/plugin-app-node": "workspace:^",
"@backstage/types": "workspace:^",
"@types/express": "^4.17.6",
+7 -1
View File
@@ -65,8 +65,10 @@ export const appPlugin = createBackendPlugin({
config: coreServices.rootConfig,
database: coreServices.database,
httpRouter: coreServices.httpRouter,
auth: coreServices.auth,
httpAuth: coreServices.httpAuth,
},
async init({ logger, config, database, httpRouter }) {
async init({ logger, config, database, httpRouter, auth, httpAuth }) {
const appPackageName =
config.getOptionalString('app.packageName') ?? 'app';
@@ -76,11 +78,15 @@ export const appPlugin = createBackendPlugin({
logger: winstonLogger,
config,
database,
auth,
httpAuth,
appPackageName,
staticFallbackHandler,
schema,
});
httpRouter.use(router);
// Access control is handled within the router
httpRouter.addAuthPolicy({
allow: 'unauthenticated',
path: '/',
+84 -2
View File
@@ -38,6 +38,8 @@ import {
CACHE_CONTROL_REVALIDATE_CACHE,
} from '../lib/headers';
import { ConfigSchema } from '@backstage/config-loader';
import { AuthService, HttpAuthService } from '@backstage/backend-plugin-api';
import { AuthenticationError } from '@backstage/errors';
// express uses mime v1 while we only have types for mime v2
type Mime = { lookup(arg0: string): string };
@@ -46,6 +48,8 @@ type Mime = { lookup(arg0: string): string };
export interface RouterOptions {
config: Config;
logger: Logger;
auth?: AuthService;
httpAuth?: HttpAuthService;
/**
* If a database is provided it will be used to cache previously deployed static assets.
@@ -99,7 +103,14 @@ export interface RouterOptions {
export async function createRouter(
options: RouterOptions,
): Promise<express.Router> {
const { config, logger, appPackageName, staticFallbackHandler } = options;
const {
config,
logger,
appPackageName,
staticFallbackHandler,
auth,
httpAuth,
} = options;
const disableConfigInjection =
options.disableConfigInjection ??
@@ -154,9 +165,80 @@ export async function createRouter(
router.use(helmet.frameguard({ action: 'deny' }));
const publicDistDir = resolvePath(appDistDir, 'public');
const enablePublicEntryPoint = await fs.pathExists(publicDistDir);
/*
TODO:
- Cookie refresh
- Backend endpoint, /.backstage/v1-cookie
- Frontend provider
- Remove issueUserCookie from HttpAuthService and move to auth-node
- Move RedirectToRoot to auth-react
- Document index-public-experimental & how to use
- Logout? How do we clear the cookie?
*/
if (enablePublicEntryPoint && auth && httpAuth) {
const publicRouter = Router();
// TODO
// publicRouter.use(createCookieAuthRouter({ httpAuth }))
publicRouter.use(async (req, _res, next) => {
const credentials = await httpAuth.credentials(req, {
allow: ['user', 'service', 'none'],
allowLimitedAccess: true,
});
if (credentials.principal.type === 'none') {
next();
} else {
next('router');
}
});
publicRouter.post(
'*',
express.urlencoded({ extended: true }),
async (req, res, next) => {
if (req.body.type === 'sign-in') {
const credentials = await auth.authenticate(req.body.token);
if (!auth.isPrincipal(credentials, 'user')) {
throw new AuthenticationError('Invalid token, not a user');
}
await httpAuth.issueUserCookie(res, {
credentials,
});
// Resume as if it was a GET request towards the outer protected router, serving index.html
req.method = 'GET';
next('router');
} else {
throw new Error('Invalid POST request to /');
}
},
);
publicRouter.use(
await createEntryPointRouter({
logger: logger.child({ entry: 'public' }),
rootDir: publicDistDir,
assetStore: assetStore?.withNamespace('public'),
appConfigs, // TODO(Rugvip): We should not be including the full config here
}),
);
router.use(publicRouter);
}
router.use(
await createEntryPointRouter({
logger,
logger: logger.child({ entry: 'main' }),
rootDir: appDistDir,
assetStore,
staticFallbackHandler,
+1
View File
@@ -4691,6 +4691,7 @@ __metadata:
"@backstage/cli": "workspace:^"
"@backstage/config": "workspace:^"
"@backstage/config-loader": "workspace:^"
"@backstage/errors": "workspace:^"
"@backstage/plugin-app-node": "workspace:^"
"@backstage/types": "workspace:^"
"@types/express": ^4.17.6