recoveryOptionsModal.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import {Fragment} from 'react';
  2. import {ModalRenderProps} from 'sentry/actionCreators/modal';
  3. import Alert from 'sentry/components/alert';
  4. import AsyncComponent from 'sentry/components/asyncComponent';
  5. import Button from 'sentry/components/button';
  6. import {t} from 'sentry/locale';
  7. import space from 'sentry/styles/space';
  8. import {Authenticator} from 'sentry/types';
  9. import TextBlock from 'sentry/views/settings/components/text/textBlock';
  10. type Props = AsyncComponent['props'] &
  11. ModalRenderProps & {
  12. authenticatorName: string;
  13. };
  14. type State = AsyncComponent['state'] & {
  15. authenticators: Authenticator[] | null;
  16. skipSms: boolean;
  17. };
  18. class RecoveryOptionsModal extends AsyncComponent<Props, State> {
  19. getDefaultState() {
  20. return {...super.getDefaultState(), skipSms: false};
  21. }
  22. getEndpoints(): ReturnType<AsyncComponent['getEndpoints']> {
  23. return [['authenticators', '/users/me/authenticators/']];
  24. }
  25. handleSkipSms = () => {
  26. this.setState({skipSms: true});
  27. };
  28. renderBody() {
  29. const {authenticatorName, closeModal, Body, Header, Footer} = this.props;
  30. const {authenticators, skipSms} = this.state;
  31. const {recovery, sms} = authenticators!.reduce<{[key: string]: Authenticator}>(
  32. (obj, item) => {
  33. obj[item.id] = item;
  34. return obj;
  35. },
  36. {}
  37. );
  38. const recoveryEnrolled = recovery && recovery.isEnrolled;
  39. const displaySmsPrompt = sms && !sms.isEnrolled && !skipSms;
  40. return (
  41. <Fragment>
  42. <Header closeButton>{t('Two-Factor Authentication Enabled')}</Header>
  43. <Body>
  44. <TextBlock>
  45. {t('Two-factor authentication via %s has been enabled.', authenticatorName)}
  46. </TextBlock>
  47. <TextBlock>
  48. {t('You should now set up recovery options to secure your account.')}
  49. </TextBlock>
  50. {displaySmsPrompt ? (
  51. // set up backup phone number
  52. <Alert type="warning">
  53. {t('We recommend adding a phone number as a backup 2FA method.')}
  54. </Alert>
  55. ) : (
  56. // get recovery codes
  57. <Alert type="warning">
  58. {t(
  59. `Recovery codes are the only way to access your account if you lose
  60. your device and cannot receive two-factor authentication codes.`
  61. )}
  62. </Alert>
  63. )}
  64. </Body>
  65. {displaySmsPrompt ? (
  66. // set up backup phone number
  67. <Footer>
  68. <Button onClick={this.handleSkipSms} name="skipStep" autoFocus>
  69. {t('Skip this step')}
  70. </Button>
  71. <Button
  72. priority="primary"
  73. onClick={closeModal}
  74. to={`/settings/account/security/mfa/${sms.id}/enroll/`}
  75. name="addPhone"
  76. css={{marginLeft: space(1)}}
  77. autoFocus
  78. >
  79. {t('Add a Phone Number')}
  80. </Button>
  81. </Footer>
  82. ) : (
  83. // get recovery codes
  84. <Footer>
  85. <Button
  86. priority="primary"
  87. onClick={closeModal}
  88. to={
  89. recoveryEnrolled
  90. ? `/settings/account/security/mfa/${recovery.authId}/`
  91. : '/settings/account/security/'
  92. }
  93. name="getCodes"
  94. autoFocus
  95. >
  96. {t('Get Recovery Codes')}
  97. </Button>
  98. </Footer>
  99. )}
  100. </Fragment>
  101. );
  102. }
  103. }
  104. export default RecoveryOptionsModal;