Improve SSO auth flow
Use replaceState instead of a redirect to strip the loginToken Put user into the same post-auth flows of E2ESetup Skip UIA prompt in this post-auth flow, happy path is a server grace periodpull/21833/head
parent
048a3f6ec8
commit
e6673bca1b
|
@ -366,7 +366,7 @@ async function abortLogin() {
|
||||||
// The plan is to gradually move the localStorage access done here into
|
// The plan is to gradually move the localStorage access done here into
|
||||||
// SessionStore to avoid bugs where the view becomes out-of-sync with
|
// SessionStore to avoid bugs where the view becomes out-of-sync with
|
||||||
// localStorage (e.g. isGuest etc.)
|
// localStorage (e.g. isGuest etc.)
|
||||||
async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }): Promise<boolean> {
|
export async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }): Promise<boolean> {
|
||||||
const ignoreGuest = opts?.ignoreGuest;
|
const ignoreGuest = opts?.ignoreGuest;
|
||||||
|
|
||||||
if (!localStorage) {
|
if (!localStorage) {
|
||||||
|
|
|
@ -218,6 +218,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
private screenAfterLogin?: IScreen;
|
private screenAfterLogin?: IScreen;
|
||||||
private windowWidth: number;
|
private windowWidth: number;
|
||||||
private pageChanging: boolean;
|
private pageChanging: boolean;
|
||||||
|
private tokenLogin?: boolean;
|
||||||
private accountPassword?: string;
|
private accountPassword?: string;
|
||||||
private accountPasswordTimer?: NodeJS.Timeout;
|
private accountPasswordTimer?: NodeJS.Timeout;
|
||||||
private focusComposer: boolean;
|
private focusComposer: boolean;
|
||||||
|
@ -323,13 +324,16 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
Lifecycle.attemptTokenLogin(
|
Lifecycle.attemptTokenLogin(
|
||||||
this.props.realQueryParams,
|
this.props.realQueryParams,
|
||||||
this.props.defaultDeviceDisplayName,
|
this.props.defaultDeviceDisplayName,
|
||||||
).then((loggedIn) => {
|
).then(async (loggedIn) => {
|
||||||
if (loggedIn) {
|
if (loggedIn) {
|
||||||
|
this.tokenLogin = true;
|
||||||
this.props.onTokenLoginCompleted();
|
this.props.onTokenLoginCompleted();
|
||||||
|
|
||||||
// don't do anything else until the page reloads - just stay in
|
// Create and start the client
|
||||||
// the 'loading' state.
|
await Lifecycle.restoreFromLocalStorage({
|
||||||
return;
|
ignoreGuest: true,
|
||||||
|
});
|
||||||
|
return this.postLoginSetup();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the user has followed a login or register link, don't reanimate
|
// if the user has followed a login or register link, don't reanimate
|
||||||
|
@ -353,6 +357,39 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
CountlyAnalytics.instance.enable(/* anonymous = */ true);
|
CountlyAnalytics.instance.enable(/* anonymous = */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async postLoginSetup() {
|
||||||
|
const cli = MatrixClientPeg.get();
|
||||||
|
const cryptoEnabled = cli.isCryptoEnabled();
|
||||||
|
|
||||||
|
const promisesList = [this.firstSyncPromise.promise];
|
||||||
|
if (cryptoEnabled) {
|
||||||
|
// wait for the client to finish downloading cross-signing keys for us so we
|
||||||
|
// know whether or not we have keys set up on this account
|
||||||
|
promisesList.push(cli.downloadKeys([cli.getUserId()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now update the state to say we're waiting for the first sync to complete rather
|
||||||
|
// than for the login to finish.
|
||||||
|
this.setState({ pendingInitialSync: true });
|
||||||
|
|
||||||
|
await Promise.all(promisesList);
|
||||||
|
|
||||||
|
if (!cryptoEnabled) {
|
||||||
|
this.setState({ pendingInitialSync: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const crossSigningIsSetUp = cli.getStoredCrossSigningForUser(cli.getUserId());
|
||||||
|
if (crossSigningIsSetUp) {
|
||||||
|
this.setStateForNewView({ view: Views.COMPLETE_SECURITY });
|
||||||
|
} else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) {
|
||||||
|
this.setStateForNewView({ view: Views.E2E_SETUP });
|
||||||
|
} else {
|
||||||
|
this.onLoggedIn();
|
||||||
|
}
|
||||||
|
this.setState({ pendingInitialSync: false });
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle stage
|
// TODO: [REACT-WARNING] Replace with appropriate lifecycle stage
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
UNSAFE_componentWillUpdate(props, state) {
|
UNSAFE_componentWillUpdate(props, state) {
|
||||||
|
@ -1833,40 +1870,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
|
|
||||||
// Create and start the client
|
// Create and start the client
|
||||||
await Lifecycle.setLoggedIn(credentials);
|
await Lifecycle.setLoggedIn(credentials);
|
||||||
|
await this.postLoginSetup();
|
||||||
const cli = MatrixClientPeg.get();
|
|
||||||
const cryptoEnabled = cli.isCryptoEnabled();
|
|
||||||
if (!cryptoEnabled) {
|
|
||||||
this.onLoggedIn();
|
|
||||||
}
|
|
||||||
|
|
||||||
const promisesList = [this.firstSyncPromise.promise];
|
|
||||||
if (cryptoEnabled) {
|
|
||||||
// wait for the client to finish downloading cross-signing keys for us so we
|
|
||||||
// know whether or not we have keys set up on this account
|
|
||||||
promisesList.push(cli.downloadKeys([cli.getUserId()]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now update the state to say we're waiting for the first sync to complete rather
|
|
||||||
// than for the login to finish.
|
|
||||||
this.setState({ pendingInitialSync: true });
|
|
||||||
|
|
||||||
await Promise.all(promisesList);
|
|
||||||
|
|
||||||
if (!cryptoEnabled) {
|
|
||||||
this.setState({ pendingInitialSync: false });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const crossSigningIsSetUp = cli.getStoredCrossSigningForUser(cli.getUserId());
|
|
||||||
if (crossSigningIsSetUp) {
|
|
||||||
this.setStateForNewView({ view: Views.COMPLETE_SECURITY });
|
|
||||||
} else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) {
|
|
||||||
this.setStateForNewView({ view: Views.E2E_SETUP });
|
|
||||||
} else {
|
|
||||||
this.onLoggedIn();
|
|
||||||
}
|
|
||||||
this.setState({ pendingInitialSync: false });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// complete security / e2e setup has finished
|
// complete security / e2e setup has finished
|
||||||
|
@ -1910,6 +1914,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
<E2eSetup
|
<E2eSetup
|
||||||
onFinished={this.onCompleteSecurityE2eSetupFinished}
|
onFinished={this.onCompleteSecurityE2eSetupFinished}
|
||||||
accountPassword={this.accountPassword}
|
accountPassword={this.accountPassword}
|
||||||
|
tokenLogin={!!this.tokenLogin}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (this.state.view === Views.LOGGED_IN) {
|
} else if (this.state.view === Views.LOGGED_IN) {
|
||||||
|
|
|
@ -24,6 +24,7 @@ export default class E2eSetup extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
accountPassword: PropTypes.string,
|
accountPassword: PropTypes.string,
|
||||||
|
tokenLogin: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -33,6 +34,7 @@ export default class E2eSetup extends React.Component {
|
||||||
<CreateCrossSigningDialog
|
<CreateCrossSigningDialog
|
||||||
onFinished={this.props.onFinished}
|
onFinished={this.props.onFinished}
|
||||||
accountPassword={this.props.accountPassword}
|
accountPassword={this.props.accountPassword}
|
||||||
|
tokenLogin={this.props.tokenLogin}
|
||||||
/>
|
/>
|
||||||
</CompleteSecurityBody>
|
</CompleteSecurityBody>
|
||||||
</AuthPage>
|
</AuthPage>
|
||||||
|
|
|
@ -34,6 +34,7 @@ import InteractiveAuthDialog from '../InteractiveAuthDialog';
|
||||||
export default class CreateCrossSigningDialog extends React.PureComponent {
|
export default class CreateCrossSigningDialog extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
accountPassword: PropTypes.string,
|
accountPassword: PropTypes.string,
|
||||||
|
tokenLogin: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -96,6 +97,9 @@ export default class CreateCrossSigningDialog extends React.PureComponent {
|
||||||
user: MatrixClientPeg.get().getUserId(),
|
user: MatrixClientPeg.get().getUserId(),
|
||||||
password: this.state.accountPassword,
|
password: this.state.accountPassword,
|
||||||
});
|
});
|
||||||
|
} else if (this.props.tokenLogin) {
|
||||||
|
// We are hoping the grace period is active
|
||||||
|
await makeRequest({});
|
||||||
} else {
|
} else {
|
||||||
const dialogAesthetics = {
|
const dialogAesthetics = {
|
||||||
[SSOAuthEntry.PHASE_PREAUTH]: {
|
[SSOAuthEntry.PHASE_PREAUTH]: {
|
||||||
|
@ -144,6 +148,12 @@ export default class CreateCrossSigningDialog extends React.PureComponent {
|
||||||
});
|
});
|
||||||
this.props.onFinished(true);
|
this.props.onFinished(true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (this.props.tokenLogin) {
|
||||||
|
// ignore any failures, we are relying on grace period here
|
||||||
|
this.props.onFinished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({ error: e });
|
this.setState({ error: e });
|
||||||
console.error("Error bootstrapping cross-signing", e);
|
console.error("Error bootstrapping cross-signing", e);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue