Merge branch 'masto-auth-error' into 'develop'
Mastodon: return a friendlier login error than 'invalid_grant', redirect password reset to backend See merge request soapbox-pub/soapbox-fe!732
This commit is contained in:
commit
f2384ef178
|
@ -163,10 +163,18 @@ export function logIn(intl, username, password) {
|
||||||
return dispatch(createUserToken(username, password));
|
return dispatch(createUserToken(username, password));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (error.response.data.error === 'mfa_required') {
|
if (error.response.data.error === 'mfa_required') {
|
||||||
|
// If MFA is required, throw the error and handle it in the component.
|
||||||
throw error;
|
throw error;
|
||||||
} else if(error.response.data.error) {
|
} else if (error.response.data.error === 'invalid_grant') {
|
||||||
|
// Mastodon returns this user-unfriendly error as a catch-all
|
||||||
|
// for everything from "bad request" to "wrong password".
|
||||||
|
// Assume our code is correct and it's a wrong password.
|
||||||
|
dispatch(snackbar.error(intl.formatMessage(messages.invalidCredentials)));
|
||||||
|
} else if (error.response.data.error) {
|
||||||
|
// If the backend returns an error, display it.
|
||||||
dispatch(snackbar.error(error.response.data.error));
|
dispatch(snackbar.error(error.response.data.error));
|
||||||
} else {
|
} else {
|
||||||
|
// Return "wrong password" message.
|
||||||
dispatch(snackbar.error(intl.formatMessage(messages.invalidCredentials)));
|
dispatch(snackbar.error(intl.formatMessage(messages.invalidCredentials)));
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
|
|
|
@ -23,7 +23,7 @@ export function obtainOAuthToken(params, baseURL) {
|
||||||
dispatch({ type: OAUTH_TOKEN_CREATE_SUCCESS, params, token });
|
dispatch({ type: OAUTH_TOKEN_CREATE_SUCCESS, params, token });
|
||||||
return token;
|
return token;
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch({ type: OAUTH_TOKEN_CREATE_FAIL, params, error });
|
dispatch({ type: OAUTH_TOKEN_CREATE_FAIL, params, error, skipAlert: true });
|
||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* @module soapbox/build_config
|
* @module soapbox/build_config
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { trim } = require('lodash');
|
const { trim, trimEnd } = require('lodash');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
NODE_ENV,
|
NODE_ENV,
|
||||||
|
@ -15,7 +15,7 @@ const {
|
||||||
|
|
||||||
const sanitizeURL = url => {
|
const sanitizeURL = url => {
|
||||||
try {
|
try {
|
||||||
return new URL(url).toString();
|
return trimEnd(new URL(url).toString(), '/');
|
||||||
} catch {
|
} catch {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,18 +3,30 @@ import { connect } from 'react-redux';
|
||||||
import { injectIntl, FormattedMessage, defineMessages } from 'react-intl';
|
import { injectIntl, FormattedMessage, defineMessages } from 'react-intl';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
import { getFeatures } from 'soapbox/utils/features';
|
||||||
|
import { getBaseURL } from 'soapbox/utils/state';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
username: { id: 'login.fields.username_placeholder', defaultMessage: 'Username' },
|
username: { id: 'login.fields.username_placeholder', defaultMessage: 'Username' },
|
||||||
password: { id: 'login.fields.password_placeholder', defaultMessage: 'Password' },
|
password: { id: 'login.fields.password_placeholder', defaultMessage: 'Password' },
|
||||||
});
|
});
|
||||||
|
|
||||||
export default @connect()
|
const mapStateToProps = state => {
|
||||||
|
const instance = state.get('instance');
|
||||||
|
const features = getFeatures(instance);
|
||||||
|
|
||||||
|
return {
|
||||||
|
baseURL: getBaseURL(state),
|
||||||
|
hasResetPasswordAPI: features.resetPasswordAPI,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default @connect(mapStateToProps)
|
||||||
@injectIntl
|
@injectIntl
|
||||||
class LoginForm extends ImmutablePureComponent {
|
class LoginForm extends ImmutablePureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { intl, isLoading, handleSubmit } = this.props;
|
const { intl, isLoading, handleSubmit, baseURL, hasResetPasswordAPI } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className='simple_form new_user' method='post' onSubmit={handleSubmit}>
|
<form className='simple_form new_user' method='post' onSubmit={handleSubmit}>
|
||||||
|
@ -43,9 +55,15 @@ class LoginForm extends ImmutablePureComponent {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p className='hint subtle-hint'>
|
<p className='hint subtle-hint'>
|
||||||
|
{hasResetPasswordAPI ? (
|
||||||
<Link to='/auth/reset_password'>
|
<Link to='/auth/reset_password'>
|
||||||
<FormattedMessage id='login.reset_password_hint' defaultMessage='Trouble logging in?' />
|
<FormattedMessage id='login.reset_password_hint' defaultMessage='Trouble logging in?' />
|
||||||
</Link>
|
</Link>
|
||||||
|
) : (
|
||||||
|
<a href={`${baseURL}/auth/password/new`}>
|
||||||
|
<FormattedMessage id='login.reset_password_hint' defaultMessage='Trouble logging in?' />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
|
@ -24,6 +24,7 @@ export const getFeatures = createSelector([
|
||||||
securityAPI: v.software === 'Pleroma',
|
securityAPI: v.software === 'Pleroma',
|
||||||
settingsStore: v.software === 'Pleroma',
|
settingsStore: v.software === 'Pleroma',
|
||||||
accountAliasesAPI: v.software === 'Pleroma',
|
accountAliasesAPI: v.software === 'Pleroma',
|
||||||
|
resetPasswordAPI: v.software === 'Pleroma',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
import { getSoapboxConfig } from'soapbox/actions/soapbox';
|
import { getSoapboxConfig } from'soapbox/actions/soapbox';
|
||||||
import { isPrerendered } from 'soapbox/precheck';
|
import { isPrerendered } from 'soapbox/precheck';
|
||||||
import { isURL } from 'soapbox/utils/auth';
|
import { isURL } from 'soapbox/utils/auth';
|
||||||
|
import { getBaseURL as getAccountBaseURL } from 'soapbox/utils/accounts';
|
||||||
import { BACKEND_URL } from 'soapbox/build_config';
|
import { BACKEND_URL } from 'soapbox/build_config';
|
||||||
|
|
||||||
export const displayFqn = state => {
|
export const displayFqn = state => {
|
||||||
|
@ -27,3 +28,15 @@ export const isStandalone = state => {
|
||||||
const instanceFetchFailed = state.getIn(['meta', 'instance_fetch_failed'], false);
|
const instanceFetchFailed = state.getIn(['meta', 'instance_fetch_failed'], false);
|
||||||
return isURL(BACKEND_URL) ? false : (!isPrerendered && instanceFetchFailed);
|
return isURL(BACKEND_URL) ? false : (!isPrerendered && instanceFetchFailed);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the baseURL of the instance.
|
||||||
|
* @param {object} state
|
||||||
|
* @returns {string} url
|
||||||
|
*/
|
||||||
|
export const getBaseURL = state => {
|
||||||
|
const me = state.get('me');
|
||||||
|
const account = state.getIn(['accounts', me]);
|
||||||
|
|
||||||
|
return isURL(BACKEND_URL) ? BACKEND_URL : getAccountBaseURL(account);
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue