Skip to content

Commit

Permalink
Remove abandoned MSC3886, MSC3903, MSC3906 implementations (#28274)
Browse files Browse the repository at this point in the history
* Remove abandoned MSC3886, MSC3903, MSC3906 implementations

Signed-off-by: Michael Telatynski <[email protected]>

* Iterate

Signed-off-by: Michael Telatynski <[email protected]>

* Remove stale snapshots

* Improve coverage

Signed-off-by: Michael Telatynski <[email protected]>

---------

Signed-off-by: Michael Telatynski <[email protected]>
  • Loading branch information
t3chguy authored Oct 24, 2024
1 parent 6d0d237 commit 5b5348e
Show file tree
Hide file tree
Showing 13 changed files with 60 additions and 1,373 deletions.
4 changes: 0 additions & 4 deletions src/components/views/auth/LoginWithQR-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ export enum Phase {
WaitingForDevice,
Verifying,
Error,
/**
* @deprecated the MSC3906 implementation is deprecated in favour of MSC4108.
*/
LegacyConnected,
}

export enum Click {
Expand Down
161 changes: 20 additions & 141 deletions src/components/views/auth/LoginWithQR.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ Please see LICENSE files in the repository root for full details.
import React from "react";
import {
ClientRendezvousFailureReason,
LegacyRendezvousFailureReason,
MSC3886SimpleHttpRendezvousTransport,
MSC3903ECDHPayload,
MSC3903ECDHv2RendezvousChannel,
MSC3906Rendezvous,
MSC4108FailureReason,
MSC4108RendezvousSession,
MSC4108SecureChannel,
Expand All @@ -23,29 +18,21 @@ import {
RendezvousIntent,
} from "matrix-js-sdk/src/rendezvous";
import { logger } from "matrix-js-sdk/src/logger";
import { HTTPError, MatrixClient } from "matrix-js-sdk/src/matrix";
import { MatrixClient } from "matrix-js-sdk/src/matrix";

import { Click, Mode, Phase } from "./LoginWithQR-types";
import LoginWithQRFlow from "./LoginWithQRFlow";
import { wrapRequestWithDialog } from "../../../utils/UserInteractiveAuth";
import { _t } from "../../../languageHandler";

interface IProps {
client: MatrixClient;
mode: Mode;
legacy: boolean;
onFinished(...args: any): void;
}

interface IState {
phase: Phase;
rendezvous?: MSC3906Rendezvous | MSC4108SignInWithQR;
rendezvous?: MSC4108SignInWithQR;
mediaPermissionError?: boolean;

// MSC3906
confirmationDigits?: string;

// MSC4108
verificationUri?: string;
userCode?: string;
checkCode?: string;
Expand All @@ -54,25 +41,18 @@ interface IState {
}

export enum LoginWithQRFailureReason {
/**
* @deprecated the MSC3906 implementation is deprecated in favour of MSC4108.
*/
RateLimited = "rate_limited",
CheckCodeMismatch = "check_code_mismatch",
}

export type FailureReason = RendezvousFailureReason | LoginWithQRFailureReason;

// n.b MSC3886/MSC3903/MSC3906 that this is based on are now closed.
// However, we want to keep this implementation around for some time.
// TODO: define an end-of-life date for this implementation.

/**
* A component that allows sign in and E2EE set up with a QR code.
*
* It implements `login.reciprocate` capabilities and showing QR codes.
*
* This uses the unstable feature of MSC3906: https://github.com/matrix-org/matrix-spec-proposals/pull/3906
* This uses the unstable feature of MSC4108: https://github.com/matrix-org/matrix-spec-proposals/pull/4108
*/
export default class LoginWithQR extends React.Component<IProps, IState> {
private finished = false;
Expand Down Expand Up @@ -104,9 +84,6 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
if (this.state.rendezvous) {
const rendezvous = this.state.rendezvous;
rendezvous.onFailure = undefined;
if (rendezvous instanceof MSC3906Rendezvous) {
await rendezvous.cancel(LegacyRendezvousFailureReason.UserCancelled);
}
this.setState({ rendezvous: undefined });
}
if (mode === Mode.Show) {
Expand All @@ -119,60 +96,7 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
// eslint-disable-next-line react/no-direct-mutation-state
this.state.rendezvous.onFailure = undefined;
// calling cancel will call close() as well to clean up the resources
if (this.state.rendezvous instanceof MSC3906Rendezvous) {
this.state.rendezvous.cancel(LegacyRendezvousFailureReason.UserCancelled);
} else {
this.state.rendezvous.cancel(MSC4108FailureReason.UserCancelled);
}
}
}

private async legacyApproveLogin(): Promise<void> {
if (!(this.state.rendezvous instanceof MSC3906Rendezvous)) {
throw new Error("Rendezvous not found");
}
if (!this.props.client) {
throw new Error("No client to approve login with");
}
this.setState({ phase: Phase.Loading });

try {
logger.info("Requesting login token");

const { login_token: loginToken } = await wrapRequestWithDialog(this.props.client.requestLoginToken, {
matrixClient: this.props.client,
title: _t("auth|qr_code_login|sign_in_new_device"),
})();

this.setState({ phase: Phase.WaitingForDevice });

const newDeviceId = await this.state.rendezvous.approveLoginOnExistingDevice(loginToken);
if (!newDeviceId) {
// user denied
return;
}
if (!this.props.client.getCrypto()) {
// no E2EE to set up
this.onFinished(true);
return;
}
this.setState({ phase: Phase.Verifying });
await this.state.rendezvous.verifyNewDeviceOnExistingDevice();
// clean up our state:
try {
await this.state.rendezvous.close();
} finally {
this.setState({ rendezvous: undefined });
}
this.onFinished(true);
} catch (e) {
logger.error("Error whilst approving sign in", e);
if (e instanceof HTTPError && e.httpStatus === 429) {
// 429: rate limit
this.setState({ phase: Phase.Error, failureReason: LoginWithQRFailureReason.RateLimited });
return;
}
this.setState({ phase: Phase.Error, failureReason: ClientRendezvousFailureReason.Unknown });
this.state.rendezvous.cancel(MSC4108FailureReason.UserCancelled);
}
}

Expand All @@ -182,28 +106,18 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
}

private generateAndShowCode = async (): Promise<void> => {
let rendezvous: MSC4108SignInWithQR | MSC3906Rendezvous;
let rendezvous: MSC4108SignInWithQR;
try {
const fallbackRzServer = this.props.client?.getClientWellKnown()?.["io.element.rendezvous"]?.server;

if (this.props.legacy) {
const transport = new MSC3886SimpleHttpRendezvousTransport<MSC3903ECDHPayload>({
onFailure: this.onFailure,
client: this.props.client,
fallbackRzServer,
});
const channel = new MSC3903ECDHv2RendezvousChannel(transport, undefined, this.onFailure);
rendezvous = new MSC3906Rendezvous(channel, this.props.client, this.onFailure);
} else {
const transport = new MSC4108RendezvousSession({
onFailure: this.onFailure,
client: this.props.client,
fallbackRzServer,
});
await transport.send("");
const channel = new MSC4108SecureChannel(transport, undefined, this.onFailure);
rendezvous = new MSC4108SignInWithQR(channel, false, this.props.client, this.onFailure);
}
const transport = new MSC4108RendezvousSession({
onFailure: this.onFailure,
client: this.props.client,
fallbackRzServer,
});
await transport.send("");
const channel = new MSC4108SecureChannel(transport, undefined, this.onFailure);
rendezvous = new MSC4108SignInWithQR(channel, false, this.props.client, this.onFailure);

await rendezvous.generateCode();
this.setState({
Expand All @@ -218,10 +132,7 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
}

try {
if (rendezvous instanceof MSC3906Rendezvous) {
const confirmationDigits = await rendezvous.startAfterShowingCode();
this.setState({ phase: Phase.LegacyConnected, confirmationDigits });
} else if (this.ourIntent === RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE) {
if (this.ourIntent === RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE) {
// MSC4108-Flow: NewScanned
await rendezvous.negotiateProtocols();
const { verificationUri } = await rendezvous.deviceAuthorizationGrant();
Expand All @@ -234,18 +145,9 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
// we ask the user to confirm that the channel is secure
} catch (e: RendezvousError | unknown) {
logger.error("Error whilst approving login", e);
if (rendezvous instanceof MSC3906Rendezvous) {
// only set to error phase if it hasn't already been set by onFailure or similar
if (this.state.phase !== Phase.Error) {
this.setState({ phase: Phase.Error, failureReason: LegacyRendezvousFailureReason.Unknown });
}
} else {
await rendezvous?.cancel(
e instanceof RendezvousError
? (e.code as MSC4108FailureReason)
: ClientRendezvousFailureReason.Unknown,
);
}
await rendezvous?.cancel(
e instanceof RendezvousError ? (e.code as MSC4108FailureReason) : ClientRendezvousFailureReason.Unknown,
);
}
};

Expand Down Expand Up @@ -298,7 +200,6 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
public reset(): void {
this.setState({
rendezvous: undefined,
confirmationDigits: undefined,
verificationUri: undefined,
failureReason: undefined,
userCode: undefined,
Expand All @@ -311,28 +212,20 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
private onClick = async (type: Click, checkCode?: string): Promise<void> => {
switch (type) {
case Click.Cancel:
if (this.state.rendezvous instanceof MSC3906Rendezvous) {
await this.state.rendezvous?.cancel(LegacyRendezvousFailureReason.UserCancelled);
} else {
await this.state.rendezvous?.cancel(MSC4108FailureReason.UserCancelled);
}
await this.state.rendezvous?.cancel(MSC4108FailureReason.UserCancelled);
this.reset();
this.onFinished(false);
break;
case Click.Approve:
await (this.props.legacy ? this.legacyApproveLogin() : this.approveLogin(checkCode));
await this.approveLogin(checkCode);
break;
case Click.Decline:
await this.state.rendezvous?.declineLoginOnExistingDevice();
this.reset();
this.onFinished(false);
break;
case Click.Back:
if (this.state.rendezvous instanceof MSC3906Rendezvous) {
await this.state.rendezvous?.cancel(LegacyRendezvousFailureReason.UserCancelled);
} else {
await this.state.rendezvous?.cancel(MSC4108FailureReason.UserCancelled);
}
await this.state.rendezvous?.cancel(MSC4108FailureReason.UserCancelled);
this.onFinished(false);
break;
case Click.ShowQr:
Expand All @@ -342,20 +235,6 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
};

public render(): React.ReactNode {
if (this.state.rendezvous instanceof MSC3906Rendezvous) {
return (
<LoginWithQRFlow
onClick={this.onClick}
phase={this.state.phase}
code={this.state.phase === Phase.ShowingQR ? this.state.rendezvous?.code : undefined}
confirmationDigits={
this.state.phase === Phase.LegacyConnected ? this.state.confirmationDigits : undefined
}
failureReason={this.state.failureReason}
/>
);
}

return (
<LoginWithQRFlow
onClick={this.onClick}
Expand Down
Loading

0 comments on commit 5b5348e

Please sign in to comment.