-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #23719 from backstage/rugvip/app-auth
[Auth] Migrate `app-backend` to use new auth services
- Loading branch information
Showing
46 changed files
with
1,238 additions
and
117 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@backstage/cli': patch | ||
--- | ||
|
||
Fix the bundle public subpath configuration. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
'@backstage/plugin-auth-react': minor | ||
--- | ||
|
||
**BREAKING**: Removed the path option from `CookieAuthRefreshProvider` and `useCookieAuthRefresh`. | ||
|
||
A new `CookieAuthRedirect` component has been added to redirect a public app bundle to the protected one when using the `app-backend` with a separate public entry point. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@backstage/plugin-techdocs-backend': patch | ||
--- | ||
|
||
Use the default cookie endpoints added automatically when a cookie policy is set. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
'@backstage/core-app-api': patch | ||
'@backstage/frontend-app-api': patch | ||
--- | ||
|
||
The app is now aware of if it is being served from the `app-backend` with a separate public and protected bundles. When in protected mode the app will now continuously refresh the session cookie, as well as clear the cookie if the user signs out. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@backstage/plugin-app-backend': patch | ||
--- | ||
|
||
Track assets namespace in the cache store, implement a cookie authentication for when the public entry is enabled and used with the new auth services. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@backstage/cli': patch | ||
--- | ||
|
||
When building the frontend app public assets are now also copied to the public dist directory when in use. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@backstage/backend-plugin-api': patch | ||
--- | ||
|
||
The credentials passed to the `issueUserCookie` method of the `HttpAuthService` are no longer required to represent a user principal. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@backstage/backend-app-api': patch | ||
--- | ||
|
||
Automatically creates a get and delete cookie endpoint when a `user-cookie` policy is added. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
--- | ||
id: enable-public-entry | ||
title: Enabling a public entry point | ||
description: A guide for how to experiment with public and protected Backstage app bundles | ||
--- | ||
|
||
# Enable Public Entry (Experimental) | ||
|
||
In this tutorial, you will learn how to restrict access to your main Backstage app bundle to authenticated users only. | ||
|
||
It is expected that the protected bundle feature will be refined in future development iterations, but for now, here is a simplified explanation of how it works: | ||
|
||
Your Backstage app bundle is split into two code entries: | ||
|
||
- Public entry point containing login pages; | ||
- There is also a protected main entry point that contains the code for what you see after signing in. | ||
|
||
With that, Backstage's cli and backend will detect public entry point and serve it to unauthenticated users, while serving the main, protected entry point only to authenticated users. | ||
|
||
## Requirements | ||
|
||
- The app needs to be served by the `app-backend` plugin, or this won't work; | ||
- Also it will only work for those using `backstage-cli` to build and serve their Backstage app. | ||
|
||
## Step-by-step | ||
|
||
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. 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'; | ||
import ReactDOM from 'react-dom/client'; | ||
import { createApp } from '@backstage/app-defaults'; | ||
import { AppRouter } from '@backstage/core-app-api'; | ||
import { | ||
AlertDisplay, | ||
OAuthRequestDialog, | ||
SignInPage, | ||
} from '@backstage/core-components'; | ||
import { | ||
configApiRef, | ||
discoveryApiRef, | ||
createApiFactory, | ||
} from '@backstage/core-plugin-api'; | ||
import { CookieAuthRedirect } from '@backstage/plugin-auth-react'; | ||
|
||
// Notice that this is only setting up what is needed by the sign-in pages | ||
const app = createApp({ | ||
// If you have any custom APIs that your sign-in page depends on, you need to add them here | ||
apis: [], | ||
components: { | ||
SignInPage: props => { | ||
return ( | ||
<SignInPage | ||
{...props} | ||
providers={['guest']} | ||
title="Select a sign-in method" | ||
/> | ||
); | ||
}, | ||
}, | ||
}); | ||
|
||
const App = app.createRoot( | ||
<> | ||
<AlertDisplay transientTimeoutMs={2500} /> | ||
<OAuthRequestDialog /> | ||
<AppRouter> | ||
{/* This component triggers an authenticated redirect to the main app, while staying on the same URL */} | ||
<CookieAuthRedirect /> | ||
</AppRouter> | ||
</>, | ||
); | ||
|
||
ReactDOM.createRoot(document.getElementById('root')!).render(<App />); | ||
``` | ||
|
||
:::note | ||
The frontend will handle cookie refreshing automatically, so you don't have to worry about it. | ||
::: | ||
|
||
3. Let's verify that everything is working locally. From your project root folder, run the following commands to build the app and start the backend: | ||
|
||
```sh | ||
# building the app package | ||
yarn workspace app start | ||
# starting the backend api | ||
yarn start-backend | ||
``` | ||
|
||
4. Visit http://localhost:7007 to see the public app and validate that the _index.html_ response only contains a minimal application. | ||
:::note | ||
Regular app serving will always serve protected apps without authenticating. | ||
::: | ||
|
||
5. Finally, as soon as you log in, you will be redirected to the main app home page (inspect the page and see that the protected bundle was served from the app backend after the redirect). | ||
|
||
That's it! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
...app-api/src/services/implementations/httpRouter/createCookieAuthRefreshMiddleware.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/* | ||
* 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 express from 'express'; | ||
import request from 'supertest'; | ||
import { mockCredentials, mockServices } from '@backstage/backend-test-utils'; | ||
import { createCookieAuthRefreshMiddleware } from './createCookieAuthRefreshMiddleware'; | ||
|
||
describe('createCookieAuthRefreshMiddleware', () => { | ||
let app: express.Express; | ||
|
||
beforeAll(async () => { | ||
const auth = mockServices.auth(); | ||
const httpAuth = mockServices.httpAuth(); | ||
const router = createCookieAuthRefreshMiddleware({ auth, httpAuth }); | ||
app = express().use(router); | ||
}); | ||
|
||
beforeEach(() => { | ||
jest.resetAllMocks(); | ||
}); | ||
|
||
it('should issue the user cookie', async () => { | ||
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()}`, | ||
); | ||
}); | ||
|
||
it('should remove the user cookie', async () => { | ||
const response = await request(app).delete('/.backstage/auth/v1/cookie'); | ||
expect(response.status).toBe(204); | ||
expect(response.header['set-cookie'][0]).toMatch('backstage-auth='); | ||
}); | ||
}); |
47 changes: 47 additions & 0 deletions
47
...kend-app-api/src/services/implementations/httpRouter/createCookieAuthRefreshMiddleware.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* | ||
* 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 { AuthService, HttpAuthService } from '@backstage/backend-plugin-api'; | ||
import Router from 'express-promise-router'; | ||
|
||
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 { auth, httpAuth } = options; | ||
const router = Router(); | ||
|
||
// Endpoint that sets the cookie for the user | ||
router.get(WELL_KNOWN_COOKIE_PATH_V1, async (_, res) => { | ||
const { expiresAt } = await httpAuth.issueUserCookie(res); | ||
res.json({ expiresAt: expiresAt.toISOString() }); | ||
}); | ||
|
||
// Endpoint that removes the cookie for the user | ||
router.delete(WELL_KNOWN_COOKIE_PATH_V1, async (_, res) => { | ||
const credentials = await auth.getNoneCredentials(); | ||
await httpAuth.issueUserCookie(res, { credentials }); | ||
res.status(204).end(); | ||
}); | ||
|
||
return router; | ||
} |
Oops, something went wrong.