Skip to content

Commit

Permalink
Merge pull request #5093 from gooddata/pb-deauth-returnurl
Browse files Browse the repository at this point in the history
feat: support returnTo with deauthenticate
  • Loading branch information
pbenes authored Jul 2, 2024
2 parents c01c498 + a78fa96 commit 3b5b6ae
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@gooddata/sdk-ui-all",
"comment": "Allow specifying returnTo parameter with backend deauthenticate",
"type": "none"
}
],
"packageName": "@gooddata/sdk-ui-all"
}
2 changes: 1 addition & 1 deletion libs/sdk-backend-base/api/sdk-backend-base.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export class AuthProviderCallGuard implements IAuthProviderCallGuard {
// (undocumented)
authenticate: (context: IAuthenticationContext) => Promise<IAuthenticatedPrincipal>;
// (undocumented)
deauthenticate(context: IAuthenticationContext): Promise<void>;
deauthenticate(context: IAuthenticationContext, returnTo?: string): Promise<void>;
// (undocumented)
getCurrentPrincipal(context: IAuthenticationContext): Promise<IAuthenticatedPrincipal | null>;
// (undocumented)
Expand Down
4 changes: 2 additions & 2 deletions libs/sdk-backend-base/src/decoratedBackend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ class BackendWithDecoratedServices implements IAnalyticalBackend {
return this.decorated.authenticate(force);
}

public deauthenticate(): Promise<void> {
return this.decorated.deauthenticate();
public deauthenticate(returnTo?: string): Promise<void> {
return this.decorated.deauthenticate(returnTo);
}

public isAuthenticated(): Promise<IAuthenticatedPrincipal | null> {
Expand Down
4 changes: 2 additions & 2 deletions libs/sdk-backend-base/src/toolkit/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ export class AuthProviderCallGuard implements IAuthProviderCallGuard {
return this.realProvider.getCurrentPrincipal(context);
}

public async deauthenticate(context: IAuthenticationContext): Promise<void> {
return this.realProvider.deauthenticate(context);
public async deauthenticate(context: IAuthenticationContext, returnTo?: string): Promise<void> {
return this.realProvider.deauthenticate(context, returnTo);
}
}

Expand Down
4 changes: 2 additions & 2 deletions libs/sdk-backend-spi/api/sdk-backend-spi.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ export interface IAnalyticalBackend {
readonly config: IAnalyticalBackendConfig;
currentUser(): IUserService;
dataSources(): IDataSourcesService;
deauthenticate(): Promise<void>;
deauthenticate(returnTo?: string): Promise<void>;
entitlements(): IEntitlements;
isAuthenticated(): Promise<IAuthenticatedPrincipal | null>;
onHostname(hostname: string): IAnalyticalBackend;
Expand Down Expand Up @@ -286,7 +286,7 @@ export interface IAuthenticationContext {
// @public
export interface IAuthenticationProvider {
authenticate(context: IAuthenticationContext): Promise<IAuthenticatedPrincipal>;
deauthenticate(context: IAuthenticationContext): Promise<void>;
deauthenticate(context: IAuthenticationContext, returnTo?: string): Promise<void>;
getCurrentPrincipal(context: IAuthenticationContext): Promise<IAuthenticatedPrincipal | null>;
initializeClient?(client: any): void;
onNotAuthenticated?: NotAuthenticatedHandler;
Expand Down
6 changes: 4 additions & 2 deletions libs/sdk-backend-spi/src/backend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,11 @@ export interface IAnalyticalBackend {

/**
* Triggers deauthentication process against the backend.
* @param returnTo - url to redirect after successful relogin
*
* @returns promise of the completed process, or rejection if deauthentication failed.
*/
deauthenticate(): Promise<void>;
deauthenticate(returnTo?: string): Promise<void>;

/**
* Returns an organization available on the backend.
Expand Down Expand Up @@ -232,8 +233,9 @@ export interface IAuthenticationProvider {
* Clear existing authentication.
*
* @param context - context in which the authentication is done
* @param returnTo - url to redirect after successful logout
*/
deauthenticate(context: IAuthenticationContext): Promise<void>;
deauthenticate(context: IAuthenticationContext, returnTo?: string): Promise<void>;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion libs/sdk-backend-tiger/api/sdk-backend-tiger.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ export abstract class TigerAuthProviderBase implements IAuthenticationProvider {
// (undocumented)
abstract authenticate(context: IAuthenticationContext): Promise<IAuthenticatedPrincipal>;
// (undocumented)
deauthenticate(context: IAuthenticationContext): Promise<void>;
deauthenticate(context: IAuthenticationContext, returnTo?: string): Promise<void>;
// (undocumented)
getCurrentPrincipal(context: IAuthenticationContext): Promise<IAuthenticatedPrincipal | null>;
// (undocumented)
Expand Down
15 changes: 10 additions & 5 deletions libs/sdk-backend-tiger/src/auth.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// (C) 2019-2023 GoodData Corporation
// (C) 2019-2024 GoodData Corporation
import {
AuthenticationFlow,
IAnalyticalBackend,
Expand All @@ -24,8 +24,8 @@ export abstract class TigerAuthProviderBase implements IAuthenticationProvider {

public abstract authenticate(context: IAuthenticationContext): Promise<IAuthenticatedPrincipal>;

public deauthenticate(context: IAuthenticationContext): Promise<void> {
const logoutUrl = createTigerDeauthenticationUrl(context.backend, window.location);
public deauthenticate(context: IAuthenticationContext, returnTo?: string): Promise<void> {
const logoutUrl = createTigerDeauthenticationUrl(context.backend, window.location, returnTo);
return Promise.resolve(window.location.assign(logoutUrl));
}

Expand Down Expand Up @@ -332,7 +332,11 @@ export function createTigerAuthenticationUrl(
* @param location - current location
* @public
*/
export function createTigerDeauthenticationUrl(backend: IAnalyticalBackend, location: Location): string {
export function createTigerDeauthenticationUrl(
backend: IAnalyticalBackend,
location: Location,
returnTo?: string,
): string {
let host = `${location.protocol}//${location.host}`;
const { hostname: backendHostname } = backend.config;

Expand All @@ -341,7 +345,8 @@ export function createTigerDeauthenticationUrl(backend: IAnalyticalBackend, loca
host = backendHostname;
}

return `${host}/logout`;
const returnUrl = returnTo ? `?returnTo=${returnTo}` : "";
return `${host}/logout${returnUrl}`;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions libs/sdk-backend-tiger/src/backend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,11 @@ export class TigerBackend implements IAnalyticalBackend {
return new TigerBackend(this.config, this.implConfig, this.telemetry, guardedAuthProvider);
}

public deauthenticate(): Promise<void> {
public deauthenticate(returnTo?: string): Promise<void> {
if (!this.authProvider) {
throw new NotAuthenticated("Backend is not set up with authentication provider.");
}
return this.authProvider.deauthenticate(this.getAuthenticationContext());
return this.authProvider.deauthenticate(this.getAuthenticationContext(), returnTo);
}

public organization(organizationId: string): IOrganization {
Expand Down
38 changes: 38 additions & 0 deletions libs/sdk-backend-tiger/src/tests/auth.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// (C) 2020-2024 GoodData Corporation
import { ContextDeferredAuthProvider } from "../auth.js";
import { TigerBackend } from "../backend/index.js";
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";

describe("ContextDeferredAuthProvider", () => {
describe("deauthenticate", () => {
const hostname = "https://domain.gooddata.com";
let originalWindow;
beforeEach(() => {
originalWindow = global.window;
});

afterEach(() => {
global.window = originalWindow;
});

it.each([
[undefined, `${hostname}/logout`],
["https://redirect.gooddata.com", `${hostname}/logout?returnTo=https://redirect.gooddata.com`],
])('should set deauthenticate for returnTo="%s" to be "%s"', async (returnTo, expectedLogoutUrl) => {
const assign = vi.fn();
global.window = {
location: {
assign,
},
} as any;
const provider = new ContextDeferredAuthProvider();
const backend = new TigerBackend({ hostname });
const context = {
backend,
client: null,
};
await provider.deauthenticate(context, returnTo);
expect(assign).toHaveBeenCalledWith(expectedLogoutUrl);
});
});
});

0 comments on commit 3b5b6ae

Please sign in to comment.