core-app-api: navigate to app base URL on signout

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2022-06-14 17:53:16 +02:00
parent c2f9527fe2
commit 8fe2357101
4 changed files with 55 additions and 25 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-app-api': patch
---
The `signOut` method of the `IdentityApi` will now navigate the user back to the base URL of the app as indicated by the `app.baseUrl` config.
@@ -31,7 +31,7 @@ describe('AppIdentityProxy', () => {
it('should forward user identities', async () => {
const proxy = new AppIdentityProxy();
proxy.setTarget(mockIdentityApi);
proxy.setTarget(mockIdentityApi, { signOutTargetUrl: '/' });
const logs = await withLogCollector(async () => {
mockIdentityApi.getBackstageIdentity.mockResolvedValueOnce({
@@ -55,7 +55,7 @@ describe('AppIdentityProxy', () => {
it('should warn about invalid user entity refs', async () => {
const proxy = new AppIdentityProxy();
proxy.setTarget(mockIdentityApi);
proxy.setTarget(mockIdentityApi, { signOutTargetUrl: '/' });
const logs = await withLogCollector(async () => {
mockIdentityApi.getBackstageIdentity.mockResolvedValueOnce({
@@ -79,4 +79,16 @@ describe('AppIdentityProxy', () => {
error: [],
});
});
it('should navigate to target URL on sign out', async () => {
const proxy = new AppIdentityProxy();
proxy.setTarget(mockIdentityApi, { signOutTargetUrl: '/foo' });
Object.defineProperty(window, 'location', {
writable: true,
value: {},
});
await proxy.signOut();
expect(location.href).toBe('/foo');
});
});
@@ -49,6 +49,7 @@ export class AppIdentityProxy implements IdentityApi {
private target?: CompatibilityIdentityApi;
private waitForTarget: Promise<CompatibilityIdentityApi>;
private resolveTarget: (api: CompatibilityIdentityApi) => void = () => {};
private signOutTargetUrl = '/';
constructor() {
this.waitForTarget = new Promise<CompatibilityIdentityApi>(resolve => {
@@ -57,8 +58,12 @@ export class AppIdentityProxy implements IdentityApi {
}
// This is called by the app manager once the sign-in page provides us with an implementation
setTarget(identityApi: CompatibilityIdentityApi) {
setTarget(
identityApi: CompatibilityIdentityApi,
targetOptions: { signOutTargetUrl: string },
) {
this.target = identityApi;
this.signOutTargetUrl = targetOptions.signOutTargetUrl;
this.resolveTarget(identityApi);
}
@@ -119,6 +124,6 @@ export class AppIdentityProxy implements IdentityApi {
async signOut(): Promise<void> {
await this.waitForTarget.then(target => target.signOut());
location.reload();
location.href = this.signOutTargetUrl;
}
}
+29 -21
View File
@@ -334,41 +334,49 @@ export class AppManager implements BackstageApp {
children: ReactElement;
}) => {
const [identityApi, setIdentityApi] = useState<IdentityApi>();
const configApi = useApi(configApiRef);
const basePath = getBasePath(configApi);
if (!identityApi) {
return <Component onSignInSuccess={setIdentityApi} />;
}
this.appIdentityProxy.setTarget(identityApi);
this.appIdentityProxy.setTarget(identityApi, {
signOutTargetUrl: basePath || '/',
});
return children;
};
const AppRouter = ({ children }: PropsWithChildren<{}>) => {
const configApi = useApi(configApiRef);
const mountPath = `${getBasePath(configApi)}/*`;
const basePath = getBasePath(configApi);
const mountPath = `${basePath}/*`;
const { routeObjects } = useContext(InternalAppContext);
// If the app hasn't configured a sign-in page, we just continue as guest.
if (!SignInPageComponent) {
this.appIdentityProxy.setTarget({
getUserId: () => 'guest',
getIdToken: async () => undefined,
getProfile: () => ({
email: 'guest@example.com',
displayName: 'Guest',
}),
getProfileInfo: async () => ({
email: 'guest@example.com',
displayName: 'Guest',
}),
getBackstageIdentity: async () => ({
type: 'user',
userEntityRef: 'user:default/guest',
ownershipEntityRefs: ['user:default/guest'],
}),
getCredentials: async () => ({}),
signOut: async () => {},
});
this.appIdentityProxy.setTarget(
{
getUserId: () => 'guest',
getIdToken: async () => undefined,
getProfile: () => ({
email: 'guest@example.com',
displayName: 'Guest',
}),
getProfileInfo: async () => ({
email: 'guest@example.com',
displayName: 'Guest',
}),
getBackstageIdentity: async () => ({
type: 'user',
userEntityRef: 'user:default/guest',
ownershipEntityRefs: ['user:default/guest'],
}),
getCredentials: async () => ({}),
signOut: async () => {},
},
{ signOutTargetUrl: basePath || '/' },
);
return (
<RouterComponent>