Merge branch 'mfa-messaging-fixes' into 'develop'
MFA improvements See merge request soapbox-pub/soapbox-fe!985
This commit is contained in:
commit
7e5fb63deb
|
@ -143,6 +143,7 @@ export function otpVerify(code, mfa_token) {
|
||||||
code: code,
|
code: code,
|
||||||
challenge_type: 'totp',
|
challenge_type: 'totp',
|
||||||
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
|
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
|
||||||
|
scope: getScopes(getState()),
|
||||||
}).then(({ data: token }) => dispatch(authLoggedIn(token)));
|
}).then(({ data: token }) => dispatch(authLoggedIn(token)));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,10 +60,12 @@ export function confirmMfa(method, code, password) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const params = { code, password };
|
const params = { code, password };
|
||||||
dispatch({ type: MFA_CONFIRM_REQUEST, method, code });
|
dispatch({ type: MFA_CONFIRM_REQUEST, method, code });
|
||||||
return api(getState).post(`/api/pleroma/accounts/mfa/confirm/${method}`, params).then(() => {
|
return api(getState).post(`/api/pleroma/accounts/mfa/confirm/${method}`, params).then(({ data }) => {
|
||||||
dispatch({ type: MFA_CONFIRM_SUCCESS, method, code });
|
dispatch({ type: MFA_CONFIRM_SUCCESS, method, code });
|
||||||
|
return data;
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch({ type: MFA_CONFIRM_FAIL, method, code, error });
|
dispatch({ type: MFA_CONFIRM_FAIL, method, code, error, skipAlert: true });
|
||||||
|
throw error;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -71,10 +73,12 @@ export function confirmMfa(method, code, password) {
|
||||||
export function disableMfa(method, password) {
|
export function disableMfa(method, password) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
dispatch({ type: MFA_DISABLE_REQUEST, method });
|
dispatch({ type: MFA_DISABLE_REQUEST, method });
|
||||||
return api(getState).delete(`/api/pleroma/accounts/mfa/${method}`, { data: { password } }).then(response => {
|
return api(getState).delete(`/api/pleroma/accounts/mfa/${method}`, { data: { password } }).then(({ data }) => {
|
||||||
dispatch({ type: MFA_DISABLE_SUCCESS, method });
|
dispatch({ type: MFA_DISABLE_SUCCESS, method });
|
||||||
|
return data;
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch({ type: MFA_DISABLE_FAIL, method });
|
dispatch({ type: MFA_DISABLE_FAIL, method, skipAlert: true });
|
||||||
|
throw error;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import Icon from './icon';
|
||||||
export default class Button extends React.PureComponent {
|
export default class Button extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
type: PropTypes.string,
|
||||||
text: PropTypes.node,
|
text: PropTypes.node,
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
to: PropTypes.string,
|
to: PropTypes.string,
|
||||||
|
@ -54,6 +55,7 @@ export default class Button extends React.PureComponent {
|
||||||
|
|
||||||
const btn = (
|
const btn = (
|
||||||
<button
|
<button
|
||||||
|
type={this.props.type}
|
||||||
className={className}
|
className={className}
|
||||||
disabled={this.props.disabled}
|
disabled={this.props.disabled}
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
|
|
|
@ -45,10 +45,7 @@ class OtpAuthForm extends ImmutablePureComponent {
|
||||||
this.setState({ shouldRedirect: true });
|
this.setState({ shouldRedirect: true });
|
||||||
return dispatch(switchAccount(account.id));
|
return dispatch(switchAccount(account.id));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
this.setState({ isLoading: false });
|
this.setState({ isLoading: false, code_error: true });
|
||||||
if (error.response.data.error === 'Invalid code') {
|
|
||||||
this.setState({ code_error: true });
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
this.setState({ isLoading: true });
|
this.setState({ isLoading: true });
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -82,11 +79,11 @@ class OtpAuthForm extends ImmutablePureComponent {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{ code_error &&
|
{code_error && (
|
||||||
<div className='error-box'>
|
<div className='error-box'>
|
||||||
<FormattedMessage id='login.otp_log_in.fail' defaultMessage='Invalid code, please try again.' />
|
<FormattedMessage id='login.otp_log_in.fail' defaultMessage='Invalid code, please try again.' />
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
<div className='actions'>
|
<div className='actions'>
|
||||||
<button name='button' type='submit' className='btn button button-primary'>
|
<button name='button' type='submit' className='btn button button-primary'>
|
||||||
<FormattedMessage id='login.log_in' defaultMessage='Log in' />
|
<FormattedMessage id='login.log_in' defaultMessage='Log in' />
|
||||||
|
|
|
@ -47,6 +47,8 @@ const messages = defineMessages({
|
||||||
disableFail: { id: 'security.disable.fail', defaultMessage: 'Incorrect password. Try again.' },
|
disableFail: { id: 'security.disable.fail', defaultMessage: 'Incorrect password. Try again.' },
|
||||||
mfaDisableSuccess: { id: 'mfa.disable.success_message', defaultMessage: 'MFA disabled' },
|
mfaDisableSuccess: { id: 'mfa.disable.success_message', defaultMessage: 'MFA disabled' },
|
||||||
mfaConfirmSuccess: { id: 'mfa.confirm.success_message', defaultMessage: 'MFA confirmed' },
|
mfaConfirmSuccess: { id: 'mfa.confirm.success_message', defaultMessage: 'MFA confirmed' },
|
||||||
|
codePlaceholder: { id: 'mfa.mfa_setup.code_placeholder', defaultMessage: 'Code' },
|
||||||
|
passwordPlaceholder: { id: 'mfa.mfa_setup.password_placeholder', defaultMessage: 'Password' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
|
@ -118,44 +120,62 @@ class DisableOtpForm extends ImmutablePureComponent {
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
password: '',
|
password: '',
|
||||||
|
isLoading: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInputChange = e => {
|
handleInputChange = e => {
|
||||||
this.setState({ [e.target.name]: e.target.value });
|
this.setState({ [e.target.name]: e.target.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOtpDisableClick = e => {
|
handleSubmit = e => {
|
||||||
const { password } = this.state;
|
const { password } = this.state;
|
||||||
const { dispatch, intl } = this.props;
|
const { dispatch, intl } = this.props;
|
||||||
|
|
||||||
|
this.setState({ isLoading: true });
|
||||||
|
|
||||||
dispatch(disableMfa('totp', password)).then(() => {
|
dispatch(disableMfa('totp', password)).then(() => {
|
||||||
dispatch(snackbar.success(intl.formatMessage(messages.mfaDisableSuccess)));
|
dispatch(snackbar.success(intl.formatMessage(messages.mfaDisableSuccess)));
|
||||||
|
this.context.router.history.push('../auth/edit');
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(snackbar.error(intl.formatMessage(messages.disableFail)));
|
dispatch(snackbar.error(intl.formatMessage(messages.disableFail)));
|
||||||
|
this.setState({ isLoading: false });
|
||||||
});
|
});
|
||||||
|
|
||||||
this.context.router.history.push('../auth/edit');
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { intl } = this.props;
|
const { intl } = this.props;
|
||||||
|
const { isLoading, password } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SimpleForm>
|
<div className='security-settings-panel'>
|
||||||
<div className='security-settings-panel'>
|
<SimpleForm onSubmit={this.handleSubmit} disabled={isLoading}>
|
||||||
<h1 className='security-settings-panel__setup-otp'>
|
<h1 className='security-settings-panel__setup-otp'>
|
||||||
<FormattedMessage id='mfa.otp_enabled_title' defaultMessage='OTP Enabled' />
|
<FormattedMessage id='mfa.otp_enabled_title' defaultMessage='OTP Enabled' />
|
||||||
</h1>
|
</h1>
|
||||||
<div><FormattedMessage id='mfa.otp_enabled_description' defaultMessage='You have enabled two-factor authentication via OTP.' /></div>
|
<h2 className='security-settings-panel__setup-otp'>
|
||||||
<div><FormattedMessage id='mfa.mfa_disable_enter_password' defaultMessage='Enter your current password to disable two-factor auth:' /></div>
|
<FormattedMessage id='mfa.otp_enabled_description' defaultMessage='You have enabled two-factor authentication via OTP.' />
|
||||||
|
</h2>
|
||||||
<ShowablePassword
|
<ShowablePassword
|
||||||
|
label={intl.formatMessage(messages.passwordPlaceholder)}
|
||||||
|
placeholder={intl.formatMessage(messages.passwordPlaceholder)}
|
||||||
|
hint={<FormattedMessage id='mfa.mfa_disable_enter_password' defaultMessage='Enter your current password to disable two-factor auth.' />}
|
||||||
|
disabled={isLoading}
|
||||||
name='password'
|
name='password'
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
|
value={password}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
<Button className='button button-primary disable' text={intl.formatMessage(messages.mfa_setup_disable_button)} onClick={this.handleOtpDisableClick} />
|
<div className='security-settings-panel__setup-otp__buttons'>
|
||||||
</div>
|
<Button
|
||||||
</SimpleForm>
|
disabled={isLoading}
|
||||||
|
className='button button-primary disable'
|
||||||
|
text={intl.formatMessage(messages.mfa_setup_disable_button)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</SimpleForm>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,6 +211,7 @@ class EnableOtpForm extends ImmutablePureComponent {
|
||||||
|
|
||||||
handleCancelClick = e => {
|
handleCancelClick = e => {
|
||||||
this.context.router.history.push('../auth/edit');
|
this.context.router.history.push('../auth/edit');
|
||||||
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -198,8 +219,8 @@ class EnableOtpForm extends ImmutablePureComponent {
|
||||||
const { backupCodes, displayOtpForm } = this.state;
|
const { backupCodes, displayOtpForm } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SimpleForm>
|
<div className='security-settings-panel'>
|
||||||
<div className='security-settings-panel'>
|
<SimpleForm>
|
||||||
<h1 className='security-settings-panel__setup-otp'>
|
<h1 className='security-settings-panel__setup-otp'>
|
||||||
<FormattedMessage id='mfa.setup_otp_title' defaultMessage='OTP Disabled' />
|
<FormattedMessage id='mfa.setup_otp_title' defaultMessage='OTP Disabled' />
|
||||||
</h1>
|
</h1>
|
||||||
|
@ -233,8 +254,8 @@ class EnableOtpForm extends ImmutablePureComponent {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</SimpleForm>
|
||||||
</SimpleForm>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,7 +276,7 @@ class OtpConfirmForm extends ImmutablePureComponent {
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
password: '',
|
password: '',
|
||||||
done: false,
|
isLoading: false,
|
||||||
code: '',
|
code: '',
|
||||||
qrCodeURI: '',
|
qrCodeURI: '',
|
||||||
confirm_key: '',
|
confirm_key: '',
|
||||||
|
@ -275,66 +296,96 @@ class OtpConfirmForm extends ImmutablePureComponent {
|
||||||
this.setState({ [e.target.name]: e.target.value });
|
this.setState({ [e.target.name]: e.target.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOtpConfirmClick = e => {
|
handleCancelClick = e => {
|
||||||
const { code, password } = this.state;
|
this.context.router.history.push('../auth/edit');
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = e => {
|
||||||
const { dispatch, intl } = this.props;
|
const { dispatch, intl } = this.props;
|
||||||
|
const { code, password } = this.state;
|
||||||
|
|
||||||
|
this.setState({ isLoading: true });
|
||||||
|
|
||||||
dispatch(confirmMfa('totp', code, password)).then(() => {
|
dispatch(confirmMfa('totp', code, password)).then(() => {
|
||||||
dispatch(snackbar.success(intl.formatMessage(messages.mfaConfirmSuccess)));
|
dispatch(snackbar.success(intl.formatMessage(messages.mfaConfirmSuccess)));
|
||||||
|
this.context.router.history.push('../auth/edit');
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(snackbar.error(intl.formatMessage(messages.confirmFail)));
|
dispatch(snackbar.error(intl.formatMessage(messages.confirmFail)));
|
||||||
|
this.setState({ isLoading: false });
|
||||||
});
|
});
|
||||||
|
|
||||||
this.context.router.history.push('../auth/edit');
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { intl } = this.props;
|
const { intl } = this.props;
|
||||||
const { qrCodeURI, confirm_key } = this.state;
|
const { isLoading, qrCodeURI, confirm_key, password, code } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SimpleForm>
|
<div className='security-settings-panel'>
|
||||||
<div className='security-settings-panel'>
|
<SimpleForm onSubmit={this.handleSubmit} disabled={isLoading}>
|
||||||
|
|
||||||
<fieldset disabled={false}>
|
<fieldset disabled={false}>
|
||||||
<FieldsGroup>
|
<FieldsGroup>
|
||||||
<div className='security-settings-panel__section-container'>
|
<div className='security-settings-panel__section-container'>
|
||||||
<h2><FormattedMessage id='mfa.mfa_setup_scan_title' defaultMessage='Scan' /></h2>
|
<h2><FormattedMessage id='mfa.mfa_setup_scan_title' defaultMessage='Scan' /></h2>
|
||||||
|
|
||||||
<div><FormattedMessage id='mfa.mfa_setup_scan_description' defaultMessage='Using your two-factor app, scan this QR code or enter text key:' /></div>
|
<div><FormattedMessage id='mfa.mfa_setup_scan_description' defaultMessage='Using your two-factor app, scan this QR code or enter the text key.' /></div>
|
||||||
|
|
||||||
<span className='security-settings-panel qr-code'>
|
<div className='security-settings-panel__qr-code'>
|
||||||
<QRCode value={qrCodeURI} />
|
<QRCode value={qrCodeURI} />
|
||||||
</span>
|
<div className='security-settings-panel__confirm-key'>
|
||||||
|
{confirm_key}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className='security-settings-panel confirm-key'><FormattedMessage id='mfa.mfa_setup_scan_key' defaultMessage='Key:' /> {confirm_key}</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='security-settings-panel__section-container'>
|
<div className='security-settings-panel__section-container'>
|
||||||
<h2><FormattedMessage id='mfa.mfa_setup_verify_title' defaultMessage='Verify' /></h2>
|
<h2><FormattedMessage id='mfa.mfa_setup_verify_title' defaultMessage='Verify' /></h2>
|
||||||
|
|
||||||
<div><FormattedMessage id='mfa.mfa_setup_verify_description' defaultMessage='To enable two-factor authentication, enter the code from your two-factor app:' /></div>
|
|
||||||
<TextInput
|
<TextInput
|
||||||
name='code'
|
name='code'
|
||||||
|
label={intl.formatMessage(messages.codePlaceholder)}
|
||||||
|
hint={<FormattedMessage id='mfa.mfa_setup.code_hint' defaultMessage='Enter the code from your two-factor app.' />}
|
||||||
|
placeholder={intl.formatMessage(messages.codePlaceholder)}
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
autoComplete='off'
|
autoComplete='off'
|
||||||
|
value={code}
|
||||||
|
disabled={isLoading}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div><FormattedMessage id='mfa.mfa_setup_enter_password' defaultMessage='Enter your current password to confirm your identity:' /></div>
|
|
||||||
<ShowablePassword
|
<ShowablePassword
|
||||||
name='password'
|
name='password'
|
||||||
|
label={intl.formatMessage(messages.passwordPlaceholder)}
|
||||||
|
hint={<FormattedMessage id='mfa.mfa_setup.password_hint' defaultMessage='Enter your current password to confirm your identity.' />}
|
||||||
|
placeholder={intl.formatMessage(messages.passwordPlaceholder)}
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
|
value={password}
|
||||||
|
disabled={isLoading}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</FieldsGroup>
|
</FieldsGroup>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div className='security-settings-panel__setup-otp__buttons'>
|
<div className='security-settings-panel__setup-otp__buttons'>
|
||||||
<Button className='button button-secondary cancel' text={intl.formatMessage(messages.mfa_cancel_button)} onClick={this.handleCancelClick} />
|
<Button
|
||||||
<Button className='button button-primary setup' text={intl.formatMessage(messages.mfa_setup_confirm_button)} onClick={this.handleOtpConfirmClick} />
|
type='button'
|
||||||
|
className='button button-secondary cancel'
|
||||||
|
text={intl.formatMessage(messages.mfa_cancel_button)}
|
||||||
|
onClick={this.handleCancelClick}
|
||||||
|
disabled={isLoading}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type='submit'
|
||||||
|
className='button button-primary setup'
|
||||||
|
text={intl.formatMessage(messages.mfa_setup_confirm_button)}
|
||||||
|
disabled={isLoading}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</SimpleForm>
|
||||||
</SimpleForm>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -580,12 +580,12 @@
|
||||||
"media_gallery.toggle_visible": "Toggle visibility",
|
"media_gallery.toggle_visible": "Toggle visibility",
|
||||||
"media_panel.empty_message": "No media found.",
|
"media_panel.empty_message": "No media found.",
|
||||||
"media_panel.title": "Media",
|
"media_panel.title": "Media",
|
||||||
"mfa.mfa_disable_enter_password": "Enter your current password to disable two-factor auth:",
|
"mfa.mfa_disable_enter_password": "Enter your current password to disable two-factor auth.",
|
||||||
"mfa.mfa_setup_enter_password": "Enter your current password to confirm your identity:",
|
"mfa.mfa_setup_enter_password": "Enter your current password to confirm your identity",
|
||||||
"mfa.mfa_setup_scan_description": "Using your two-factor app, scan this QR code or enter text key:",
|
"mfa.mfa_setup_scan_description": "Using your two-factor app, scan this QR code or enter the text key.",
|
||||||
"mfa.mfa_setup_scan_key": "Key:",
|
"mfa.mfa_setup_scan_key": "Key:",
|
||||||
"mfa.mfa_setup_scan_title": "Scan",
|
"mfa.mfa_setup_scan_title": "Scan",
|
||||||
"mfa.mfa_setup_verify_description": "To enable two-factor authentication, enter the code from your two-factor app:",
|
"mfa.mfa_setup_verify_description": "To enable two-factor authentication, enter the code from your two-factor app",
|
||||||
"mfa.mfa_setup_verify_title": "Verify",
|
"mfa.mfa_setup_verify_title": "Verify",
|
||||||
"mfa.otp_enabled_description": "You have enabled two-factor authentication via OTP.",
|
"mfa.otp_enabled_description": "You have enabled two-factor authentication via OTP.",
|
||||||
"mfa.otp_enabled_title": "OTP Enabled",
|
"mfa.otp_enabled_title": "OTP Enabled",
|
||||||
|
|
|
@ -3,9 +3,7 @@ import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
||||||
import {
|
import {
|
||||||
MFA_FETCH_SUCCESS,
|
MFA_FETCH_SUCCESS,
|
||||||
MFA_CONFIRM_SUCCESS,
|
MFA_CONFIRM_SUCCESS,
|
||||||
MFA_DISABLE_REQUEST,
|
|
||||||
MFA_DISABLE_SUCCESS,
|
MFA_DISABLE_SUCCESS,
|
||||||
MFA_DISABLE_FAIL,
|
|
||||||
} from '../actions/mfa';
|
} from '../actions/mfa';
|
||||||
import {
|
import {
|
||||||
FETCH_TOKENS_SUCCESS,
|
FETCH_TOKENS_SUCCESS,
|
||||||
|
@ -49,11 +47,8 @@ export default function security(state = initialState, action) {
|
||||||
return importMfa(state, fromJS(action.data));
|
return importMfa(state, fromJS(action.data));
|
||||||
case MFA_CONFIRM_SUCCESS:
|
case MFA_CONFIRM_SUCCESS:
|
||||||
return enableMfa(state, action.method);
|
return enableMfa(state, action.method);
|
||||||
case MFA_DISABLE_REQUEST:
|
|
||||||
case MFA_DISABLE_SUCCESS:
|
case MFA_DISABLE_SUCCESS:
|
||||||
return disableMfa(state, action.method);
|
return disableMfa(state, action.method);
|
||||||
case MFA_DISABLE_FAIL:
|
|
||||||
return enableMfa(state, action.method);
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
.security-settings-panel {
|
.security-settings-panel {
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
|
|
||||||
|
.simple_form {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
h1.security-settings-panel__setup-otp {
|
h1.security-settings-panel__setup-otp {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
line-height: 1.25;
|
line-height: 1.25;
|
||||||
|
@ -26,7 +30,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.backup_codes {
|
.backup_codes {
|
||||||
margin: 20px;
|
margin: 10px 0;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
padding: 15px 20px;
|
padding: 15px 20px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
@ -35,6 +39,9 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 125px;
|
min-height: 125px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
.backup_code {
|
.backup_code {
|
||||||
margin: 5px auto;
|
margin: 5px auto;
|
||||||
|
@ -42,22 +49,30 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.security-settings-panel__setup-otp__buttons {
|
.security-settings-panel__setup-otp__buttons {
|
||||||
margin: 20px;
|
margin: 15px 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
min-width: 182px;
|
flex: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div.confirm-key {
|
&__qr-code {
|
||||||
|
margin: 20px 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__confirm-key {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 16px;
|
font-size: 14px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
color: var(--primary-text-color--faint);
|
color: var(--primary-text-color--faint);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
margin: 0 0 20px 20px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -643,20 +643,6 @@ code {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qr-code {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
background: var(--foreground-color);
|
|
||||||
padding: 4px;
|
|
||||||
margin: 0 10px 20px 0;
|
|
||||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
display: block;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.simple_form {
|
.simple_form {
|
||||||
.warning {
|
.warning {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
Loading…
Reference in New Issue