Support for stable m.oauth UIA stage from MSC4312 (#31704)
* Support for stable m.oauth UIA stage from MSC4312 * Unit tests * readonly props
This commit is contained in:
@@ -117,6 +117,7 @@ export default class InteractiveAuthComponent<T> extends React.Component<Interac
|
|||||||
AuthType.UnstableRegistrationToken,
|
AuthType.UnstableRegistrationToken,
|
||||||
AuthType.Sso,
|
AuthType.Sso,
|
||||||
AuthType.SsoUnstable,
|
AuthType.SsoUnstable,
|
||||||
|
AuthType.OAuth,
|
||||||
CustomAuthType.MasCrossSigningReset,
|
CustomAuthType.MasCrossSigningReset,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -951,7 +951,7 @@ export class FallbackAuthEntry<T extends object> extends React.Component<IAuthEn
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum CustomAuthType {
|
export enum CustomAuthType {
|
||||||
// Workaround for MAS requiring non-UIA authentication for resetting cross-signing.
|
// This is the unstable value from MSC4312
|
||||||
MasCrossSigningReset = "org.matrix.cross_signing_reset",
|
MasCrossSigningReset = "org.matrix.cross_signing_reset",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -960,7 +960,8 @@ export class MasUnlockCrossSigningAuthEntry extends FallbackAuthEntry<{
|
|||||||
url?: string;
|
url?: string;
|
||||||
};
|
};
|
||||||
}> {
|
}> {
|
||||||
public static LOGIN_TYPE = CustomAuthType.MasCrossSigningReset;
|
public static readonly LOGIN_TYPE = AuthType.OAuth;
|
||||||
|
public static readonly UNSTABLE_LOGIN_TYPE = CustomAuthType.MasCrossSigningReset;
|
||||||
|
|
||||||
private onGoToAccountClick = (): void => {
|
private onGoToAccountClick = (): void => {
|
||||||
if (!this.props.stageParams?.url) return;
|
if (!this.props.stageParams?.url) return;
|
||||||
@@ -1017,6 +1018,7 @@ export interface IStageComponent extends React.ComponentClass<React.PropsWithRef
|
|||||||
|
|
||||||
export default function getEntryComponentForLoginType(loginType: AuthType | CustomAuthType): IStageComponent {
|
export default function getEntryComponentForLoginType(loginType: AuthType | CustomAuthType): IStageComponent {
|
||||||
switch (loginType) {
|
switch (loginType) {
|
||||||
|
case AuthType.OAuth:
|
||||||
case CustomAuthType.MasCrossSigningReset:
|
case CustomAuthType.MasCrossSigningReset:
|
||||||
return MasUnlockCrossSigningAuthEntry;
|
return MasUnlockCrossSigningAuthEntry;
|
||||||
case AuthType.Password:
|
case AuthType.Password:
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2026 Element Creations Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { fireEvent, render, type RenderResult } from "jest-matrix-react";
|
||||||
|
|
||||||
|
import InteractiveAuthComponent from "../../../../../src/components/structures/InteractiveAuth";
|
||||||
|
import { getMockClientWithEventEmitter, unmockClientPeg } from "../../../../test-utils";
|
||||||
|
|
||||||
|
describe("InteractiveAuthComponent", function () {
|
||||||
|
const mockClient = getMockClientWithEventEmitter({
|
||||||
|
generateClientSecret: jest.fn().mockReturnValue("t35tcl1Ent5ECr3T"),
|
||||||
|
getDomain: jest.fn().mockReturnValue("test.local"),
|
||||||
|
});
|
||||||
|
const authUrl = "https://test.local/oauth?action=foo";
|
||||||
|
const onAuthFinished = jest.fn();
|
||||||
|
const makeRequest = jest.fn().mockResolvedValue({ a: 1 });
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
matrixClient: mockClient,
|
||||||
|
makeRequest: jest.fn().mockResolvedValue(undefined),
|
||||||
|
onAuthFinished: jest.fn(),
|
||||||
|
};
|
||||||
|
const getComponent = (props = {}) => render(<InteractiveAuthComponent {...defaultProps} {...props} />);
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
jest.spyOn(global.window, "open").mockImplementation();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
unmockClientPeg();
|
||||||
|
});
|
||||||
|
|
||||||
|
const getSubmitButton = ({ container }: RenderResult) => container.querySelector(".mx_Dialog_nonDialogButton");
|
||||||
|
|
||||||
|
it("should use an m.oauth stage", async () => {
|
||||||
|
const authData = {
|
||||||
|
session: "sess",
|
||||||
|
flows: [{ stages: ["m.oauth"] }],
|
||||||
|
params: {
|
||||||
|
"m.oauth": {
|
||||||
|
url: authUrl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = getComponent({ makeRequest, onAuthFinished, authData });
|
||||||
|
|
||||||
|
const submitNode = getSubmitButton(wrapper);
|
||||||
|
expect(submitNode).toBeTruthy();
|
||||||
|
|
||||||
|
// click button; should trigger the auth URL to be opened
|
||||||
|
fireEvent.click(submitNode!);
|
||||||
|
|
||||||
|
expect(global.window.open).toHaveBeenCalledWith(authUrl, "_blank");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use an unstable org.matrix.cross_signing_reset stage", async () => {
|
||||||
|
const authData = {
|
||||||
|
session: "sess",
|
||||||
|
flows: [{ stages: ["org.matrix.cross_signing_reset"] }],
|
||||||
|
params: {
|
||||||
|
"org.matrix.cross_signing_reset": {
|
||||||
|
url: authUrl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = getComponent({ makeRequest, onAuthFinished, authData });
|
||||||
|
|
||||||
|
const submitNode = getSubmitButton(wrapper);
|
||||||
|
expect(submitNode).toBeTruthy();
|
||||||
|
|
||||||
|
// click button; should trigger the auth URL to be opened
|
||||||
|
fireEvent.click(submitNode!);
|
||||||
|
|
||||||
|
expect(global.window.open).toHaveBeenCalledWith(authUrl, "_blank");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use the first flow when both stable and unstable are present", async () => {
|
||||||
|
const authData = {
|
||||||
|
session: "sess",
|
||||||
|
flows: [{ stages: ["org.matrix.cross_signing_reset"] }, { stages: ["m.oauth"] }],
|
||||||
|
params: {
|
||||||
|
"org.matrix.cross_signing_reset": {
|
||||||
|
url: authUrl,
|
||||||
|
},
|
||||||
|
"m.oauth": {
|
||||||
|
url: "https://should.not.be/opened",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = getComponent({ makeRequest, onAuthFinished, authData });
|
||||||
|
|
||||||
|
const submitNode = getSubmitButton(wrapper);
|
||||||
|
expect(submitNode).toBeTruthy();
|
||||||
|
|
||||||
|
// click button; should trigger the auth URL to be opened
|
||||||
|
fireEvent.click(submitNode!);
|
||||||
|
|
||||||
|
expect(global.window.open).toHaveBeenCalledWith(authUrl, "_blank");
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user