Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit a25920f

Browse files
author
Mikhail Aheichyk
committed
Fixes authentication when user is registered via module API
Signed-off-by: Mikhail Aheichyk <[email protected]>
1 parent 9e5c4e9 commit a25920f

File tree

5 files changed

+147
-4
lines changed

5 files changed

+147
-4
lines changed

src/Lifecycle.ts

+14-4
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,9 @@ dis.register((payload) => {
7373
onLoggedOut();
7474
} else if (payload.action === Action.OverwriteLogin) {
7575
const typed = <OverwriteLoginPayload>payload;
76+
// is invoked without dispatching of "on_logging_in"
7677
// noinspection JSIgnoredPromiseFromCall - we don't care if it fails
77-
doSetLoggedIn(typed.credentials, true);
78+
doSetLoggedIn(typed.credentials, true, false);
7879
}
7980
});
8081

@@ -573,10 +574,14 @@ export async function hydrateSession(credentials: IMatrixClientCreds): Promise<M
573574
*
574575
* @param {IMatrixClientCreds} credentials
575576
* @param {Boolean} clearStorageEnabled
576-
*
577+
* @param {Boolean} dispatchOnLoggingIn if true then "on_logging_in" is dispatched
577578
* @returns {Promise} promise which resolves to the new MatrixClient once it has been started
578579
*/
579-
async function doSetLoggedIn(credentials: IMatrixClientCreds, clearStorageEnabled: boolean): Promise<MatrixClient> {
580+
async function doSetLoggedIn(
581+
credentials: IMatrixClientCreds,
582+
clearStorageEnabled: boolean,
583+
dispatchOnLoggingIn = true,
584+
): Promise<MatrixClient> {
580585
credentials.guest = Boolean(credentials.guest);
581586

582587
const softLogout = isSoftLogout();
@@ -602,7 +607,12 @@ async function doSetLoggedIn(credentials: IMatrixClientCreds, clearStorageEnable
602607
//
603608
// we fire it *synchronously* to make sure it fires before on_logged_in.
604609
// (dis.dispatch uses `window.setTimeout`, which does not guarantee ordering.)
605-
dis.dispatch({ action: "on_logging_in" }, true);
610+
//
611+
// can be disabled to resolve "Cannot dispatch in the middle of a dispatch."
612+
// error when it is invoked via another dispatch that is not yet finished.
613+
if (dispatchOnLoggingIn) {
614+
dis.dispatch({ action: "on_logging_in" }, true);
615+
}
606616

607617
if (clearStorageEnabled) {
608618
await clearStorage();

src/modules/ProxiedModuleApi.ts

+18
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { MatrixClientPeg } from "../MatrixClientPeg";
3636
import { getCachedRoomIDForAlias } from "../RoomAliasCache";
3737
import { Action } from "../dispatcher/actions";
3838
import { OverwriteLoginPayload } from "../dispatcher/payloads/OverwriteLoginPayload";
39+
import { ActionPayload } from "../dispatcher/payloads";
3940

4041
/**
4142
* Glue between the `ModuleApi` interface and the react-sdk. Anticipates one instance
@@ -44,6 +45,18 @@ import { OverwriteLoginPayload } from "../dispatcher/payloads/OverwriteLoginPayl
4445
export class ProxiedModuleApi implements ModuleApi {
4546
private cachedTranslations: Optional<TranslationStringsObject>;
4647

48+
private overrideLoginResolve?: () => void;
49+
50+
public constructor() {
51+
dispatcher.register(this.onAction);
52+
}
53+
54+
private onAction = (payload: ActionPayload): void => {
55+
if (payload.action === Action.OnLoggedIn) {
56+
this.overrideLoginResolve?.();
57+
}
58+
};
59+
4760
/**
4861
* All custom translations used by the associated module.
4962
*/
@@ -154,6 +167,11 @@ export class ProxiedModuleApi implements ModuleApi {
154167
},
155168
true,
156169
); // require to be sync to match inherited interface behaviour
170+
171+
// wait for login to complete
172+
await new Promise<void>((resolve) => {
173+
this.overrideLoginResolve = resolve;
174+
});
157175
}
158176

159177
/**

test/Lifecycle-test.ts

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
Copyright 2023 Mikhail Aheichyk
3+
Copyright 2023 Nordeck IT + Consulting GmbH.
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
import { AccountAuthInfo } from "@matrix-org/react-sdk-module-api/lib/types/AccountAuthInfo";
19+
import { logger } from "matrix-js-sdk/src/logger";
20+
21+
import defaultDispatcher from "../src/dispatcher/dispatcher";
22+
import { OverwriteLoginPayload } from "../src/dispatcher/payloads/OverwriteLoginPayload";
23+
import { Action } from "../src/dispatcher/actions";
24+
import { setLoggedIn } from "../src/Lifecycle";
25+
import { stubClient } from "./test-utils";
26+
import { ActionPayload } from "../src/dispatcher/payloads";
27+
28+
jest.mock("../src/utils/createMatrixClient", () => ({
29+
__esModule: true,
30+
default: jest.fn().mockReturnValue({
31+
clearStores: jest.fn(),
32+
}),
33+
}));
34+
35+
describe("Lifecycle", () => {
36+
stubClient();
37+
38+
jest.spyOn(logger, "error").mockReturnValue(undefined);
39+
jest.spyOn(logger, "warn").mockReturnValue(undefined);
40+
jest.spyOn(logger, "log").mockImplementation(undefined);
41+
42+
it("should call 'overwrite_login' callback and receive 'on_logged_in'", async () => {
43+
// promise to wait 'on_logged_in'
44+
const loggedInPromise = new Promise((resolve, reject) => {
45+
defaultDispatcher.register((payload: ActionPayload) => {
46+
if (payload.action === Action.OnLoggedIn) {
47+
resolve(undefined);
48+
}
49+
});
50+
});
51+
52+
// dispatch 'overwrite_login'
53+
const accountInfo = {} as unknown as AccountAuthInfo;
54+
defaultDispatcher.dispatch<OverwriteLoginPayload>(
55+
{
56+
action: Action.OverwriteLogin,
57+
credentials: {
58+
...accountInfo,
59+
guest: false,
60+
},
61+
},
62+
true,
63+
);
64+
65+
await expect(loggedInPromise).resolves.toBeUndefined();
66+
});
67+
68+
it("should setLoggedIn", async () => {
69+
// promise to wait 'on_logging_in'
70+
const loggingInPromise = new Promise((resolve, reject) => {
71+
defaultDispatcher.register((payload: ActionPayload) => {
72+
if (payload.action === "on_logging_in") {
73+
resolve(undefined);
74+
}
75+
});
76+
});
77+
78+
await setLoggedIn({
79+
accessToken: "some_token",
80+
homeserverUrl: "https://example.com",
81+
userId: "@some_user_id",
82+
});
83+
84+
await expect(loggingInPromise).resolves.toBeUndefined();
85+
});
86+
});

test/modules/ProxiedModuleApi-test.ts

+26
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ limitations under the License.
1515
*/
1616

1717
import { TranslationStringsObject } from "@matrix-org/react-sdk-module-api/lib/types/translations";
18+
import { AccountAuthInfo } from "@matrix-org/react-sdk-module-api/lib/types/AccountAuthInfo";
1819

1920
import { ProxiedModuleApi } from "../../src/modules/ProxiedModuleApi";
2021
import { stubClient } from "../test-utils";
2122
import { setLanguage } from "../../src/languageHandler";
2223
import { ModuleRunner } from "../../src/modules/ModuleRunner";
2324
import { registerMockModule } from "./MockModule";
25+
import defaultDispatcher from "../../src/dispatcher/dispatcher";
26+
import { Action } from "../../src/dispatcher/actions";
2427

2528
describe("ProxiedApiModule", () => {
2629
afterEach(() => {
@@ -44,6 +47,29 @@ describe("ProxiedApiModule", () => {
4447
expect(api.translations).toBe(translations);
4548
});
4649

50+
it("should overwriteAccountAuth", async () => {
51+
const dispatchSpy = jest.spyOn(defaultDispatcher, "dispatch");
52+
53+
const api = new ProxiedModuleApi();
54+
const accountInfo = {} as unknown as AccountAuthInfo;
55+
const promise = api.overwriteAccountAuth(accountInfo);
56+
57+
expect(dispatchSpy).toHaveBeenCalledWith(
58+
expect.objectContaining({
59+
action: Action.OverwriteLogin,
60+
credentials: {
61+
...accountInfo,
62+
guest: false,
63+
},
64+
}),
65+
expect.anything(),
66+
);
67+
68+
defaultDispatcher.fire(Action.OnLoggedIn);
69+
70+
await expect(promise).resolves.toBeUndefined();
71+
});
72+
4773
describe("integration", () => {
4874
it("should translate strings using translation system", async () => {
4975
// Test setup

test/test-utils/test-utils.ts

+3
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,9 @@ export function createTestClient(): MatrixClient {
231231
room_id: roomId,
232232
});
233233
}),
234+
startClient: jest.fn().mockResolvedValue(undefined),
235+
stopClient: jest.fn().mockReturnValue(undefined),
236+
removeAllListeners: jest.fn().mockReturnValue(undefined),
234237
} as unknown as MatrixClient;
235238

236239
client.reEmitter = new ReEmitter(client);

0 commit comments

Comments
 (0)