2016-10-11 20:04:55 +03:00
|
|
|
/*
|
|
|
|
Copyright 2016 OpenMarket Ltd
|
2017-02-13 19:15:00 +03:00
|
|
|
Copyright 2017 Vector Creations Ltd
|
2016-10-11 20:04:55 +03:00
|
|
|
|
|
|
|
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 React from 'react';
|
2017-12-26 04:03:18 +03:00
|
|
|
import PropTypes from 'prop-types';
|
2017-03-14 14:50:13 +03:00
|
|
|
import url from 'url';
|
|
|
|
import classnames from 'classnames';
|
2016-10-11 20:04:55 +03:00
|
|
|
|
|
|
|
import sdk from '../../../index';
|
2017-11-13 22:19:33 +03:00
|
|
|
import { _t } from '../../../languageHandler';
|
2018-10-16 01:12:42 +03:00
|
|
|
import SettingsStore from "../../../settings/SettingsStore";
|
2016-10-11 20:04:55 +03:00
|
|
|
|
|
|
|
/* This file contains a collection of components which are used by the
|
2017-02-13 22:09:43 +03:00
|
|
|
* InteractiveAuth to prompt the user to enter the information needed
|
2016-10-11 20:04:55 +03:00
|
|
|
* for an auth stage. (The intention is that they could also be used for other
|
|
|
|
* components, such as the registration flow).
|
|
|
|
*
|
|
|
|
* Call getEntryComponentForLoginType() to get a component suitable for a
|
|
|
|
* particular login type. Each component requires the same properties:
|
|
|
|
*
|
2017-02-24 14:41:23 +03:00
|
|
|
* matrixClient: A matrix client. May be a different one to the one
|
|
|
|
* currently being used generally (eg. to register with
|
|
|
|
* one HS whilst beign a guest on another).
|
2016-10-11 20:04:55 +03:00
|
|
|
* loginType: the login type of the auth stage being attempted
|
|
|
|
* authSessionId: session id from the server
|
2017-03-01 19:04:15 +03:00
|
|
|
* clientSecret: The client secret in use for ID server auth sessions
|
2016-10-11 20:04:55 +03:00
|
|
|
* stageParams: params from the server for the stage being attempted
|
|
|
|
* errorText: error message from a previous attempt to authenticate
|
|
|
|
* submitAuthDict: a function which will be called with the new auth dict
|
2017-02-14 13:34:43 +03:00
|
|
|
* busy: a boolean indicating whether the auth logic is doing something
|
|
|
|
* the user needs to wait for.
|
2017-03-01 19:04:15 +03:00
|
|
|
* inputs: Object of inputs provided by the user, as in js-sdk
|
2017-02-24 14:41:23 +03:00
|
|
|
* interactive-auth
|
2017-03-01 19:04:15 +03:00
|
|
|
* stageState: Stage-specific object used for communicating state information
|
2017-02-24 14:41:23 +03:00
|
|
|
* to the UI from the state-specific auth logic.
|
2017-03-01 19:04:15 +03:00
|
|
|
* Defined keys for stages are:
|
|
|
|
* m.login.email.identity:
|
|
|
|
* * emailSid: string representing the sid of the active
|
|
|
|
* verification session from the ID server, or
|
|
|
|
* null if no session is active.
|
|
|
|
* fail: a function which should be called with an error object if an
|
|
|
|
* error occurred during the auth stage. This will cause the auth
|
|
|
|
* session to be failed and the process to go back to the start.
|
|
|
|
* setEmailSid: m.login.email.identity only: a function to be called with the
|
|
|
|
* email sid after a token is requested.
|
|
|
|
* makeRegistrationUrl A function that makes a registration URL
|
2016-10-11 20:04:55 +03:00
|
|
|
*
|
|
|
|
* Each component may also provide the following functions (beyond the standard React ones):
|
|
|
|
* focus: set the input focus appropriately in the form.
|
|
|
|
*/
|
|
|
|
|
|
|
|
export const PasswordAuthEntry = React.createClass({
|
|
|
|
displayName: 'PasswordAuthEntry',
|
|
|
|
|
|
|
|
statics: {
|
|
|
|
LOGIN_TYPE: "m.login.password",
|
|
|
|
},
|
|
|
|
|
|
|
|
propTypes: {
|
2017-12-26 04:03:18 +03:00
|
|
|
matrixClient: PropTypes.object.isRequired,
|
|
|
|
submitAuthDict: PropTypes.func.isRequired,
|
|
|
|
errorText: PropTypes.string,
|
2017-02-13 21:52:33 +03:00
|
|
|
// is the auth logic currently waiting for something to
|
|
|
|
// happen?
|
2017-12-26 04:03:18 +03:00
|
|
|
busy: PropTypes.bool,
|
2016-10-11 20:04:55 +03:00
|
|
|
},
|
|
|
|
|
2017-02-13 19:03:21 +03:00
|
|
|
getInitialState: function() {
|
|
|
|
return {
|
2017-02-13 21:52:33 +03:00
|
|
|
passwordValid: false,
|
2017-02-13 19:03:21 +03:00
|
|
|
};
|
2016-10-11 20:04:55 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
focus: function() {
|
|
|
|
if (this.refs.passwordField) {
|
|
|
|
this.refs.passwordField.focus();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2017-02-13 21:52:33 +03:00
|
|
|
_onSubmit: function(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
if (this.props.busy) return;
|
|
|
|
|
2016-10-11 20:04:55 +03:00
|
|
|
this.props.submitAuthDict({
|
|
|
|
type: PasswordAuthEntry.LOGIN_TYPE,
|
2017-02-24 14:41:23 +03:00
|
|
|
user: this.props.matrixClient.credentials.userId,
|
2016-10-11 20:04:55 +03:00
|
|
|
password: this.refs.passwordField.value,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2017-01-20 17:22:27 +03:00
|
|
|
_onPasswordFieldChange: function(ev) {
|
2016-10-11 20:04:55 +03:00
|
|
|
// enable the submit button iff the password is non-empty
|
2017-02-13 19:03:21 +03:00
|
|
|
this.setState({
|
2017-02-13 21:52:33 +03:00
|
|
|
passwordValid: Boolean(this.refs.passwordField.value),
|
2017-02-13 19:03:21 +03:00
|
|
|
});
|
2016-10-11 20:04:55 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
render: function() {
|
|
|
|
let passwordBoxClass = null;
|
|
|
|
|
|
|
|
if (this.props.errorText) {
|
|
|
|
passwordBoxClass = 'error';
|
|
|
|
}
|
|
|
|
|
2017-02-13 21:52:33 +03:00
|
|
|
let submitButtonOrSpinner;
|
|
|
|
if (this.props.busy) {
|
|
|
|
const Loader = sdk.getComponent("elements.Spinner");
|
|
|
|
submitButtonOrSpinner = <Loader />;
|
|
|
|
} else {
|
|
|
|
submitButtonOrSpinner = (
|
|
|
|
<input type="submit"
|
|
|
|
className="mx_Dialog_primary"
|
|
|
|
disabled={!this.state.passwordValid}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-12-14 12:31:28 +03:00
|
|
|
let errorSection;
|
|
|
|
if (this.props.errorText) {
|
|
|
|
errorSection = (
|
|
|
|
<div className="error" role="alert">
|
|
|
|
{ this.props.errorText }
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-10-11 20:04:55 +03:00
|
|
|
return (
|
|
|
|
<div>
|
2017-10-11 19:56:17 +03:00
|
|
|
<p>{ _t("To continue, please enter your password.") }</p>
|
2017-02-13 19:03:21 +03:00
|
|
|
<form onSubmit={this._onSubmit}>
|
2017-12-05 15:52:20 +03:00
|
|
|
<label htmlFor="passwordField">{ _t("Password:") }</label>
|
2017-02-13 19:03:21 +03:00
|
|
|
<input
|
2017-12-05 15:52:20 +03:00
|
|
|
name="passwordField"
|
2017-02-13 19:03:21 +03:00
|
|
|
ref="passwordField"
|
|
|
|
className={passwordBoxClass}
|
|
|
|
onChange={this._onPasswordFieldChange}
|
|
|
|
type="password"
|
|
|
|
/>
|
|
|
|
<div className="mx_button_row">
|
2017-10-11 19:56:17 +03:00
|
|
|
{ submitButtonOrSpinner }
|
2017-02-13 19:03:21 +03:00
|
|
|
</div>
|
|
|
|
</form>
|
2017-12-14 12:31:28 +03:00
|
|
|
{ errorSection }
|
2016-10-11 20:04:55 +03:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
export const RecaptchaAuthEntry = React.createClass({
|
|
|
|
displayName: 'RecaptchaAuthEntry',
|
|
|
|
|
|
|
|
statics: {
|
|
|
|
LOGIN_TYPE: "m.login.recaptcha",
|
|
|
|
},
|
|
|
|
|
|
|
|
propTypes: {
|
2017-12-26 04:03:18 +03:00
|
|
|
submitAuthDict: PropTypes.func.isRequired,
|
|
|
|
stageParams: PropTypes.object.isRequired,
|
|
|
|
errorText: PropTypes.string,
|
|
|
|
busy: PropTypes.bool,
|
2016-10-11 20:04:55 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
_onCaptchaResponse: function(response) {
|
|
|
|
this.props.submitAuthDict({
|
|
|
|
type: RecaptchaAuthEntry.LOGIN_TYPE,
|
|
|
|
response: response,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
render: function() {
|
2017-03-21 21:40:41 +03:00
|
|
|
if (this.props.busy) {
|
|
|
|
const Loader = sdk.getComponent("elements.Spinner");
|
|
|
|
return <Loader />;
|
|
|
|
}
|
|
|
|
|
2016-10-11 20:04:55 +03:00
|
|
|
const CaptchaForm = sdk.getComponent("views.login.CaptchaForm");
|
2017-10-11 19:56:17 +03:00
|
|
|
const sitePublicKey = this.props.stageParams.public_key;
|
2017-12-14 12:31:28 +03:00
|
|
|
|
|
|
|
let errorSection;
|
|
|
|
if (this.props.errorText) {
|
|
|
|
errorSection = (
|
|
|
|
<div className="error" role="alert">
|
|
|
|
{ this.props.errorText }
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-10-11 20:04:55 +03:00
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<CaptchaForm sitePublicKey={sitePublicKey}
|
|
|
|
onCaptchaResponse={this._onCaptchaResponse}
|
|
|
|
/>
|
2017-12-14 12:31:28 +03:00
|
|
|
{ errorSection }
|
2016-10-11 20:04:55 +03:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2018-10-16 01:12:42 +03:00
|
|
|
export const TermsAuthEntry = React.createClass({
|
|
|
|
displayName: 'TermsAuthEntry',
|
|
|
|
|
|
|
|
statics: {
|
|
|
|
LOGIN_TYPE: "m.login.terms",
|
|
|
|
},
|
|
|
|
|
|
|
|
propTypes: {
|
|
|
|
submitAuthDict: PropTypes.func.isRequired,
|
|
|
|
stageParams: PropTypes.object.isRequired,
|
|
|
|
errorText: PropTypes.string,
|
|
|
|
busy: PropTypes.bool,
|
2018-11-16 22:15:44 +03:00
|
|
|
showContinue: PropTypes.bool,
|
2018-10-16 01:12:42 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
componentWillMount: function() {
|
2018-10-16 18:19:36 +03:00
|
|
|
// example stageParams:
|
|
|
|
//
|
|
|
|
// {
|
|
|
|
// "policies": {
|
|
|
|
// "privacy_policy": {
|
|
|
|
// "version": "1.0",
|
|
|
|
// "en": {
|
|
|
|
// "name": "Privacy Policy",
|
|
|
|
// "url": "https://example.org/privacy-1.0-en.html",
|
|
|
|
// },
|
|
|
|
// "fr": {
|
|
|
|
// "name": "Politique de confidentialité",
|
|
|
|
// "url": "https://example.org/privacy-1.0-fr.html",
|
|
|
|
// },
|
|
|
|
// },
|
|
|
|
// "other_policy": { ... },
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
2018-10-16 01:12:42 +03:00
|
|
|
const allPolicies = this.props.stageParams.policies || {};
|
|
|
|
const prefLang = SettingsStore.getValue("language");
|
|
|
|
const initToggles = {};
|
|
|
|
const pickedPolicies = [];
|
|
|
|
for (const policyId of Object.keys(allPolicies)) {
|
|
|
|
const policy = allPolicies[policyId];
|
|
|
|
|
|
|
|
// Pick a language based on the user's language, falling back to english,
|
|
|
|
// and finally to the first language available. If there's still no policy
|
|
|
|
// available then the homeserver isn't respecting the spec.
|
|
|
|
let langPolicy = policy[prefLang];
|
2018-10-16 18:19:36 +03:00
|
|
|
if (!langPolicy) langPolicy = policy["en"];
|
|
|
|
if (!langPolicy) {
|
|
|
|
// last resort
|
|
|
|
const firstLang = Object.keys(policy).find(e => e !== "version");
|
|
|
|
langPolicy = policy[firstLang];
|
|
|
|
}
|
2018-10-16 01:12:42 +03:00
|
|
|
if (!langPolicy) throw new Error("Failed to find a policy to show the user");
|
|
|
|
|
|
|
|
initToggles[policyId] = false;
|
|
|
|
|
|
|
|
langPolicy.id = policyId;
|
|
|
|
pickedPolicies.push(langPolicy);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
"toggledPolicies": initToggles,
|
|
|
|
"policies": pickedPolicies,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2018-11-15 04:26:08 +03:00
|
|
|
tryContinue: function() {
|
|
|
|
this._trySubmit();
|
|
|
|
},
|
|
|
|
|
|
|
|
_togglePolicy: function(policyId) {
|
2018-10-16 01:12:42 +03:00
|
|
|
const newToggles = {};
|
|
|
|
for (const policy of this.state.policies) {
|
|
|
|
let checked = this.state.toggledPolicies[policy.id];
|
|
|
|
if (policy.id === policyId) checked = !checked;
|
|
|
|
|
|
|
|
newToggles[policy.id] = checked;
|
2018-11-15 04:26:08 +03:00
|
|
|
}
|
|
|
|
this.setState({"toggledPolicies": newToggles});
|
|
|
|
},
|
|
|
|
|
|
|
|
_trySubmit: function() {
|
|
|
|
let allChecked = true;
|
|
|
|
for (const policy of this.state.policies) {
|
|
|
|
let checked = this.state.toggledPolicies[policy.id];
|
2018-10-16 01:12:42 +03:00
|
|
|
allChecked = allChecked && checked;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (allChecked) this.props.submitAuthDict({type: TermsAuthEntry.LOGIN_TYPE});
|
2018-11-15 04:26:08 +03:00
|
|
|
else this.setState({errorText: _t("Please review and accept all of the homeserver's policies")});
|
2018-10-16 01:12:42 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
render: function() {
|
|
|
|
if (this.props.busy) {
|
|
|
|
const Loader = sdk.getComponent("elements.Spinner");
|
|
|
|
return <Loader />;
|
|
|
|
}
|
|
|
|
|
2018-10-27 06:50:35 +03:00
|
|
|
const checkboxes = [];
|
2018-10-16 01:12:42 +03:00
|
|
|
let allChecked = true;
|
|
|
|
for (const policy of this.state.policies) {
|
|
|
|
const checked = this.state.toggledPolicies[policy.id];
|
|
|
|
allChecked = allChecked && checked;
|
|
|
|
|
|
|
|
checkboxes.push(
|
2018-11-15 04:26:08 +03:00
|
|
|
<label key={"policy_checkbox_" + policy.id} className="mx_InteractiveAuthEntryComponents_termsPolicy">
|
|
|
|
<input type="checkbox" onClick={() => this._togglePolicy(policy.id)} checked={checked} />
|
2018-10-16 01:12:42 +03:00
|
|
|
<a href={policy.url} target="_blank" rel="noopener">{ policy.name }</a>
|
2018-10-27 06:50:35 +03:00
|
|
|
</label>,
|
2018-10-16 01:12:42 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
let errorSection;
|
2018-11-15 04:26:08 +03:00
|
|
|
if (this.props.errorText || this.state.errorText) {
|
2018-10-16 01:12:42 +03:00
|
|
|
errorSection = (
|
|
|
|
<div className="error" role="alert">
|
2018-11-15 04:26:08 +03:00
|
|
|
{ this.props.errorText || this.state.errorText }
|
2018-10-16 01:12:42 +03:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-11-15 04:26:08 +03:00
|
|
|
let submitButton;
|
2018-11-16 22:15:44 +03:00
|
|
|
if (this.props.showContinue !== false) {
|
2018-11-15 04:40:00 +03:00
|
|
|
// XXX: button classes
|
|
|
|
submitButton = <button className="mx_InteractiveAuthEntryComponents_termsSubmit mx_UserSettings_button"
|
2018-11-16 06:26:37 +03:00
|
|
|
onClick={this._trySubmit} disabled={!allChecked}>{_t("Accept")}</button>;
|
2018-11-15 04:26:08 +03:00
|
|
|
}
|
|
|
|
|
2018-10-16 01:12:42 +03:00
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<p>{_t("Please review and accept the policies of this homeserver:")}</p>
|
|
|
|
{ checkboxes }
|
|
|
|
{ errorSection }
|
2018-11-15 04:26:08 +03:00
|
|
|
{ submitButton }
|
2018-10-16 01:12:42 +03:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2017-02-24 14:41:23 +03:00
|
|
|
export const EmailIdentityAuthEntry = React.createClass({
|
|
|
|
displayName: 'EmailIdentityAuthEntry',
|
|
|
|
|
|
|
|
statics: {
|
|
|
|
LOGIN_TYPE: "m.login.email.identity",
|
|
|
|
},
|
|
|
|
|
|
|
|
propTypes: {
|
2017-12-26 04:03:18 +03:00
|
|
|
matrixClient: PropTypes.object.isRequired,
|
|
|
|
submitAuthDict: PropTypes.func.isRequired,
|
|
|
|
authSessionId: PropTypes.string.isRequired,
|
|
|
|
clientSecret: PropTypes.string.isRequired,
|
|
|
|
inputs: PropTypes.object.isRequired,
|
|
|
|
stageState: PropTypes.object.isRequired,
|
|
|
|
fail: PropTypes.func.isRequired,
|
|
|
|
setEmailSid: PropTypes.func.isRequired,
|
|
|
|
makeRegistrationUrl: PropTypes.func.isRequired,
|
2017-03-01 19:04:15 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
getInitialState: function() {
|
|
|
|
return {
|
|
|
|
requestingToken: false,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
componentWillMount: function() {
|
|
|
|
if (this.props.stageState.emailSid === null) {
|
|
|
|
this.setState({requestingToken: true});
|
|
|
|
this._requestEmailToken().catch((e) => {
|
|
|
|
this.props.fail(e);
|
|
|
|
}).finally(() => {
|
|
|
|
this.setState({requestingToken: false});
|
|
|
|
}).done();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Requests a verification token by email.
|
|
|
|
*/
|
|
|
|
_requestEmailToken: function() {
|
|
|
|
const nextLink = this.props.makeRegistrationUrl({
|
|
|
|
client_secret: this.props.clientSecret,
|
|
|
|
hs_url: this.props.matrixClient.getHomeserverUrl(),
|
|
|
|
is_url: this.props.matrixClient.getIdentityServerUrl(),
|
|
|
|
session_id: this.props.authSessionId,
|
|
|
|
});
|
|
|
|
|
|
|
|
return this.props.matrixClient.requestRegisterEmailToken(
|
|
|
|
this.props.inputs.emailAddress,
|
|
|
|
this.props.clientSecret,
|
|
|
|
1, // TODO: Multiple send attempts?
|
|
|
|
nextLink,
|
|
|
|
).then((result) => {
|
|
|
|
this.props.setEmailSid(result.sid);
|
|
|
|
});
|
2017-02-24 14:41:23 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
render: function() {
|
2017-03-01 19:04:15 +03:00
|
|
|
if (this.state.requestingToken) {
|
2017-02-24 14:41:23 +03:00
|
|
|
const Loader = sdk.getComponent("elements.Spinner");
|
|
|
|
return <Loader />;
|
|
|
|
} else {
|
|
|
|
return (
|
|
|
|
<div>
|
2017-11-13 22:19:33 +03:00
|
|
|
<p>{ _t("An email has been sent to %(emailAddress)s",
|
|
|
|
{ emailAddress: (sub) => <i>{ this.props.inputs.emailAddress }</i> },
|
|
|
|
) }
|
|
|
|
</p>
|
2017-10-11 19:56:17 +03:00
|
|
|
<p>{ _t("Please check your email to continue registration.") }</p>
|
2017-02-24 14:41:23 +03:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2017-03-14 14:50:13 +03:00
|
|
|
export const MsisdnAuthEntry = React.createClass({
|
|
|
|
displayName: 'MsisdnAuthEntry',
|
|
|
|
|
|
|
|
statics: {
|
|
|
|
LOGIN_TYPE: "m.login.msisdn",
|
|
|
|
},
|
|
|
|
|
|
|
|
propTypes: {
|
2017-12-26 04:03:18 +03:00
|
|
|
inputs: PropTypes.shape({
|
|
|
|
phoneCountry: PropTypes.string,
|
|
|
|
phoneNumber: PropTypes.string,
|
2017-03-14 14:50:13 +03:00
|
|
|
}),
|
2017-12-26 04:03:18 +03:00
|
|
|
fail: PropTypes.func,
|
|
|
|
clientSecret: PropTypes.func,
|
|
|
|
submitAuthDict: PropTypes.func.isRequired,
|
|
|
|
matrixClient: PropTypes.object,
|
|
|
|
submitAuthDict: PropTypes.func,
|
2017-03-14 14:50:13 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
getInitialState: function() {
|
|
|
|
return {
|
|
|
|
token: '',
|
|
|
|
requestingToken: false,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
componentWillMount: function() {
|
|
|
|
this._sid = null;
|
|
|
|
this._msisdn = null;
|
|
|
|
this._tokenBox = null;
|
|
|
|
|
|
|
|
this.setState({requestingToken: true});
|
|
|
|
this._requestMsisdnToken().catch((e) => {
|
|
|
|
this.props.fail(e);
|
|
|
|
}).finally(() => {
|
|
|
|
this.setState({requestingToken: false});
|
|
|
|
}).done();
|
|
|
|
},
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Requests a verification token by SMS.
|
|
|
|
*/
|
|
|
|
_requestMsisdnToken: function() {
|
|
|
|
return this.props.matrixClient.requestRegisterMsisdnToken(
|
|
|
|
this.props.inputs.phoneCountry,
|
|
|
|
this.props.inputs.phoneNumber,
|
|
|
|
this.props.clientSecret,
|
|
|
|
1, // TODO: Multiple send attempts?
|
|
|
|
).then((result) => {
|
|
|
|
this._sid = result.sid;
|
|
|
|
this._msisdn = result.msisdn;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
_onTokenChange: function(e) {
|
|
|
|
this.setState({
|
|
|
|
token: e.target.value,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
_onFormSubmit: function(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
if (this.state.token == '') return;
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
errorText: null,
|
|
|
|
});
|
|
|
|
|
|
|
|
this.props.matrixClient.submitMsisdnToken(
|
2017-10-11 19:56:17 +03:00
|
|
|
this._sid, this.props.clientSecret, this.state.token,
|
2017-03-14 14:50:13 +03:00
|
|
|
).then((result) => {
|
|
|
|
if (result.success) {
|
|
|
|
const idServerParsedUrl = url.parse(
|
|
|
|
this.props.matrixClient.getIdentityServerUrl(),
|
2017-10-11 19:56:17 +03:00
|
|
|
);
|
2017-03-14 14:50:13 +03:00
|
|
|
this.props.submitAuthDict({
|
|
|
|
type: MsisdnAuthEntry.LOGIN_TYPE,
|
|
|
|
threepid_creds: {
|
|
|
|
sid: this._sid,
|
|
|
|
client_secret: this.props.clientSecret,
|
|
|
|
id_server: idServerParsedUrl.host,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.setState({
|
2017-05-30 17:09:57 +03:00
|
|
|
errorText: _t("Token incorrect"),
|
2017-03-14 14:50:13 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}).catch((e) => {
|
|
|
|
this.props.fail(e);
|
|
|
|
console.log("Failed to submit msisdn token");
|
|
|
|
}).done();
|
|
|
|
},
|
|
|
|
|
|
|
|
render: function() {
|
|
|
|
if (this.state.requestingToken) {
|
|
|
|
const Loader = sdk.getComponent("elements.Spinner");
|
|
|
|
return <Loader />;
|
|
|
|
} else {
|
|
|
|
const enableSubmit = Boolean(this.state.token);
|
|
|
|
const submitClasses = classnames({
|
|
|
|
mx_InteractiveAuthEntryComponents_msisdnSubmit: true,
|
|
|
|
mx_UserSettings_button: true, // XXX button classes
|
|
|
|
});
|
2017-12-14 12:31:28 +03:00
|
|
|
let errorSection;
|
|
|
|
if (this.state.errorText) {
|
|
|
|
errorSection = (
|
|
|
|
<div className="error" role="alert">
|
|
|
|
{ this.state.errorText }
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2017-03-14 14:50:13 +03:00
|
|
|
return (
|
|
|
|
<div>
|
2017-11-13 22:19:33 +03:00
|
|
|
<p>{ _t("A text message has been sent to %(msisdn)s",
|
2017-12-18 15:59:21 +03:00
|
|
|
{ msisdn: <i>{ this._msisdn }</i> },
|
2017-11-13 22:19:33 +03:00
|
|
|
) }
|
|
|
|
</p>
|
2017-10-11 19:56:17 +03:00
|
|
|
<p>{ _t("Please enter the code it contains:") }</p>
|
2017-03-14 14:50:13 +03:00
|
|
|
<div className="mx_InteractiveAuthEntryComponents_msisdnWrapper">
|
|
|
|
<form onSubmit={this._onFormSubmit}>
|
|
|
|
<input type="text"
|
|
|
|
className="mx_InteractiveAuthEntryComponents_msisdnEntry"
|
|
|
|
value={this.state.token}
|
|
|
|
onChange={this._onTokenChange}
|
2017-12-05 15:52:20 +03:00
|
|
|
aria-label={ _t("Code")}
|
2017-03-14 14:50:13 +03:00
|
|
|
/>
|
|
|
|
<br />
|
2017-06-02 12:18:31 +03:00
|
|
|
<input type="submit" value={_t("Submit")}
|
2017-03-14 14:50:13 +03:00
|
|
|
className={submitClasses}
|
|
|
|
disabled={!enableSubmit}
|
|
|
|
/>
|
|
|
|
</form>
|
2017-12-14 12:31:28 +03:00
|
|
|
{errorSection}
|
2017-03-14 14:50:13 +03:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2016-10-11 20:04:55 +03:00
|
|
|
export const FallbackAuthEntry = React.createClass({
|
|
|
|
displayName: 'FallbackAuthEntry',
|
|
|
|
|
|
|
|
propTypes: {
|
2017-12-26 04:03:18 +03:00
|
|
|
matrixClient: PropTypes.object.isRequired,
|
|
|
|
authSessionId: PropTypes.string.isRequired,
|
|
|
|
loginType: PropTypes.string.isRequired,
|
|
|
|
submitAuthDict: PropTypes.func.isRequired,
|
|
|
|
errorText: PropTypes.string,
|
2016-10-11 20:04:55 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
componentWillMount: function() {
|
|
|
|
// we have to make the user click a button, as browsers will block
|
|
|
|
// the popup if we open it immediately.
|
|
|
|
this._popupWindow = null;
|
|
|
|
window.addEventListener("message", this._onReceiveMessage);
|
|
|
|
},
|
|
|
|
|
|
|
|
componentWillUnmount: function() {
|
|
|
|
window.removeEventListener("message", this._onReceiveMessage);
|
|
|
|
if (this._popupWindow) {
|
|
|
|
this._popupWindow.close();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2017-12-05 15:52:20 +03:00
|
|
|
focus: function() {
|
|
|
|
if (this.refs.fallbackButton) {
|
|
|
|
this.refs.fallbackButton.focus();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2017-02-13 22:09:43 +03:00
|
|
|
_onShowFallbackClick: function() {
|
2017-10-11 19:56:17 +03:00
|
|
|
const url = this.props.matrixClient.getFallbackAuthUrl(
|
2016-10-11 20:04:55 +03:00
|
|
|
this.props.loginType,
|
2017-10-11 19:56:17 +03:00
|
|
|
this.props.authSessionId,
|
2016-10-11 20:04:55 +03:00
|
|
|
);
|
|
|
|
this._popupWindow = window.open(url);
|
|
|
|
},
|
|
|
|
|
|
|
|
_onReceiveMessage: function(event) {
|
|
|
|
if (
|
|
|
|
event.data === "authDone" &&
|
2017-02-24 14:41:23 +03:00
|
|
|
event.origin === this.props.matrixClient.getHomeserverUrl()
|
2016-10-11 20:04:55 +03:00
|
|
|
) {
|
|
|
|
this.props.submitAuthDict({});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
render: function() {
|
2017-12-14 12:31:28 +03:00
|
|
|
let errorSection;
|
|
|
|
if (this.props.errorText) {
|
|
|
|
errorSection = (
|
|
|
|
<div className="error" role="alert">
|
|
|
|
{ this.props.errorText }
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2016-10-11 20:04:55 +03:00
|
|
|
return (
|
|
|
|
<div>
|
2017-12-05 15:52:20 +03:00
|
|
|
<a ref="fallbackButton" onClick={this._onShowFallbackClick}>{ _t("Start authentication") }</a>
|
2017-12-14 12:31:28 +03:00
|
|
|
{errorSection}
|
2016-10-11 20:04:55 +03:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const AuthEntryComponents = [
|
|
|
|
PasswordAuthEntry,
|
|
|
|
RecaptchaAuthEntry,
|
2017-02-24 14:41:23 +03:00
|
|
|
EmailIdentityAuthEntry,
|
2017-03-14 14:50:13 +03:00
|
|
|
MsisdnAuthEntry,
|
2018-10-16 01:12:42 +03:00
|
|
|
TermsAuthEntry,
|
2016-10-11 20:04:55 +03:00
|
|
|
];
|
|
|
|
|
|
|
|
export function getEntryComponentForLoginType(loginType) {
|
2017-10-11 19:56:17 +03:00
|
|
|
for (const c of AuthEntryComponents) {
|
2016-10-11 20:04:55 +03:00
|
|
|
if (c.LOGIN_TYPE == loginType) {
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FallbackAuthEntry;
|
2017-01-20 17:22:27 +03:00
|
|
|
}
|