Better errors for SSO failures

This commit is contained in:
Michael Telatynski 2021-02-01 16:25:50 +00:00
parent d06f61393d
commit bad71cf52f
5 changed files with 49 additions and 5 deletions

View file

@ -30,6 +30,7 @@ import {idbLoad, idbSave, idbDelete} from "./utils/StorageManager";
export const SSO_HOMESERVER_URL_KEY = "mx_sso_hs_url"; export const SSO_HOMESERVER_URL_KEY = "mx_sso_hs_url";
export const SSO_ID_SERVER_URL_KEY = "mx_sso_is_url"; export const SSO_ID_SERVER_URL_KEY = "mx_sso_is_url";
export const SSO_IDP_ID_KEY = "mx_sso_idp_id";
export enum UpdateCheckStatus { export enum UpdateCheckStatus {
Checking = "CHECKING", Checking = "CHECKING",
@ -258,6 +259,9 @@ export default abstract class BasePlatform {
if (mxClient.getIdentityServerUrl()) { if (mxClient.getIdentityServerUrl()) {
localStorage.setItem(SSO_ID_SERVER_URL_KEY, mxClient.getIdentityServerUrl()); localStorage.setItem(SSO_ID_SERVER_URL_KEY, mxClient.getIdentityServerUrl());
} }
if (idpId) {
localStorage.setItem(SSO_IDP_ID_KEY, idpId);
}
const callbackUrl = this.getSSOCallbackUrl(fragmentAfterLogin); const callbackUrl = this.getSSOCallbackUrl(fragmentAfterLogin);
window.location.href = mxClient.getSsoLoginUrl(callbackUrl.toString(), loginType, idpId); // redirect to SSO window.location.href = mxClient.getSsoLoginUrl(callbackUrl.toString(), loginType, idpId); // redirect to SSO
} }

View file

@ -46,11 +46,13 @@ import {IntegrationManagers} from "./integrations/IntegrationManagers";
import {Mjolnir} from "./mjolnir/Mjolnir"; import {Mjolnir} from "./mjolnir/Mjolnir";
import DeviceListener from "./DeviceListener"; import DeviceListener from "./DeviceListener";
import {Jitsi} from "./widgets/Jitsi"; import {Jitsi} from "./widgets/Jitsi";
import {SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY} from "./BasePlatform"; import {SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY, SSO_IDP_ID_KEY} from "./BasePlatform";
import ThreepidInviteStore from "./stores/ThreepidInviteStore"; import ThreepidInviteStore from "./stores/ThreepidInviteStore";
import CountlyAnalytics from "./CountlyAnalytics"; import CountlyAnalytics from "./CountlyAnalytics";
import CallHandler from './CallHandler'; import CallHandler from './CallHandler';
import LifecycleCustomisations from "./customisations/Lifecycle"; import LifecycleCustomisations from "./customisations/Lifecycle";
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
import {_t} from "./languageHandler";
const HOMESERVER_URL_KEY = "mx_hs_url"; const HOMESERVER_URL_KEY = "mx_hs_url";
const ID_SERVER_URL_KEY = "mx_is_url"; const ID_SERVER_URL_KEY = "mx_is_url";
@ -162,7 +164,8 @@ export async function getStoredSessionOwner(): Promise<[string, boolean]> {
* query-parameters extracted from the real query-string of the starting * query-parameters extracted from the real query-string of the starting
* URI. * URI.
* *
* @param {String} defaultDeviceDisplayName * @param {string} defaultDeviceDisplayName
* @param {string} fragmentAfterLogin path to go to after a successful login, only used for "Try again"
* *
* @returns {Promise} promise which resolves to true if we completed the token * @returns {Promise} promise which resolves to true if we completed the token
* login, else false * login, else false
@ -170,6 +173,7 @@ export async function getStoredSessionOwner(): Promise<[string, boolean]> {
export function attemptTokenLogin( export function attemptTokenLogin(
queryParams: Record<string, string>, queryParams: Record<string, string>,
defaultDeviceDisplayName?: string, defaultDeviceDisplayName?: string,
fragmentAfterLogin?: string,
): Promise<boolean> { ): Promise<boolean> {
if (!queryParams.loginToken) { if (!queryParams.loginToken) {
return Promise.resolve(false); return Promise.resolve(false);
@ -179,6 +183,12 @@ export function attemptTokenLogin(
const identityServer = localStorage.getItem(SSO_ID_SERVER_URL_KEY); const identityServer = localStorage.getItem(SSO_ID_SERVER_URL_KEY);
if (!homeserver) { if (!homeserver) {
console.warn("Cannot log in with token: can't determine HS URL to use"); console.warn("Cannot log in with token: can't determine HS URL to use");
Modal.createTrackedDialog("SSO", "Unknown HS", ErrorDialog, {
title: _t("We couldn't log you in"),
description: _t("We asked the browser to remember which homeserver you use, to let you sign in. " +
"Unfortunately your browser has forgotten in. Go to the sign in page and try again."),
button: _t("Try again"),
});
return Promise.resolve(false); return Promise.resolve(false);
} }
@ -198,8 +208,28 @@ export function attemptTokenLogin(
return true; return true;
}); });
}).catch((err) => { }).catch((err) => {
console.error("Failed to log in with login token: " + err + " " + Modal.createTrackedDialog("SSO", "Token Rejected", ErrorDialog, {
err.data); title: _t("We couldn't log you in"),
description: err.name === "ConnectionError"
? _t("Your homeserver was unreachable and was not able to log you in. Please try again. " +
"If this continues, please contact your homeserver administrator.")
: _t("Your homeserver rejected your log in attempt. " +
"This could be due to things just taking too long. Please try again. " +
"If this continues, please contact your homeserver administrator."),
button: _t("Try again"),
onFinished: tryAgain => {
if (tryAgain) {
const cli = Matrix.createClient({
baseUrl: homeserver,
idBaseUrl: identityServer,
});
const idpId = localStorage.getItem(SSO_IDP_ID_KEY) || undefined;
PlatformPeg.get().startSingleSignOn(cli, "sso", fragmentAfterLogin, idpId);
}
},
});
console.error("Failed to log in with login token:");
console.error(err);
return false; return false;
}); });
} }

View file

@ -325,6 +325,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
Lifecycle.attemptTokenLogin( Lifecycle.attemptTokenLogin(
this.props.realQueryParams, this.props.realQueryParams,
this.props.defaultDeviceDisplayName, this.props.defaultDeviceDisplayName,
this.getFragmentAfterLogin(),
).then(async (loggedIn) => { ).then(async (loggedIn) => {
if (this.props.realQueryParams?.loginToken) { if (this.props.realQueryParams?.loginToken) {
// remove the loginToken from the URL regardless // remove the loginToken from the URL regardless

View file

@ -50,6 +50,10 @@ export default class ErrorDialog extends React.Component {
button: null, button: null,
}; };
onClick = () => {
this.props.onFinished(true);
};
render() { render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return ( return (
@ -64,7 +68,7 @@ export default class ErrorDialog extends React.Component {
{ this.props.description || _t('An error has occurred.') } { this.props.description || _t('An error has occurred.') }
</div> </div>
<div className="mx_Dialog_buttons"> <div className="mx_Dialog_buttons">
<button className="mx_Dialog_primary" onClick={this.props.onFinished} autoFocus={this.props.focus}> <button className="mx_Dialog_primary" onClick={this.onClick} autoFocus={this.props.focus}>
{ this.props.button || _t('OK') } { this.props.button || _t('OK') }
</button> </button>
</div> </div>

View file

@ -118,6 +118,11 @@
"This action requires accessing the default identity server <server /> to validate an email address or phone number, but the server does not have any terms of service.": "This action requires accessing the default identity server <server /> to validate an email address or phone number, but the server does not have any terms of service.", "This action requires accessing the default identity server <server /> to validate an email address or phone number, but the server does not have any terms of service.": "This action requires accessing the default identity server <server /> to validate an email address or phone number, but the server does not have any terms of service.",
"Only continue if you trust the owner of the server.": "Only continue if you trust the owner of the server.", "Only continue if you trust the owner of the server.": "Only continue if you trust the owner of the server.",
"Trust": "Trust", "Trust": "Trust",
"We couldn't log you in": "We couldn't log you in",
"We asked the browser to remember which homeserver you use, to let you sign in. Unfortunately your browser has forgotten in. Go to the sign in page and try again.": "We asked the browser to remember which homeserver you use, to let you sign in. Unfortunately your browser has forgotten in. Go to the sign in page and try again.",
"Try again": "Try again",
"Your homeserver was unreachable and was not able to log you in. Please try again. If this continues, please contact your homeserver administrator.": "Your homeserver was unreachable and was not able to log you in. Please try again. If this continues, please contact your homeserver administrator.",
"Your homeserver rejected your log in attempt. This could be due to things just taking too long. Please try again. If this continues, please contact your homeserver administrator.": "Your homeserver rejected your log in attempt. This could be due to things just taking too long. Please try again. If this continues, please contact your homeserver administrator.",
"%(name)s is requesting verification": "%(name)s is requesting verification", "%(name)s is requesting verification": "%(name)s is requesting verification",
"%(brand)s does not have permission to send you notifications - please check your browser settings": "%(brand)s does not have permission to send you notifications - please check your browser settings", "%(brand)s does not have permission to send you notifications - please check your browser settings": "%(brand)s does not have permission to send you notifications - please check your browser settings",
"%(brand)s was not given permission to send notifications - please try again": "%(brand)s was not given permission to send notifications - please try again", "%(brand)s was not given permission to send notifications - please try again": "%(brand)s was not given permission to send notifications - please try again",