Support SSO for rehydrating a soft-logged-out session.
Fixes https://github.com/vector-im/riot-web/issues/10238
This commit is contained in:
parent
6cb148a88a
commit
1eb60ef1c4
2 changed files with 120 additions and 25 deletions
|
@ -283,6 +283,8 @@ export default React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
// the first thing to do is to try the token params in the query-string
|
// the first thing to do is to try the token params in the query-string
|
||||||
|
// if the session isn't soft logged out (ie: is a clean session being logged in)
|
||||||
|
if (!Lifecycle.isSoftLogout()) {
|
||||||
Lifecycle.attemptTokenLogin(this.props.realQueryParams).then((loggedIn) => {
|
Lifecycle.attemptTokenLogin(this.props.realQueryParams).then((loggedIn) => {
|
||||||
if (loggedIn) {
|
if (loggedIn) {
|
||||||
this.props.onTokenLoginCompleted();
|
this.props.onTokenLoginCompleted();
|
||||||
|
@ -306,6 +308,7 @@ export default React.createClass({
|
||||||
|
|
||||||
return this._loadSession();
|
return this._loadSession();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (SettingsStore.getValue("showCookieBar")) {
|
if (SettingsStore.getValue("showCookieBar")) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -1250,10 +1253,7 @@ export default React.createClass({
|
||||||
this._screenAfterLogin = null;
|
this._screenAfterLogin = null;
|
||||||
} else if (localStorage && localStorage.getItem('mx_last_room_id')) {
|
} else if (localStorage && localStorage.getItem('mx_last_room_id')) {
|
||||||
// Before defaulting to directory, show the last viewed room
|
// Before defaulting to directory, show the last viewed room
|
||||||
dis.dispatch({
|
this._viewLastRoom();
|
||||||
action: 'view_room',
|
|
||||||
room_id: localStorage.getItem('mx_last_room_id'),
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
if (MatrixClientPeg.get().isGuest()) {
|
if (MatrixClientPeg.get().isGuest()) {
|
||||||
dis.dispatch({action: 'view_welcome_page'});
|
dis.dispatch({action: 'view_welcome_page'});
|
||||||
|
@ -1267,6 +1267,13 @@ export default React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_viewLastRoom: function() {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'view_room',
|
||||||
|
room_id: localStorage.getItem('mx_last_room_id'),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the session is logged out
|
* Called when the session is logged out
|
||||||
*/
|
*/
|
||||||
|
@ -1565,6 +1572,17 @@ export default React.createClass({
|
||||||
action: 'start_password_recovery',
|
action: 'start_password_recovery',
|
||||||
params: params,
|
params: params,
|
||||||
});
|
});
|
||||||
|
} else if (screen === 'soft_logout') {
|
||||||
|
if (MatrixClientPeg.get() && MatrixClientPeg.get().getUserId()) {
|
||||||
|
// Logged in - visit a room
|
||||||
|
this._viewLastRoom();
|
||||||
|
} else {
|
||||||
|
// Ultimately triggers soft_logout if needed
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'start_login',
|
||||||
|
params: params,
|
||||||
|
});
|
||||||
|
}
|
||||||
} else if (screen == 'new') {
|
} else if (screen == 'new') {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_create_room',
|
action: 'view_create_room',
|
||||||
|
@ -1957,7 +1975,10 @@ export default React.createClass({
|
||||||
if (this.state.view === VIEWS.SOFT_LOGOUT) {
|
if (this.state.view === VIEWS.SOFT_LOGOUT) {
|
||||||
const SoftLogout = sdk.getComponent('structures.auth.SoftLogout');
|
const SoftLogout = sdk.getComponent('structures.auth.SoftLogout');
|
||||||
return (
|
return (
|
||||||
<SoftLogout />
|
<SoftLogout
|
||||||
|
realQueryParams={this.props.realQueryParams}
|
||||||
|
onTokenLoginCompleted={this.props.onTokenLoginCompleted}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import {_t} from '../../../languageHandler';
|
import {_t} from '../../../languageHandler';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import dis from '../../../dispatcher';
|
import dis from '../../../dispatcher';
|
||||||
|
@ -24,6 +25,7 @@ import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
|
||||||
import SdkConfig from "../../../SdkConfig";
|
import SdkConfig from "../../../SdkConfig";
|
||||||
import MatrixClientPeg from "../../../MatrixClientPeg";
|
import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||||
import {sendLoginRequest} from "../../../Login";
|
import {sendLoginRequest} from "../../../Login";
|
||||||
|
import url from 'url';
|
||||||
|
|
||||||
const LOGIN_VIEW = {
|
const LOGIN_VIEW = {
|
||||||
LOADING: 1,
|
LOADING: 1,
|
||||||
|
@ -41,7 +43,11 @@ const FLOWS_TO_VIEWS = {
|
||||||
|
|
||||||
export default class SoftLogout extends React.Component {
|
export default class SoftLogout extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
// Nothing.
|
// Query parameters from MatrixChat
|
||||||
|
realQueryParams: PropTypes.object, // {homeserver, identityServer, loginToken}
|
||||||
|
|
||||||
|
// Called when the SSO login completes
|
||||||
|
onTokenLoginCompleted: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -67,6 +73,7 @@ export default class SoftLogout extends React.Component {
|
||||||
displayName,
|
displayName,
|
||||||
loginView: LOGIN_VIEW.LOADING,
|
loginView: LOGIN_VIEW.LOADING,
|
||||||
keyBackupNeeded: true, // assume we do while we figure it out (see componentWillMount)
|
keyBackupNeeded: true, // assume we do while we figure it out (see componentWillMount)
|
||||||
|
ssoUrl: null,
|
||||||
|
|
||||||
busy: false,
|
busy: false,
|
||||||
password: "",
|
password: "",
|
||||||
|
@ -75,6 +82,12 @@ export default class SoftLogout extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
|
// We've ended up here when we don't need to - navigate to login
|
||||||
|
if (!Lifecycle.isSoftLogout()) {
|
||||||
|
dis.dispatch({action: "on_logged_in"});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._initLogin();
|
this._initLogin();
|
||||||
|
|
||||||
MatrixClientPeg.get().flagAllGroupSessionsForBackup().then(remaining => {
|
MatrixClientPeg.get().flagAllGroupSessionsForBackup().then(remaining => {
|
||||||
|
@ -95,6 +108,15 @@ export default class SoftLogout extends React.Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
async _initLogin() {
|
async _initLogin() {
|
||||||
|
const requiredQueryParams = ['homeserver', 'loginToken'];
|
||||||
|
const hasAllParams = requiredQueryParams
|
||||||
|
.filter(p => Object.keys(this.props.realQueryParams).includes(p)).length === requiredQueryParams.length;
|
||||||
|
if (this.props.realQueryParams && hasAllParams) {
|
||||||
|
this.setState({loginView: LOGIN_VIEW.LOADING});
|
||||||
|
this.trySsoLogin();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Note: we don't use the existing Login class because it is heavily flow-based. We don't
|
// Note: we don't use the existing Login class because it is heavily flow-based. We don't
|
||||||
// care about login flows here, unless it is the single flow we support.
|
// care about login flows here, unless it is the single flow we support.
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
|
@ -102,6 +124,18 @@ export default class SoftLogout extends React.Component {
|
||||||
|
|
||||||
const chosenView = loginViews.filter(f => !!f)[0] || LOGIN_VIEW.UNSUPPORTED;
|
const chosenView = loginViews.filter(f => !!f)[0] || LOGIN_VIEW.UNSUPPORTED;
|
||||||
this.setState({loginView: chosenView});
|
this.setState({loginView: chosenView});
|
||||||
|
|
||||||
|
if (chosenView === LOGIN_VIEW.CAS || chosenView === LOGIN_VIEW.SSO) {
|
||||||
|
const client = MatrixClientPeg.get();
|
||||||
|
|
||||||
|
const appUrl = url.parse(window.location.href, true);
|
||||||
|
appUrl.hash = ""; // Clear #/soft_logout off the URL
|
||||||
|
appUrl.query["homeserver"] = client.getHomeserverUrl();
|
||||||
|
appUrl.query["identityServer"] = client.getIdentityServerUrl();
|
||||||
|
|
||||||
|
const ssoUrl = client.getSsoLoginUrl(url.format(appUrl), chosenView === LOGIN_VIEW.CAS ? "cas" : "sso");
|
||||||
|
this.setState({ssoUrl});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onPasswordChange = (ev) => {
|
onPasswordChange = (ev) => {
|
||||||
|
@ -152,6 +186,42 @@ export default class SoftLogout extends React.Component {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async trySsoLogin() {
|
||||||
|
this.setState({busy: true});
|
||||||
|
|
||||||
|
const hsUrl = this.props.realQueryParams['homeserver'];
|
||||||
|
const isUrl = this.props.realQueryParams['identityServer'] || MatrixClientPeg.get().getIdentityServerUrl();
|
||||||
|
const loginType = "m.login.token";
|
||||||
|
const loginParams = {
|
||||||
|
token: this.props.realQueryParams['loginToken'],
|
||||||
|
device_id: MatrixClientPeg.get().getDeviceId(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let credentials = null;
|
||||||
|
try {
|
||||||
|
credentials = await sendLoginRequest(hsUrl, isUrl, loginType, loginParams);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
this.setState({busy: false, loginView: LOGIN_VIEW.UNSUPPORTED});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lifecycle.hydrateSession(credentials).then(() => {
|
||||||
|
if (this.props.onTokenLoginCompleted) this.props.onTokenLoginCompleted();
|
||||||
|
}).catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
this.setState({busy: false, loginView: LOGIN_VIEW.UNSUPPORTED});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSsoLogin = async (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
this.setState({busy: true});
|
||||||
|
window.location.href = this.state.ssoUrl;
|
||||||
|
};
|
||||||
|
|
||||||
_renderSignInSection() {
|
_renderSignInSection() {
|
||||||
if (this.state.loginView === LOGIN_VIEW.LOADING) {
|
if (this.state.loginView === LOGIN_VIEW.LOADING) {
|
||||||
const Spinner = sdk.getComponent("elements.Spinner");
|
const Spinner = sdk.getComponent("elements.Spinner");
|
||||||
|
@ -202,8 +272,12 @@ export default class SoftLogout extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.loginView === LOGIN_VIEW.SSO || this.state.loginView === LOGIN_VIEW.CAS) {
|
if (this.state.loginView === LOGIN_VIEW.SSO || this.state.loginView === LOGIN_VIEW.CAS) {
|
||||||
// TODO: TravisR - https://github.com/vector-im/riot-web/issues/10238
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
return <p>PLACEHOLDER</p>;
|
return (
|
||||||
|
<AccessibleButton kind='primary' onClick={this.onSsoLogin}>
|
||||||
|
{_t('Sign in with single sign-on')}
|
||||||
|
</AccessibleButton>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default: assume unsupported
|
// Default: assume unsupported
|
||||||
|
|
Loading…
Reference in a new issue