recoveryOptionsModal.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  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 =
  40. sms && !sms.isEnrolled && !skipSms && !sms.disallowNewEnrollment;
  41. return (
  42. <Fragment>
  43. <Header closeButton>{t('Two-Factor Authentication Enabled')}</Header>
  44. <Body>
  45. <TextBlock>
  46. {t('Two-factor authentication via %s has been enabled.', authenticatorName)}
  47. </TextBlock>
  48. <TextBlock>
  49. {t('You should now set up recovery options to secure your account.')}
  50. </TextBlock>
  51. {displaySmsPrompt ? (
  52. // set up backup phone number
  53. <Alert type="warning">
  54. {t('We recommend adding a phone number as a backup 2FA method.')}
  55. </Alert>
  56. ) : (
  57. // get recovery codes
  58. <Alert type="warning">
  59. {t(
  60. `Recovery codes are the only way to access your account if you lose
  61. your device and cannot receive two-factor authentication codes.`
  62. )}
  63. </Alert>
  64. )}
  65. </Body>
  66. {displaySmsPrompt ? (
  67. // set up backup phone number
  68. <Footer>
  69. <Button onClick={this.handleSkipSms} name="skipStep" autoFocus>
  70. {t('Skip this step')}
  71. </Button>
  72. <Button
  73. priority="primary"
  74. onClick={closeModal}
  75. to={`/settings/account/security/mfa/${sms.id}/enroll/`}
  76. name="addPhone"
  77. css={{marginLeft: space(1)}}
  78. autoFocus
  79. >
  80. {t('Add a Phone Number')}
  81. </Button>
  82. </Footer>
  83. ) : (
  84. // get recovery codes
  85. <Footer>
  86. <Button
  87. priority="primary"
  88. onClick={closeModal}
  89. to={
  90. recoveryEnrolled
  91. ? `/settings/account/security/mfa/${recovery.authId}/`
  92. : '/settings/account/security/'
  93. }
  94. name="getCodes"
  95. autoFocus
  96. >
  97. {t('Get Recovery Codes')}
  98. </Button>
  99. </Footer>
  100. )}
  101. </Fragment>
  102. );
  103. }
  104. }
  105. export default RecoveryOptionsModal;