/* Copyright 2024 New Vector Ltd. Copyright 2020 The Matrix.org Foundation C.I.C. Copyright 2017 Vector Creations Ltd Copyright 2016 OpenMarket Ltd SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ import React from "react"; import { MatrixClient, UIAResponse } from "matrix-js-sdk/src/matrix"; import { AuthType } from "matrix-js-sdk/src/interactive-auth"; import { _t } from "../../../languageHandler"; import AccessibleButton from "../elements/AccessibleButton"; import InteractiveAuth, { ERROR_USER_CANCELLED, InteractiveAuthCallback, InteractiveAuthProps, } from "../../structures/InteractiveAuth"; import { ContinueKind, SSOAuthEntry } from "../auth/InteractiveAuthEntryComponents"; import BaseDialog from "./BaseDialog"; import { Linkify } from "../../../Linkify"; type DialogAesthetics = Partial<{ [x in AuthType]: { [x: number]: { title: string; body: string; continueText: string; continueKind: ContinueKind; }; }; }>; export interface InteractiveAuthDialogProps extends Pick, "makeRequest" | "authData"> { // matrix client to use for UI auth requests matrixClient: MatrixClient; // Optional title and body to show when not showing a particular stage title?: string; body?: string; // Optional title and body pairs for particular stages and phases within // those stages. Object structure/example is: // { // "org.example.stage_type": { // 1: { // "body": "This is a body for phase 1" of org.example.stage_type, // "title": "Title for phase 1 of org.example.stage_type" // }, // 2: { // "body": "This is a body for phase 2 of org.example.stage_type", // "title": "Title for phase 2 of org.example.stage_type" // "continueText": "Confirm identity with Example Auth", // "continueKind": "danger" // } // } // } // // Default is defined in _getDefaultDialogAesthetics() aestheticsForStagePhases?: DialogAesthetics; onFinished(success?: boolean, result?: UIAResponse | Error | null): void; } interface IState { authError: Error | null; // See _onUpdateStagePhase() uiaStage: AuthType | null; uiaStagePhase: number | null; } export default class InteractiveAuthDialog extends React.Component, IState> { public constructor(props: InteractiveAuthDialogProps) { super(props); this.state = { authError: null, // See _onUpdateStagePhase() uiaStage: null, uiaStagePhase: null, }; } private getDefaultDialogAesthetics(): DialogAesthetics { const ssoAesthetics = { [SSOAuthEntry.PHASE_PREAUTH]: { title: _t("auth|uia|sso_title"), body: _t("auth|uia|sso_preauth_body"), continueText: _t("auth|sso"), continueKind: "primary", }, [SSOAuthEntry.PHASE_POSTAUTH]: { title: _t("auth|uia|sso_postauth_title"), body: _t("auth|uia|sso_postauth_body"), continueText: _t("action|confirm"), continueKind: "primary", }, }; return { [SSOAuthEntry.LOGIN_TYPE]: ssoAesthetics, [SSOAuthEntry.UNSTABLE_LOGIN_TYPE]: ssoAesthetics, }; } private onAuthFinished: InteractiveAuthCallback = async (success, result): Promise => { if (success) { this.props.onFinished(true, result); } else { if (result === ERROR_USER_CANCELLED) { this.props.onFinished(false, null); } else { this.setState({ authError: result as Error, }); } } }; private onUpdateStagePhase = (newStage: AuthType, newPhase: number): void => { // We copy the stage and stage phase params into state for title selection in render() this.setState({ uiaStage: newStage, uiaStagePhase: newPhase }); }; private onDismissClick = (): void => { this.props.onFinished(false); }; public render(): React.ReactNode { // Let's pick a title, body, and other params text that we'll show to the user. The order // is most specific first, so stagePhase > our props > defaults. let title = this.state.authError ? "Error" : this.props.title || _t("common|authentication"); let body = this.state.authError ? null : this.props.body; let continueText: string | undefined; let continueKind: ContinueKind | undefined; const dialogAesthetics = this.props.aestheticsForStagePhases || this.getDefaultDialogAesthetics(); if (!this.state.authError && dialogAesthetics) { if ( this.state.uiaStage !== null && this.state.uiaStagePhase !== null && dialogAesthetics[this.state.uiaStage] ) { const aesthetics = dialogAesthetics[this.state.uiaStage]![this.state.uiaStagePhase]; if (aesthetics) { if (aesthetics.title) title = aesthetics.title; if (aesthetics.body) body = aesthetics.body; if (aesthetics.continueText) continueText = aesthetics.continueText; if (aesthetics.continueKind) continueKind = aesthetics.continueKind; } } } let content: JSX.Element; if (this.state.authError) { content = (
{this.state.authError.message || this.state.authError.toString()}

{_t("action|dismiss")}
); } else { content = (
{body}
); } return ( {content} ); } }