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,29 +283,32 @@ export default React.createClass({
|
|||
}
|
||||
|
||||
// the first thing to do is to try the token params in the query-string
|
||||
Lifecycle.attemptTokenLogin(this.props.realQueryParams).then((loggedIn) => {
|
||||
if (loggedIn) {
|
||||
this.props.onTokenLoginCompleted();
|
||||
// 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) => {
|
||||
if (loggedIn) {
|
||||
this.props.onTokenLoginCompleted();
|
||||
|
||||
// don't do anything else until the page reloads - just stay in
|
||||
// the 'loading' state.
|
||||
return;
|
||||
}
|
||||
// don't do anything else until the page reloads - just stay in
|
||||
// the 'loading' state.
|
||||
return;
|
||||
}
|
||||
|
||||
// if the user has followed a login or register link, don't reanimate
|
||||
// the old creds, but rather go straight to the relevant page
|
||||
const firstScreen = this._screenAfterLogin ?
|
||||
this._screenAfterLogin.screen : null;
|
||||
// if the user has followed a login or register link, don't reanimate
|
||||
// the old creds, but rather go straight to the relevant page
|
||||
const firstScreen = this._screenAfterLogin ?
|
||||
this._screenAfterLogin.screen : null;
|
||||
|
||||
if (firstScreen === 'login' ||
|
||||
if (firstScreen === 'login' ||
|
||||
firstScreen === 'register' ||
|
||||
firstScreen === 'forgot_password') {
|
||||
this._showScreenAfterLogin();
|
||||
return;
|
||||
}
|
||||
this._showScreenAfterLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
return this._loadSession();
|
||||
});
|
||||
return this._loadSession();
|
||||
});
|
||||
}
|
||||
|
||||
if (SettingsStore.getValue("showCookieBar")) {
|
||||
this.setState({
|
||||
|
@ -1250,10 +1253,7 @@ export default React.createClass({
|
|||
this._screenAfterLogin = null;
|
||||
} else if (localStorage && localStorage.getItem('mx_last_room_id')) {
|
||||
// Before defaulting to directory, show the last viewed room
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: localStorage.getItem('mx_last_room_id'),
|
||||
});
|
||||
this._viewLastRoom();
|
||||
} else {
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
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
|
||||
*/
|
||||
|
@ -1565,6 +1572,17 @@ export default React.createClass({
|
|||
action: 'start_password_recovery',
|
||||
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') {
|
||||
dis.dispatch({
|
||||
action: 'view_create_room',
|
||||
|
@ -1957,7 +1975,10 @@ export default React.createClass({
|
|||
if (this.state.view === VIEWS.SOFT_LOGOUT) {
|
||||
const SoftLogout = sdk.getComponent('structures.auth.SoftLogout');
|
||||
return (
|
||||
<SoftLogout />
|
||||
<SoftLogout
|
||||
realQueryParams={this.props.realQueryParams}
|
||||
onTokenLoginCompleted={this.props.onTokenLoginCompleted}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {_t} from '../../../languageHandler';
|
||||
import sdk from '../../../index';
|
||||
import dis from '../../../dispatcher';
|
||||
|
@ -24,6 +25,7 @@ import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
|
|||
import SdkConfig from "../../../SdkConfig";
|
||||
import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||
import {sendLoginRequest} from "../../../Login";
|
||||
import url from 'url';
|
||||
|
||||
const LOGIN_VIEW = {
|
||||
LOADING: 1,
|
||||
|
@ -41,7 +43,11 @@ const FLOWS_TO_VIEWS = {
|
|||
|
||||
export default class SoftLogout extends React.Component {
|
||||
static propTypes = {
|
||||
// Nothing.
|
||||
// Query parameters from MatrixChat
|
||||
realQueryParams: PropTypes.object, // {homeserver, identityServer, loginToken}
|
||||
|
||||
// Called when the SSO login completes
|
||||
onTokenLoginCompleted: PropTypes.func,
|
||||
};
|
||||
|
||||
constructor() {
|
||||
|
@ -67,6 +73,7 @@ export default class SoftLogout extends React.Component {
|
|||
displayName,
|
||||
loginView: LOGIN_VIEW.LOADING,
|
||||
keyBackupNeeded: true, // assume we do while we figure it out (see componentWillMount)
|
||||
ssoUrl: null,
|
||||
|
||||
busy: false,
|
||||
password: "",
|
||||
|
@ -75,6 +82,12 @@ export default class SoftLogout extends React.Component {
|
|||
}
|
||||
|
||||
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();
|
||||
|
||||
MatrixClientPeg.get().flagAllGroupSessionsForBackup().then(remaining => {
|
||||
|
@ -95,6 +108,15 @@ export default class SoftLogout extends React.Component {
|
|||
};
|
||||
|
||||
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
|
||||
// care about login flows here, unless it is the single flow we support.
|
||||
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;
|
||||
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) => {
|
||||
|
@ -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() {
|
||||
if (this.state.loginView === LOGIN_VIEW.LOADING) {
|
||||
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) {
|
||||
// TODO: TravisR - https://github.com/vector-im/riot-web/issues/10238
|
||||
return <p>PLACEHOLDER</p>;
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
return (
|
||||
<AccessibleButton kind='primary' onClick={this.onSsoLogin}>
|
||||
{_t('Sign in with single sign-on')}
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
||||
// Default: assume unsupported
|
||||
|
|
Loading…
Reference in a new issue