From e6673bca1bbbd4839d97645e1462b4fc761af611 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 Jan 2021 12:50:12 +0000 Subject: [PATCH 1/8] Improve SSO auth flow Use replaceState instead of a redirect to strip the loginToken Put user into the same post-auth flows of E2ESetup Skip UIA prompt in this post-auth flow, happy path is a server grace period --- src/Lifecycle.ts | 2 +- src/components/structures/MatrixChat.tsx | 81 ++++++++++--------- src/components/structures/auth/E2eSetup.js | 2 + .../security/CreateCrossSigningDialog.js | 10 +++ 4 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index f87af1a791..4602f2b5bb 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -366,7 +366,7 @@ async function abortLogin() { // The plan is to gradually move the localStorage access done here into // SessionStore to avoid bugs where the view becomes out-of-sync with // localStorage (e.g. isGuest etc.) -async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }): Promise { +export async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }): Promise { const ignoreGuest = opts?.ignoreGuest; if (!localStorage) { diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 62c729c422..a399d92eaf 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -218,6 +218,7 @@ export default class MatrixChat extends React.PureComponent { private screenAfterLogin?: IScreen; private windowWidth: number; private pageChanging: boolean; + private tokenLogin?: boolean; private accountPassword?: string; private accountPasswordTimer?: NodeJS.Timeout; private focusComposer: boolean; @@ -323,13 +324,16 @@ export default class MatrixChat extends React.PureComponent { Lifecycle.attemptTokenLogin( this.props.realQueryParams, this.props.defaultDeviceDisplayName, - ).then((loggedIn) => { + ).then(async (loggedIn) => { if (loggedIn) { + this.tokenLogin = true; this.props.onTokenLoginCompleted(); - // don't do anything else until the page reloads - just stay in - // the 'loading' state. - return; + // Create and start the client + await Lifecycle.restoreFromLocalStorage({ + ignoreGuest: true, + }); + return this.postLoginSetup(); } // if the user has followed a login or register link, don't reanimate @@ -353,6 +357,39 @@ export default class MatrixChat extends React.PureComponent { CountlyAnalytics.instance.enable(/* anonymous = */ true); } + private async postLoginSetup() { + const cli = MatrixClientPeg.get(); + const cryptoEnabled = cli.isCryptoEnabled(); + + const promisesList = [this.firstSyncPromise.promise]; + if (cryptoEnabled) { + // wait for the client to finish downloading cross-signing keys for us so we + // know whether or not we have keys set up on this account + promisesList.push(cli.downloadKeys([cli.getUserId()])); + } + + // Now update the state to say we're waiting for the first sync to complete rather + // than for the login to finish. + this.setState({ pendingInitialSync: true }); + + await Promise.all(promisesList); + + if (!cryptoEnabled) { + this.setState({ pendingInitialSync: false }); + return; + } + + const crossSigningIsSetUp = cli.getStoredCrossSigningForUser(cli.getUserId()); + if (crossSigningIsSetUp) { + this.setStateForNewView({ view: Views.COMPLETE_SECURITY }); + } else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) { + this.setStateForNewView({ view: Views.E2E_SETUP }); + } else { + this.onLoggedIn(); + } + this.setState({ pendingInitialSync: false }); + } + // TODO: [REACT-WARNING] Replace with appropriate lifecycle stage // eslint-disable-next-line camelcase UNSAFE_componentWillUpdate(props, state) { @@ -1833,40 +1870,7 @@ export default class MatrixChat extends React.PureComponent { // Create and start the client await Lifecycle.setLoggedIn(credentials); - - const cli = MatrixClientPeg.get(); - const cryptoEnabled = cli.isCryptoEnabled(); - if (!cryptoEnabled) { - this.onLoggedIn(); - } - - const promisesList = [this.firstSyncPromise.promise]; - if (cryptoEnabled) { - // wait for the client to finish downloading cross-signing keys for us so we - // know whether or not we have keys set up on this account - promisesList.push(cli.downloadKeys([cli.getUserId()])); - } - - // Now update the state to say we're waiting for the first sync to complete rather - // than for the login to finish. - this.setState({ pendingInitialSync: true }); - - await Promise.all(promisesList); - - if (!cryptoEnabled) { - this.setState({ pendingInitialSync: false }); - return; - } - - const crossSigningIsSetUp = cli.getStoredCrossSigningForUser(cli.getUserId()); - if (crossSigningIsSetUp) { - this.setStateForNewView({ view: Views.COMPLETE_SECURITY }); - } else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) { - this.setStateForNewView({ view: Views.E2E_SETUP }); - } else { - this.onLoggedIn(); - } - this.setState({ pendingInitialSync: false }); + await this.postLoginSetup(); }; // complete security / e2e setup has finished @@ -1910,6 +1914,7 @@ export default class MatrixChat extends React.PureComponent { ); } else if (this.state.view === Views.LOGGED_IN) { diff --git a/src/components/structures/auth/E2eSetup.js b/src/components/structures/auth/E2eSetup.js index 6df8158002..d97a972718 100644 --- a/src/components/structures/auth/E2eSetup.js +++ b/src/components/structures/auth/E2eSetup.js @@ -24,6 +24,7 @@ export default class E2eSetup extends React.Component { static propTypes = { onFinished: PropTypes.func.isRequired, accountPassword: PropTypes.string, + tokenLogin: PropTypes.bool, }; render() { @@ -33,6 +34,7 @@ export default class E2eSetup extends React.Component { diff --git a/src/components/views/dialogs/security/CreateCrossSigningDialog.js b/src/components/views/dialogs/security/CreateCrossSigningDialog.js index 226419e759..be546d2616 100644 --- a/src/components/views/dialogs/security/CreateCrossSigningDialog.js +++ b/src/components/views/dialogs/security/CreateCrossSigningDialog.js @@ -34,6 +34,7 @@ import InteractiveAuthDialog from '../InteractiveAuthDialog'; export default class CreateCrossSigningDialog extends React.PureComponent { static propTypes = { accountPassword: PropTypes.string, + tokenLogin: PropTypes.bool, }; constructor(props) { @@ -96,6 +97,9 @@ export default class CreateCrossSigningDialog extends React.PureComponent { user: MatrixClientPeg.get().getUserId(), password: this.state.accountPassword, }); + } else if (this.props.tokenLogin) { + // We are hoping the grace period is active + await makeRequest({}); } else { const dialogAesthetics = { [SSOAuthEntry.PHASE_PREAUTH]: { @@ -144,6 +148,12 @@ export default class CreateCrossSigningDialog extends React.PureComponent { }); this.props.onFinished(true); } catch (e) { + if (this.props.tokenLogin) { + // ignore any failures, we are relying on grace period here + this.props.onFinished(); + return; + } + this.setState({ error: e }); console.error("Error bootstrapping cross-signing", e); } From 34ae766893f4c0aa31a6a5b3c5653c567a03f387 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 Jan 2021 14:27:41 +0000 Subject: [PATCH 2/8] Wire up MSC2858 brand attribute For better brand adherance. Also removes old support for https IdP icons. --- res/css/views/elements/_SSOButtons.scss | 18 ++++++++++ res/img/element-icons/brands/apple.svg | 3 ++ res/img/element-icons/brands/facebook.svg | 4 +++ res/img/element-icons/brands/github.svg | 3 ++ res/img/element-icons/brands/gitlab.svg | 9 +++++ res/img/element-icons/brands/google.svg | 6 ++++ res/img/element-icons/brands/twitter.svg | 3 ++ src/Login.ts | 10 ++++++ src/components/views/elements/SSOButtons.tsx | 38 +++++++++++++++----- 9 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 res/img/element-icons/brands/apple.svg create mode 100644 res/img/element-icons/brands/facebook.svg create mode 100644 res/img/element-icons/brands/github.svg create mode 100644 res/img/element-icons/brands/gitlab.svg create mode 100644 res/img/element-icons/brands/google.svg create mode 100644 res/img/element-icons/brands/twitter.svg diff --git a/res/css/views/elements/_SSOButtons.scss b/res/css/views/elements/_SSOButtons.scss index c61247655c..d20f2cafce 100644 --- a/res/css/views/elements/_SSOButtons.scss +++ b/res/css/views/elements/_SSOButtons.scss @@ -30,6 +30,8 @@ limitations under the License. width: 100%; padding-left: 32px; padding-right: 32px; + border-color: $input-border-color; + color: $primary-fg-color; > img { object-fit: contain; @@ -56,3 +58,19 @@ limitations under the License. } } } + +.mx_SSOButton.mx_SSOButton_brand_facebook { + background-color: #3c5a99; + border-color: #3c5a99; + color: #ffffff; +} +.mx_SSOButton.mx_SSOButton_brand_google { + background-color: #eb4335; + border-color: #eb4335; + color: #ffffff; +} +.mx_SSOButton.mx_SSOButton_brand_twitter { + background-color: #47acdf; + border-color: #47acdf; + color: #ffffff; +} diff --git a/res/img/element-icons/brands/apple.svg b/res/img/element-icons/brands/apple.svg new file mode 100644 index 0000000000..308c3c5d5a --- /dev/null +++ b/res/img/element-icons/brands/apple.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/element-icons/brands/facebook.svg b/res/img/element-icons/brands/facebook.svg new file mode 100644 index 0000000000..087ddacdff --- /dev/null +++ b/res/img/element-icons/brands/facebook.svg @@ -0,0 +1,4 @@ + + + + diff --git a/res/img/element-icons/brands/github.svg b/res/img/element-icons/brands/github.svg new file mode 100644 index 0000000000..503719520b --- /dev/null +++ b/res/img/element-icons/brands/github.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/element-icons/brands/gitlab.svg b/res/img/element-icons/brands/gitlab.svg new file mode 100644 index 0000000000..df84c41e21 --- /dev/null +++ b/res/img/element-icons/brands/gitlab.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/res/img/element-icons/brands/google.svg b/res/img/element-icons/brands/google.svg new file mode 100644 index 0000000000..25f2bb7f0a --- /dev/null +++ b/res/img/element-icons/brands/google.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/res/img/element-icons/brands/twitter.svg b/res/img/element-icons/brands/twitter.svg new file mode 100644 index 0000000000..4fc3d2f2a2 --- /dev/null +++ b/res/img/element-icons/brands/twitter.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/Login.ts b/src/Login.ts index 6493b244e0..aecc0493c7 100644 --- a/src/Login.ts +++ b/src/Login.ts @@ -33,10 +33,20 @@ interface IPasswordFlow { type: "m.login.password"; } +export enum IdentityProviderBrand { + Gitlab = "org.matrix.gitlab", + Github = "org.matrix.github", + Apple = "org.matrix.apple", + Google = "org.matrix.google", + Facebook = "org.matrix.facebook", + Twitter = "org.matrix.twitter", +} + export interface IIdentityProvider { id: string; name: string; icon?: string; + brand?: IdentityProviderBrand | string; } export interface ISSOFlow { diff --git a/src/components/views/elements/SSOButtons.tsx b/src/components/views/elements/SSOButtons.tsx index 5c3098d807..cc894eaf03 100644 --- a/src/components/views/elements/SSOButtons.tsx +++ b/src/components/views/elements/SSOButtons.tsx @@ -22,13 +22,32 @@ import {MatrixClient} from "matrix-js-sdk/src/client"; import PlatformPeg from "../../../PlatformPeg"; import AccessibleButton from "./AccessibleButton"; import {_t} from "../../../languageHandler"; -import {IIdentityProvider, ISSOFlow} from "../../../Login"; +import {IdentityProviderBrand, IIdentityProvider, ISSOFlow} from "../../../Login"; interface ISSOButtonProps extends Omit { idp: IIdentityProvider; mini?: boolean; } +const getIcon = (brand: IdentityProviderBrand | string) => { + switch (brand) { + case IdentityProviderBrand.Apple: + return require(`../../../../res/img/element-icons/brands/apple.svg`); + case IdentityProviderBrand.Facebook: + return require(`../../../../res/img/element-icons/brands/facebook.svg`); + case IdentityProviderBrand.Github: + return require(`../../../../res/img/element-icons/brands/github.svg`); + case IdentityProviderBrand.Gitlab: + return require(`../../../../res/img/element-icons/brands/gitlab.svg`); + case IdentityProviderBrand.Google: + return require(`../../../../res/img/element-icons/brands/google.svg`); + case IdentityProviderBrand.Twitter: + return require(`../../../../res/img/element-icons/brands/twitter.svg`); + default: + return null; + } +} + const SSOButton: React.FC = ({ matrixClient, loginType, @@ -46,16 +65,19 @@ const SSOButton: React.FC = ({ }; let icon; - if (typeof idp?.icon === "string" && (idp.icon.startsWith("mxc://") || idp.icon.startsWith("https://"))) { - icon = {label}; + let brandClass; + const brandIcon = idp ? getIcon(idp.brand) : null; + if (brandIcon) { + const brandName = idp.brand.split(".").pop(); + brandClass = `mx_SSOButton_brand_${brandName}`; + icon = {brandName}; + } else if (typeof idp?.icon === "string" && idp.icon.startsWith("mxc://")) { + const src = matrixClient.mxcUrlToHttp(idp.icon, 24, 24, "crop", true); + icon = {idp.name}; } const classes = classNames("mx_SSOButton", { + [brandClass]: brandClass, mx_SSOButton_mini: mini, }); From 1761a4ec800624bef0b81875948cff50610324f8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 28 Jan 2021 10:45:29 +0000 Subject: [PATCH 3/8] Improve SSO UIA Fixes Fallback UIA postmessage interface Auto-closes SSO UIA tab when the user has completed the flow within it Error for when auth stage is restarted because it failed --- src/components/structures/InteractiveAuth.js | 8 ++- src/components/structures/MatrixChat.tsx | 6 +- .../auth/InteractiveAuthEntryComponents.js | 56 ++++++++++++++++--- src/i18n/strings/en_EN.json | 1 + 4 files changed, 62 insertions(+), 9 deletions(-) diff --git a/src/components/structures/InteractiveAuth.js b/src/components/structures/InteractiveAuth.js index c8fcd7e9ca..23e43da195 100644 --- a/src/components/structures/InteractiveAuth.js +++ b/src/components/structures/InteractiveAuth.js @@ -177,7 +177,13 @@ export default class InteractiveAuthComponent extends React.Component { stageState: stageState, errorText: stageState.error, }, () => { - if (oldStage != stageType) this._setFocus(); + if (oldStage !== stageType) { + this._setFocus(); + } else if (!stageState.error && this._stageComponent.current && + this._stageComponent.current.attemptFailed + ) { + this._stageComponent.current.attemptFailed(); + } }); }; diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index a399d92eaf..2f6c5bf353 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -325,9 +325,13 @@ export default class MatrixChat extends React.PureComponent { this.props.realQueryParams, this.props.defaultDeviceDisplayName, ).then(async (loggedIn) => { + if (this.props.realQueryParams?.loginToken) { + // remove the loginToken from the URL regardless + this.props.onTokenLoginCompleted(); + } + if (loggedIn) { this.tokenLogin = true; - this.props.onTokenLoginCompleted(); // Create and start the client await Lifecycle.restoreFromLocalStorage({ diff --git a/src/components/views/auth/InteractiveAuthEntryComponents.js b/src/components/views/auth/InteractiveAuthEntryComponents.js index 60e57afc98..c8469192d2 100644 --- a/src/components/views/auth/InteractiveAuthEntryComponents.js +++ b/src/components/views/auth/InteractiveAuthEntryComponents.js @@ -609,8 +609,12 @@ export class SSOAuthEntry extends React.Component { this.props.authSessionId, ); + this._popupWindow = null; + window.addEventListener("message", this._onReceiveMessage); + this.state = { phase: SSOAuthEntry.PHASE_PREAUTH, + attemptFailed: false, }; } @@ -618,12 +622,33 @@ export class SSOAuthEntry extends React.Component { this.props.onPhaseChange(SSOAuthEntry.PHASE_PREAUTH); } + componentWillUnmount() { + window.removeEventListener("message", this._onReceiveMessage); + if (this._popupWindow) { + this._popupWindow.close(); + } + } + + attemptFailed = () => { + this.setState({ + attemptFailed: true, + }); + }; + + _onReceiveMessage = event => { + if (event.data === "authDone" && event.origin === this.props.matrixClient.getHomeserverUrl()) { + if (this._popupWindow) { + this._popupWindow.close(); + } + } + }; + onStartAuthClick = () => { // Note: We don't use PlatformPeg's startSsoAuth functions because we almost // certainly will need to open the thing in a new tab to avoid losing application // context. - window.open(this._ssoUrl, '_blank'); + this._popupWindow = window.open(this._ssoUrl, "_blank"); this.setState({phase: SSOAuthEntry.PHASE_POSTAUTH}); this.props.onPhaseChange(SSOAuthEntry.PHASE_POSTAUTH); }; @@ -656,10 +681,28 @@ export class SSOAuthEntry extends React.Component { ); } - return
- {cancelButton} - {continueButton} -
; + let errorSection; + if (this.props.errorText) { + errorSection = ( +
+ { this.props.errorText } +
+ ); + } else if (this.state.attemptFailed) { + errorSection = ( +
+ { _t("Something went wrong in confirming your identity. Cancel and try again.") } +
+ ); + } + + return + { errorSection } +
+ {cancelButton} + {continueButton} +
+
; } } @@ -710,8 +753,7 @@ export class FallbackAuthEntry extends React.Component { this.props.loginType, this.props.authSessionId, ); - this._popupWindow = window.open(url); - this._popupWindow.opener = null; + this._popupWindow = window.open(url, "_blank"); }; _onReceiveMessage = event => { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 8d047ea3f1..cc69a66d18 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2334,6 +2334,7 @@ "Please enter the code it contains:": "Please enter the code it contains:", "Code": "Code", "Submit": "Submit", + "Something went wrong in confirming your identity. Cancel and try again.": "Something went wrong in confirming your identity. Cancel and try again.", "Start authentication": "Start authentication", "Enter password": "Enter password", "Nice, strong password!": "Nice, strong password!", From fd4109b8ad61e37f089d12b2d421a85b67522cdc Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 28 Jan 2021 11:00:04 +0000 Subject: [PATCH 4/8] fix tests by reverting if removal --- src/components/structures/MatrixChat.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 2f6c5bf353..9520815134 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -364,6 +364,9 @@ export default class MatrixChat extends React.PureComponent { private async postLoginSetup() { const cli = MatrixClientPeg.get(); const cryptoEnabled = cli.isCryptoEnabled(); + if (!cryptoEnabled) { + this.onLoggedIn(); + } const promisesList = [this.firstSyncPromise.promise]; if (cryptoEnabled) { From 2c2cf4c0735947858c1581543f97f6be3ec1c467 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 29 Jan 2021 11:42:34 +0000 Subject: [PATCH 5/8] Update google branding --- res/css/views/elements/_SSOButtons.scss | 5 ----- res/img/element-icons/brands/google.svg | 8 ++++---- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/res/css/views/elements/_SSOButtons.scss b/res/css/views/elements/_SSOButtons.scss index d20f2cafce..ff6545045a 100644 --- a/res/css/views/elements/_SSOButtons.scss +++ b/res/css/views/elements/_SSOButtons.scss @@ -64,11 +64,6 @@ limitations under the License. border-color: #3c5a99; color: #ffffff; } -.mx_SSOButton.mx_SSOButton_brand_google { - background-color: #eb4335; - border-color: #eb4335; - color: #ffffff; -} .mx_SSOButton.mx_SSOButton_brand_twitter { background-color: #47acdf; border-color: #47acdf; diff --git a/res/img/element-icons/brands/google.svg b/res/img/element-icons/brands/google.svg index 25f2bb7f0a..1b0b19ae5b 100644 --- a/res/img/element-icons/brands/google.svg +++ b/res/img/element-icons/brands/google.svg @@ -1,6 +1,6 @@ - - - - + + + + From 1eb88c988cc3c2f2b593357ed6a78f34baca637c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 29 Jan 2021 12:05:15 +0000 Subject: [PATCH 6/8] Add tooltips to mini sso buttons --- src/components/views/elements/SSOButtons.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/SSOButtons.tsx b/src/components/views/elements/SSOButtons.tsx index cc894eaf03..80e8a4956e 100644 --- a/src/components/views/elements/SSOButtons.tsx +++ b/src/components/views/elements/SSOButtons.tsx @@ -23,6 +23,7 @@ import PlatformPeg from "../../../PlatformPeg"; import AccessibleButton from "./AccessibleButton"; import {_t} from "../../../languageHandler"; import {IdentityProviderBrand, IIdentityProvider, ISSOFlow} from "../../../Login"; +import AccessibleTooltipButton from "./AccessibleTooltipButton"; interface ISSOButtonProps extends Omit { idp: IIdentityProvider; @@ -84,9 +85,9 @@ const SSOButton: React.FC = ({ if (mini) { // TODO fallback icon return ( - + { icon } - + ); } From 0167c3cefbc8f7e1ba1be0c7473b606504e9480f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 29 Jan 2021 13:04:05 +0000 Subject: [PATCH 7/8] Iterate SSO auth ux styling --- res/css/views/auth/_AuthBody.scss | 2 +- res/css/views/elements/_SSOButtons.scss | 20 +++++++++++++++++--- src/components/views/elements/SSOButtons.tsx | 7 ++++--- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/res/css/views/auth/_AuthBody.scss b/res/css/views/auth/_AuthBody.scss index 8f0c758e7a..90dca32e48 100644 --- a/res/css/views/auth/_AuthBody.scss +++ b/res/css/views/auth/_AuthBody.scss @@ -34,7 +34,7 @@ limitations under the License. h3 { font-size: $font-14px; font-weight: 600; - color: $authpage-primary-color; + color: $authpage-secondary-color; } h3.mx_AuthBody_centered { diff --git a/res/css/views/elements/_SSOButtons.scss b/res/css/views/elements/_SSOButtons.scss index ff6545045a..add048efb0 100644 --- a/res/css/views/elements/_SSOButtons.scss +++ b/res/css/views/elements/_SSOButtons.scss @@ -28,9 +28,13 @@ limitations under the License. .mx_SSOButton { position: relative; width: 100%; - padding-left: 32px; - padding-right: 32px; - border-color: $input-border-color; + padding: 7px 32px; + text-align: center; + border-radius: 8px; + display: inline-block; + font-size: $font-14px; + font-weight: $font-semi-bold; + border: 1px solid $input-border-color; color: $primary-fg-color; > img { @@ -41,6 +45,16 @@ limitations under the License. } } + .mx_SSOButton_default { + color: $button-primary-bg-color; + background-color: $button-secondary-bg-color; + border-color: $button-primary-bg-color; + } + .mx_SSOButton_default.mx_SSOButton_primary { + color: $button-primary-fg-color; + background-color: $button-primary-bg-color; + } + .mx_SSOButton_mini { box-sizing: border-box; width: 50px; // 48px + 1px border on all sides diff --git a/src/components/views/elements/SSOButtons.tsx b/src/components/views/elements/SSOButtons.tsx index 80e8a4956e..3a03252ebd 100644 --- a/src/components/views/elements/SSOButtons.tsx +++ b/src/components/views/elements/SSOButtons.tsx @@ -58,7 +58,6 @@ const SSOButton: React.FC = ({ mini, ...props }) => { - const kind = primary ? "primary" : "primary_outline"; const label = idp ? _t("Continue with %(provider)s", { provider: idp.name }) : _t("Sign in with single sign-on"); const onClick = () => { @@ -80,19 +79,21 @@ const SSOButton: React.FC = ({ const classes = classNames("mx_SSOButton", { [brandClass]: brandClass, mx_SSOButton_mini: mini, + mx_SSOButton_default: !idp, + mx_SSOButton_primary: primary, }); if (mini) { // TODO fallback icon return ( - + { icon } ); } return ( - + { icon } { label } From 0eb0d242d56b24286f48da6eabacf2f0af57d537 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 29 Jan 2021 13:05:09 +0000 Subject: [PATCH 8/8] Apply suggestions from code review Co-authored-by: J. Ryan Stinnett --- src/components/structures/InteractiveAuth.js | 3 ++- src/components/views/auth/InteractiveAuthEntryComponents.js | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/structures/InteractiveAuth.js b/src/components/structures/InteractiveAuth.js index 23e43da195..ac7049ed88 100644 --- a/src/components/structures/InteractiveAuth.js +++ b/src/components/structures/InteractiveAuth.js @@ -179,7 +179,8 @@ export default class InteractiveAuthComponent extends React.Component { }, () => { if (oldStage !== stageType) { this._setFocus(); - } else if (!stageState.error && this._stageComponent.current && + } else if ( + !stageState.error && this._stageComponent.current && this._stageComponent.current.attemptFailed ) { this._stageComponent.current.attemptFailed(); diff --git a/src/components/views/auth/InteractiveAuthEntryComponents.js b/src/components/views/auth/InteractiveAuthEntryComponents.js index c8469192d2..7dc1976641 100644 --- a/src/components/views/auth/InteractiveAuthEntryComponents.js +++ b/src/components/views/auth/InteractiveAuthEntryComponents.js @@ -626,6 +626,7 @@ export class SSOAuthEntry extends React.Component { window.removeEventListener("message", this._onReceiveMessage); if (this._popupWindow) { this._popupWindow.close(); + this._popupWindow = null; } } @@ -639,6 +640,7 @@ export class SSOAuthEntry extends React.Component { if (event.data === "authDone" && event.origin === this.props.matrixClient.getHomeserverUrl()) { if (this._popupWindow) { this._popupWindow.close(); + this._popupWindow = null; } } };