diff --git a/res/css/views/auth/_ServerConfig.scss b/res/css/views/auth/_ServerConfig.scss
index 79ad9e8238..fe96da2019 100644
--- a/res/css/views/auth/_ServerConfig.scss
+++ b/res/css/views/auth/_ServerConfig.scss
@@ -35,3 +35,8 @@ limitations under the License.
.mx_ServerConfig_help:link {
opacity: 0.8;
}
+
+.mx_ServerConfig_error {
+ display: block;
+ color: $warning-color;
+}
diff --git a/src/components/views/auth/ModularServerConfig.js b/src/components/views/auth/ModularServerConfig.js
index 9c6c4b01bf..ea22577dbd 100644
--- a/src/components/views/auth/ModularServerConfig.js
+++ b/src/components/views/auth/ModularServerConfig.js
@@ -18,9 +18,15 @@ import React from 'react';
import PropTypes from 'prop-types';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';
+import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
+import SdkConfig from "../../../SdkConfig";
+import AutoDiscoveryUtils from "../../../utils/AutoDiscoveryUtils";
+import * as ServerType from '../../views/auth/ServerTypeSelector';
const MODULAR_URL = 'https://modular.im/?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication';
+// TODO: TravisR - Can this extend ServerConfig for most things?
+
/*
* Configure the Modular server name.
*
@@ -31,65 +37,87 @@ export default class ModularServerConfig extends React.PureComponent {
static propTypes = {
onServerConfigChange: PropTypes.func,
- // default URLs are defined in config.json (or the hardcoded defaults)
- // they are used if the user has not overridden them with a custom URL.
- // In other words, if the custom URL is blank, the default is used.
- defaultHsUrl: PropTypes.string, // e.g. https://matrix.org
-
- // This component always uses the default IS URL and doesn't allow it
- // to be changed. We still receive it as a prop here to simplify
- // consumers by still passing the IS URL via onServerConfigChange.
- defaultIsUrl: PropTypes.string, // e.g. https://vector.im
-
- // custom URLs are explicitly provided by the user and override the
- // default URLs. The user enters them via the component's input fields,
- // which is reflected on these properties whenever on..UrlChanged fires.
- // They are persisted in localStorage by MatrixClientPeg, and so can
- // override the default URLs when the component initially loads.
- customHsUrl: PropTypes.string,
+ // The current configuration that the user is expecting to change.
+ serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
delayTimeMs: PropTypes.number, // time to wait before invoking onChanged
- }
+ };
static defaultProps = {
onServerConfigChange: function() {},
customHsUrl: "",
delayTimeMs: 0,
- }
+ };
constructor(props) {
super(props);
this.state = {
- hsUrl: props.customHsUrl,
+ busy: false,
+ errorText: "",
+ hsUrl: props.serverConfig.hsUrl,
+ isUrl: props.serverConfig.isUrl,
};
}
componentWillReceiveProps(newProps) {
- if (newProps.customHsUrl === this.state.hsUrl) return;
+ if (newProps.serverConfig.hsUrl === this.state.hsUrl &&
+ newProps.serverConfig.isUrl === this.state.isUrl) return;
+
+ this.validateAndApplyServer(newProps.serverConfig.hsUrl, newProps.serverConfig.isUrl);
+ }
+
+ async validateAndApplyServer(hsUrl, isUrl) {
+ // Always try and use the defaults first
+ const defaultConfig: ValidatedServerConfig = SdkConfig.get()["validated_server_config"];
+ if (defaultConfig.hsUrl === hsUrl && defaultConfig.isUrl === isUrl) {
+ this.setState({busy: false, errorText: ""});
+ this.props.onServerConfigChange(defaultConfig);
+ return defaultConfig;
+ }
this.setState({
- hsUrl: newProps.customHsUrl,
- });
- this.props.onServerConfigChange({
- hsUrl: newProps.customHsUrl,
- isUrl: this.props.defaultIsUrl,
+ hsUrl,
+ isUrl,
+ busy: true,
+ errorText: "",
});
+
+ try {
+ const result = await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(hsUrl, isUrl);
+ this.setState({busy: false, errorText: ""});
+ this.props.onServerConfigChange(result);
+ return result;
+ } catch (e) {
+ console.error(e);
+ let message = _t("Unable to validate homeserver/identity server");
+ if (e.translatedMessage) {
+ message = e.translatedMessage;
+ }
+ this.setState({
+ busy: false,
+ errorText: message,
+ });
+ }
+ }
+
+ async validateServer() {
+ // TODO: Do we want to support .well-known lookups here?
+ // If for some reason someone enters "matrix.org" for a URL, we could do a lookup to
+ // find their homeserver without demanding they use "https://matrix.org"
+ return this.validateAndApplyServer(this.state.hsUrl, ServerType.TYPES.PREMIUM.identityServerUrl);
}
onHomeserverBlur = (ev) => {
this._hsTimeoutId = this._waitThenInvoke(this._hsTimeoutId, () => {
- this.props.onServerConfigChange({
- hsUrl: this.state.hsUrl,
- isUrl: this.props.defaultIsUrl,
- });
+ this.validateServer();
});
- }
+ };
onHomeserverChange = (ev) => {
const hsUrl = ev.target.value;
this.setState({ hsUrl });
- }
+ };
_waitThenInvoke(existingTimeoutId, fn) {
if (existingTimeoutId) {
@@ -116,7 +144,7 @@ export default class ModularServerConfig extends React.PureComponent {
{
this._hsTimeoutId = this._waitThenInvoke(this._hsTimeoutId, () => {
- this.props.onServerConfigChange({
- hsUrl: this.state.hsUrl,
- isUrl: this.state.isUrl,
- });
+ this.validateServer();
});
- }
+ };
onHomeserverChange = (ev) => {
const hsUrl = ev.target.value;
this.setState({ hsUrl });
- }
+ };
onIdentityServerBlur = (ev) => {
this._isTimeoutId = this._waitThenInvoke(this._isTimeoutId, () => {
- this.props.onServerConfigChange({
- hsUrl: this.state.hsUrl,
- isUrl: this.state.isUrl,
- });
+ this.validateServer();
});
- }
+ };
onIdentityServerChange = (ev) => {
const isUrl = ev.target.value;
this.setState({ isUrl });
- }
+ };
_waitThenInvoke(existingTimeoutId, fn) {
if (existingTimeoutId) {
@@ -114,11 +134,15 @@ export default class ServerConfig extends React.PureComponent {
showHelpPopup = () => {
const CustomServerDialog = sdk.getComponent('auth.CustomServerDialog');
Modal.createTrackedDialog('Custom Server Dialog', '', CustomServerDialog);
- }
+ };
render() {
const Field = sdk.getComponent('elements.Field');
+ const errorText = this.state.errorText
+ ? {this.state.errorText}
+ : null;
+
return (
{_t("Other servers")}
@@ -127,20 +151,23 @@ export default class ServerConfig extends React.PureComponent {
{ sub }
,
})}
+ {errorText}
diff --git a/src/utils/AutoDiscoveryUtils.js b/src/utils/AutoDiscoveryUtils.js
new file mode 100644
index 0000000000..318c706136
--- /dev/null
+++ b/src/utils/AutoDiscoveryUtils.js
@@ -0,0 +1,104 @@
+/*
+Copyright 2019 New Vector Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import {AutoDiscovery} from "matrix-js-sdk";
+import {_td, newTranslatableError} from "../languageHandler";
+import {makeType} from "./TypeUtils";
+import SdkConfig from "../SdkConfig";
+
+export class ValidatedServerConfig {
+ hsUrl: string;
+ hsName: string;
+ hsNameIsDifferent: string;
+
+ isUrl: string;
+ identityEnabled: boolean;
+}
+
+export default class AutoDiscoveryUtils {
+ static async validateServerConfigWithStaticUrls(homeserverUrl: string, identityUrl: string): ValidatedServerConfig {
+ if (!homeserverUrl) {
+ throw newTranslatableError(_td("No homeserver URL provided"));
+ }
+
+ const wellknownConfig = {
+ "m.homeserver": {
+ base_url: homeserverUrl,
+ },
+ "m.identity_server": {
+ base_url: identityUrl,
+ },
+ };
+
+ const result = await AutoDiscovery.fromDiscoveryConfig(wellknownConfig);
+
+ const url = new URL(homeserverUrl);
+ const serverName = url.hostname;
+
+ return AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, result);
+ }
+
+ static async validateServerName(serverName: string): ValidatedServerConfig {
+ const result = await AutoDiscovery.findClientConfig(serverName);
+ return AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, result);
+ }
+
+ static buildValidatedConfigFromDiscovery(serverName: string, discoveryResult): ValidatedServerConfig {
+ if (!discoveryResult || !discoveryResult["m.homeserver"]) {
+ // This shouldn't happen without major misconfiguration, so we'll log a bit of information
+ // in the log so we can find this bit of codee but otherwise tell teh user "it broke".
+ console.error("Ended up in a state of not knowing which homeserver to connect to.");
+ throw newTranslatableError(_td("Unexpected error resolving homeserver configuration"));
+ }
+
+ const hsResult = discoveryResult['m.homeserver'];
+ if (hsResult.state !== AutoDiscovery.SUCCESS) {
+ if (AutoDiscovery.ALL_ERRORS.indexOf(hsResult.error) !== -1) {
+ throw newTranslatableError(hsResult.error);
+ }
+ throw newTranslatableError(_td("Unexpected error resolving homeserver configuration"));
+ }
+
+ const isResult = discoveryResult['m.identity_server'];
+ let preferredIdentityUrl = "https://vector.im";
+ if (isResult && isResult.state === AutoDiscovery.SUCCESS) {
+ preferredIdentityUrl = isResult["base_url"];
+ } else if (isResult && isResult.state !== AutoDiscovery.PROMPT) {
+ console.error("Error determining preferred identity server URL:", isResult);
+ throw newTranslatableError(_td("Unexpected error resolving homeserver configuration"));
+ }
+
+ const preferredHomeserverUrl = hsResult["base_url"];
+ let preferredHomeserverName = serverName ? serverName : hsResult["server_name"];
+
+ const url = new URL(preferredHomeserverUrl);
+ if (!preferredHomeserverName) preferredHomeserverName = url.hostname;
+
+ // It should have been set by now, so check it
+ if (!preferredHomeserverName) {
+ console.error("Failed to parse homeserver name from homeserver URL");
+ throw newTranslatableError(_td("Unexpected error resolving homeserver configuration"));
+ }
+
+ return makeType(ValidatedServerConfig, {
+ hsUrl: preferredHomeserverUrl,
+ hsName: preferredHomeserverName,
+ hsNameIsDifferent: url.hostname !== preferredHomeserverName,
+ isUrl: preferredIdentityUrl,
+ identityEnabled: !SdkConfig.get()['disable_identity_server'],
+ });
+ }
+}