mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-17 05:55:00 +08:00
Avoid transitioning to loggedIn state during token login
Fixes riot-web#4334 When we do a token login, don't carry on with the normal app startup (transitioning to the loggedIn state etc) - instead tell the app about the successful login and wait for it to redirect. Replace onLoadCompleted with onTokenLoginCompleted so that the app can see what it's supposed to be doing.
This commit is contained in:
parent
6a2c2d64fa
commit
eb1fc9ae2d
102
src/Lifecycle.js
102
src/Lifecycle.js
@ -35,26 +35,20 @@ import { _t } from './languageHandler';
|
||||
* Called at startup, to attempt to build a logged-in Matrix session. It tries
|
||||
* a number of things:
|
||||
*
|
||||
* 1. if we have a loginToken in the (real) query params, it uses that to log
|
||||
* in.
|
||||
*
|
||||
* 2. if we have a guest access token in the fragment query params, it uses
|
||||
* 1. if we have a guest access token in the fragment query params, it uses
|
||||
* that.
|
||||
*
|
||||
* 3. if an access token is stored in local storage (from a previous session),
|
||||
* 2. if an access token is stored in local storage (from a previous session),
|
||||
* it uses that.
|
||||
*
|
||||
* 4. it attempts to auto-register as a guest user.
|
||||
* 3. it attempts to auto-register as a guest user.
|
||||
*
|
||||
* If any of steps 1-4 are successful, it will call {_doSetLoggedIn}, which in
|
||||
* turn will raise on_logged_in and will_start_client events.
|
||||
*
|
||||
* @param {object} opts
|
||||
*
|
||||
* @param {object} opts.realQueryParams: string->string map of the
|
||||
* query-parameters extracted from the real query-string of the starting
|
||||
* URI.
|
||||
*
|
||||
* @param {object} opts.fragmentQueryParams: string->string map of the
|
||||
* query-parameters extracted from the #-fragment of the starting URI.
|
||||
*
|
||||
@ -70,7 +64,6 @@ import { _t } from './languageHandler';
|
||||
* @returns {Promise} a promise which resolves when the above process completes.
|
||||
*/
|
||||
export function loadSession(opts) {
|
||||
const realQueryParams = opts.realQueryParams || {};
|
||||
const fragmentQueryParams = opts.fragmentQueryParams || {};
|
||||
let enableGuest = opts.enableGuest || false;
|
||||
const guestHsUrl = opts.guestHsUrl;
|
||||
@ -82,14 +75,6 @@ export function loadSession(opts) {
|
||||
enableGuest = false;
|
||||
}
|
||||
|
||||
if (realQueryParams.loginToken) {
|
||||
if (!realQueryParams.homeserver) {
|
||||
console.warn("Cannot log in with token: can't determine HS URL to use");
|
||||
} else {
|
||||
return _loginWithToken(realQueryParams, defaultDeviceDisplayName);
|
||||
}
|
||||
}
|
||||
|
||||
if (enableGuest &&
|
||||
fragmentQueryParams.guest_user_id &&
|
||||
fragmentQueryParams.guest_access_token
|
||||
@ -117,7 +102,26 @@ export function loadSession(opts) {
|
||||
});
|
||||
}
|
||||
|
||||
function _loginWithToken(queryParams, defaultDeviceDisplayName) {
|
||||
/**
|
||||
* @param {Object} queryParams string->string map of the
|
||||
* query-parameters extracted from the real query-string of the starting
|
||||
* URI.
|
||||
*
|
||||
* @param {String} defaultDeviceDisplayName
|
||||
*
|
||||
* @returns {Promise} promise which resolves to true if we completed the token
|
||||
* login, else false
|
||||
*/
|
||||
export function attemptTokenLogin(queryParams, defaultDeviceDisplayName) {
|
||||
if (!queryParams.loginToken) {
|
||||
return q(false);
|
||||
}
|
||||
|
||||
if (!queryParams.homeserver) {
|
||||
console.warn("Cannot log in with token: can't determine HS URL to use");
|
||||
return q(false);
|
||||
}
|
||||
|
||||
// create a temporary MatrixClient to do the login
|
||||
const client = Matrix.createClient({
|
||||
baseUrl: queryParams.homeserver,
|
||||
@ -130,17 +134,21 @@ function _loginWithToken(queryParams, defaultDeviceDisplayName) {
|
||||
},
|
||||
).then(function(data) {
|
||||
console.log("Logged in with token");
|
||||
return _doSetLoggedIn({
|
||||
userId: data.user_id,
|
||||
deviceId: data.device_id,
|
||||
accessToken: data.access_token,
|
||||
homeserverUrl: queryParams.homeserver,
|
||||
identityServerUrl: queryParams.identityServer,
|
||||
guest: false,
|
||||
}, true);
|
||||
}, (err) => {
|
||||
return _clearStorage().then(() => {
|
||||
_persistCredentialsToLocalStorage({
|
||||
userId: data.user_id,
|
||||
deviceId: data.device_id,
|
||||
accessToken: data.access_token,
|
||||
homeserverUrl: queryParams.homeserver,
|
||||
identityServerUrl: queryParams.identityServer,
|
||||
guest: false,
|
||||
});
|
||||
return true;
|
||||
});
|
||||
}).catch((err) => {
|
||||
console.error("Failed to log in with login token: " + err + " " +
|
||||
err.data);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@ -322,23 +330,10 @@ async function _doSetLoggedIn(credentials, clearStorage) {
|
||||
// Resolves by default
|
||||
let teamPromise = Promise.resolve(null);
|
||||
|
||||
// persist the session
|
||||
|
||||
if (localStorage) {
|
||||
try {
|
||||
localStorage.setItem("mx_hs_url", credentials.homeserverUrl);
|
||||
localStorage.setItem("mx_is_url", credentials.identityServerUrl);
|
||||
localStorage.setItem("mx_user_id", credentials.userId);
|
||||
localStorage.setItem("mx_access_token", credentials.accessToken);
|
||||
localStorage.setItem("mx_is_guest", JSON.stringify(credentials.guest));
|
||||
|
||||
// if we didn't get a deviceId from the login, leave mx_device_id unset,
|
||||
// rather than setting it to "undefined".
|
||||
//
|
||||
// (in this case MatrixClient doesn't bother with the crypto stuff
|
||||
// - that's fine for us).
|
||||
if (credentials.deviceId) {
|
||||
localStorage.setItem("mx_device_id", credentials.deviceId);
|
||||
}
|
||||
_persistCredentialsToLocalStorage(credentials);
|
||||
|
||||
// The user registered as a PWLU (PassWord-Less User), the generated password
|
||||
// is cached here such that the user can change it at a later time.
|
||||
@ -349,8 +344,6 @@ async function _doSetLoggedIn(credentials, clearStorage) {
|
||||
cachedPassword: credentials.password,
|
||||
});
|
||||
}
|
||||
|
||||
console.log("Session persisted for %s", credentials.userId);
|
||||
} catch (e) {
|
||||
console.warn("Error using local storage: can't persist session!", e);
|
||||
}
|
||||
@ -379,6 +372,25 @@ async function _doSetLoggedIn(credentials, clearStorage) {
|
||||
startMatrixClient();
|
||||
}
|
||||
|
||||
function _persistCredentialsToLocalStorage(credentials) {
|
||||
localStorage.setItem("mx_hs_url", credentials.homeserverUrl);
|
||||
localStorage.setItem("mx_is_url", credentials.identityServerUrl);
|
||||
localStorage.setItem("mx_user_id", credentials.userId);
|
||||
localStorage.setItem("mx_access_token", credentials.accessToken);
|
||||
localStorage.setItem("mx_is_guest", JSON.stringify(credentials.guest));
|
||||
|
||||
// if we didn't get a deviceId from the login, leave mx_device_id unset,
|
||||
// rather than setting it to "undefined".
|
||||
//
|
||||
// (in this case MatrixClient doesn't bother with the crypto stuff
|
||||
// - that's fine for us).
|
||||
if (credentials.deviceId) {
|
||||
localStorage.setItem("mx_device_id", credentials.deviceId);
|
||||
}
|
||||
|
||||
console.log("Session persisted for %s", credentials.userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the current session out and transitions to the logged-out state
|
||||
*/
|
||||
|
@ -59,8 +59,8 @@ module.exports = React.createClass({
|
||||
// the initial queryParams extracted from the hash-fragment of the URI
|
||||
startingFragmentQueryParams: React.PropTypes.object,
|
||||
|
||||
// called when the session load completes
|
||||
onLoadCompleted: React.PropTypes.func,
|
||||
// called when we have completed a token login
|
||||
onTokenLoginCompleted: React.PropTypes.func,
|
||||
|
||||
// Represents the screen to display as a result of parsing the initial
|
||||
// window.location
|
||||
@ -143,7 +143,7 @@ module.exports = React.createClass({
|
||||
realQueryParams: {},
|
||||
startingFragmentQueryParams: {},
|
||||
config: {},
|
||||
onLoadCompleted: () => {},
|
||||
onTokenLoginCompleted: () => {},
|
||||
};
|
||||
},
|
||||
|
||||
@ -266,39 +266,47 @@ module.exports = React.createClass({
|
||||
const teamServerConfig = this.props.config.teamServerConfig || {};
|
||||
Lifecycle.initRtsClient(teamServerConfig.teamServerURL);
|
||||
|
||||
// if the user has followed a login or register link, don't reanimate
|
||||
// the old creds, but rather go straight to the relevant page
|
||||
// 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();
|
||||
|
||||
const firstScreen = this.state.screenAfterLogin ?
|
||||
this.state.screenAfterLogin.screen : null;
|
||||
// don't do anything else until the page reloads - just stay in
|
||||
// the 'loading' state.
|
||||
return;
|
||||
}
|
||||
|
||||
if (firstScreen === 'login' ||
|
||||
firstScreen === 'register' ||
|
||||
firstScreen === 'forgot_password') {
|
||||
this.props.onLoadCompleted();
|
||||
this.setState({loading: false});
|
||||
this._showScreenAfterLogin();
|
||||
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.state.screenAfterLogin ?
|
||||
this.state.screenAfterLogin.screen : null;
|
||||
|
||||
// the extra q() ensures that synchronous exceptions hit the same codepath as
|
||||
// asynchronous ones.
|
||||
q().then(() => {
|
||||
return Lifecycle.loadSession({
|
||||
realQueryParams: this.props.realQueryParams,
|
||||
fragmentQueryParams: this.props.startingFragmentQueryParams,
|
||||
enableGuest: this.props.enableGuest,
|
||||
guestHsUrl: this.getCurrentHsUrl(),
|
||||
guestIsUrl: this.getCurrentIsUrl(),
|
||||
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
|
||||
if (firstScreen === 'login' ||
|
||||
firstScreen === 'register' ||
|
||||
firstScreen === 'forgot_password') {
|
||||
this.setState({loading: false});
|
||||
this._showScreenAfterLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
// the extra q() ensures that synchronous exceptions hit the same codepath as
|
||||
// asynchronous ones.
|
||||
return q().then(() => {
|
||||
return Lifecycle.loadSession({
|
||||
fragmentQueryParams: this.props.startingFragmentQueryParams,
|
||||
enableGuest: this.props.enableGuest,
|
||||
guestHsUrl: this.getCurrentHsUrl(),
|
||||
guestIsUrl: this.getCurrentIsUrl(),
|
||||
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
|
||||
});
|
||||
}).catch((e) => {
|
||||
console.error("Unable to load session", e);
|
||||
}).then(()=>{
|
||||
// stuff this through the dispatcher so that it happens
|
||||
// after the on_logged_in action.
|
||||
dis.dispatch({action: 'load_completed'});
|
||||
});
|
||||
}).catch((e) => {
|
||||
console.error("Unable to load session", e);
|
||||
}).done(()=>{
|
||||
// stuff this through the dispatcher so that it happens
|
||||
// after the on_logged_in action.
|
||||
dis.dispatch({action: 'load_completed'});
|
||||
});
|
||||
}).done();
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
@ -850,7 +858,6 @@ module.exports = React.createClass({
|
||||
* Called when the sessionloader has finished
|
||||
*/
|
||||
_onLoadCompleted: function() {
|
||||
this.props.onLoadCompleted();
|
||||
this.setState({loading: false});
|
||||
},
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user