@authing/react-ui-components 3.0.2-beta.9 → 3.1.1-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/.eslintignore +6 -0
  2. package/.eslintrc.js +7 -0
  3. package/.prettierrc +5 -0
  4. package/.vscode/settings.json +31 -0
  5. package/LICENSE +21 -0
  6. package/config/antdReplacer.js +51 -0
  7. package/config/env.js +106 -0
  8. package/config/getHttpsConfig.js +66 -0
  9. package/config/jest/cssTransform.js +14 -0
  10. package/config/jest/fileTransform.js +40 -0
  11. package/config/modules.js +134 -0
  12. package/config/paths.js +71 -0
  13. package/config/pnpTs.js +35 -0
  14. package/config/webpack.config.js +857 -0
  15. package/config/webpackDevServer.config.js +130 -0
  16. package/lib/index.d.ts +211 -1669
  17. package/lib/index.min.css +1 -2
  18. package/lib/index.min.js +1 -1
  19. package/lib/index.min.js.LICENSE.txt +0 -32
  20. package/package.json +18 -42
  21. package/public/favicon.ico +0 -0
  22. package/public/index.html +43 -0
  23. package/public/logo192.png +0 -0
  24. package/public/logo512.png +0 -0
  25. package/public/manifest.json +25 -0
  26. package/public/robots.txt +3 -0
  27. package/scripts/build.js +212 -0
  28. package/scripts/lib.js +200 -0
  29. package/scripts/start.js +166 -0
  30. package/scripts/test.js +53 -0
  31. package/src/common/AuthingDropdown/index.tsx +52 -0
  32. package/src/common/AuthingDropdown/style.less +43 -0
  33. package/src/common/AuthingTabs/index.tsx +98 -0
  34. package/src/common/AuthingTabs/style.less +135 -0
  35. package/src/common/CopyAbleText/index.tsx +54 -0
  36. package/src/common/CopyAbleText/style.less +13 -0
  37. package/src/common/VerifyCodeInput/index.tsx +76 -0
  38. package/src/common/VerifyCodeInput/style.less +24 -0
  39. package/src/components/AuthingGuard/AppMFALayout/index.tsx +74 -0
  40. package/src/components/AuthingGuard/AppMFALayout/style.less +12 -0
  41. package/src/components/AuthingGuard/CompleteUserInfoLayout/index.tsx +29 -0
  42. package/src/components/AuthingGuard/CompleteUserInfoLayout/style.less +8 -0
  43. package/src/components/AuthingGuard/Forms/ADLoginForm/index.tsx +117 -0
  44. package/src/components/AuthingGuard/Forms/Agreements/index.tsx +81 -0
  45. package/src/components/AuthingGuard/Forms/Agreements/style.less +44 -0
  46. package/src/components/AuthingGuard/Forms/CompleteUserInfoForm/index.tsx +139 -0
  47. package/src/components/AuthingGuard/Forms/EmailMfaVerifyForm/CheckEmailForm.tsx +86 -0
  48. package/src/components/AuthingGuard/Forms/EmailMfaVerifyForm/VerifyCodeForm.tsx +116 -0
  49. package/src/components/AuthingGuard/Forms/EmailMfaVerifyForm/index.tsx +40 -0
  50. package/src/components/AuthingGuard/Forms/EmailMfaVerifyForm/style.less +0 -0
  51. package/src/components/AuthingGuard/Forms/EmailRegisterForm/index.tsx +181 -0
  52. package/src/components/AuthingGuard/Forms/EmailRegisterForm/style.less +0 -0
  53. package/src/components/AuthingGuard/Forms/LdapLoginForm/index.tsx +161 -0
  54. package/src/components/AuthingGuard/Forms/LdapLoginForm/style.less +0 -0
  55. package/src/components/AuthingGuard/Forms/LoginFormFooter/index.tsx +76 -0
  56. package/src/components/AuthingGuard/Forms/LoginFormFooter/style.less +6 -0
  57. package/src/components/AuthingGuard/Forms/MfaResetCodeForm/Step1.tsx +86 -0
  58. package/src/components/AuthingGuard/Forms/MfaResetCodeForm/Step2.tsx +53 -0
  59. package/src/components/AuthingGuard/Forms/MfaResetCodeForm/index.tsx +65 -0
  60. package/src/components/AuthingGuard/Forms/MfaResetCodeForm/style.less +20 -0
  61. package/src/components/AuthingGuard/Forms/MfaVerifyForm/index.tsx +105 -0
  62. package/src/components/AuthingGuard/Forms/MfaVerifyForm/style.less +12 -0
  63. package/src/components/AuthingGuard/Forms/PasswordLoginForm/index.tsx +261 -0
  64. package/src/components/AuthingGuard/Forms/PhoneCodeLoginForm/index.tsx +133 -0
  65. package/src/components/AuthingGuard/Forms/PhoneRegisterForm/index.tsx +202 -0
  66. package/src/components/AuthingGuard/Forms/QrCodeLoginForm/index.tsx +61 -0
  67. package/src/components/AuthingGuard/Forms/QrCodeLoginForm/style.less +14 -0
  68. package/src/components/AuthingGuard/Forms/RegisterFormFooter/index.tsx +56 -0
  69. package/src/components/AuthingGuard/Forms/RegisterFormFooter/style.less +0 -0
  70. package/src/components/AuthingGuard/Forms/ResetPwdForm/Footer.tsx +21 -0
  71. package/src/components/AuthingGuard/Forms/ResetPwdForm/Step1.tsx +86 -0
  72. package/src/components/AuthingGuard/Forms/ResetPwdForm/Step2.tsx +127 -0
  73. package/src/components/AuthingGuard/Forms/ResetPwdForm/Step3.tsx +141 -0
  74. package/src/components/AuthingGuard/Forms/ResetPwdForm/Step4.tsx +51 -0
  75. package/src/components/AuthingGuard/Forms/ResetPwdForm/index.tsx +96 -0
  76. package/src/components/AuthingGuard/Forms/ResetPwdForm/style.less +3 -0
  77. package/src/components/AuthingGuard/Forms/SendPhoneCode/SendCodeBtn.tsx +88 -0
  78. package/src/components/AuthingGuard/Forms/SendPhoneCode/index.tsx +50 -0
  79. package/src/components/AuthingGuard/Forms/SendPhoneCode/style.less +26 -0
  80. package/src/components/AuthingGuard/Forms/SmsMfaVerifyForm/CheckPhoneForm.tsx +86 -0
  81. package/src/components/AuthingGuard/Forms/SmsMfaVerifyForm/VerifyCodeForm.tsx +113 -0
  82. package/src/components/AuthingGuard/Forms/SmsMfaVerifyForm/index.tsx +40 -0
  83. package/src/components/AuthingGuard/Forms/SmsMfaVerifyForm/style.less +3 -0
  84. package/src/components/AuthingGuard/Forms/SocialAndIdpLogin/index.tsx +325 -0
  85. package/src/components/AuthingGuard/Forms/SocialAndIdpLogin/style.less +75 -0
  86. package/src/components/AuthingGuard/Forms/UploadImage/index.tsx +70 -0
  87. package/src/components/AuthingGuard/Forms/index.ts +13 -0
  88. package/src/components/AuthingGuard/GuardLayout/index.tsx +488 -0
  89. package/src/components/AuthingGuard/GuardLayout/style.less +111 -0
  90. package/src/components/AuthingGuard/Header/index.tsx +28 -0
  91. package/src/components/AuthingGuard/Header/style.less +64 -0
  92. package/src/components/AuthingGuard/IconFont/iconfont.js +74 -0
  93. package/src/components/AuthingGuard/IconFont/index.tsx +19 -0
  94. package/src/components/AuthingGuard/IconFont/style.less +6 -0
  95. package/src/components/AuthingGuard/IconFont/svg.js +2 -0
  96. package/src/components/AuthingGuard/LoginLayout/index.tsx +205 -0
  97. package/src/components/AuthingGuard/LoginLayout/style.less +0 -0
  98. package/src/components/AuthingGuard/MfaLayout/Steps.ts +4 -0
  99. package/src/components/AuthingGuard/MfaLayout/index.tsx +49 -0
  100. package/src/components/AuthingGuard/MfaLayout/style.less +3 -0
  101. package/src/components/AuthingGuard/RegisterLayout/index.tsx +89 -0
  102. package/src/components/AuthingGuard/RegisterLayout/style.less +0 -0
  103. package/src/components/AuthingGuard/ResetPwdLayout/index.tsx +20 -0
  104. package/src/components/AuthingGuard/ToggleLang/index.tsx +51 -0
  105. package/src/components/AuthingGuard/api/appConfig.ts +154 -0
  106. package/src/components/AuthingGuard/api/http.ts +88 -0
  107. package/src/components/AuthingGuard/api/index.ts +3 -0
  108. package/src/components/AuthingGuard/api/sso.ts +29 -0
  109. package/src/components/AuthingGuard/api/userPoolConfig.ts +112 -0
  110. package/src/components/AuthingGuard/constants.ts +107 -0
  111. package/src/components/AuthingGuard/hooks/index.tsx +70 -0
  112. package/src/components/AuthingGuard/hooks/useScreenSize.tsx +68 -0
  113. package/src/components/AuthingGuard/index.tsx +134 -0
  114. package/src/components/AuthingGuard/locales/en/common.json +185 -0
  115. package/src/components/AuthingGuard/locales/en/index.ts +6 -0
  116. package/src/components/AuthingGuard/locales/en/login.json +94 -0
  117. package/src/components/AuthingGuard/locales/en/map.json +4 -0
  118. package/src/components/AuthingGuard/locales/en/user.json +81 -0
  119. package/src/components/AuthingGuard/locales/index.ts +45 -0
  120. package/src/components/AuthingGuard/locales/zh/common.json +185 -0
  121. package/src/components/AuthingGuard/locales/zh/index.ts +6 -0
  122. package/src/components/AuthingGuard/locales/zh/login.json +94 -0
  123. package/src/components/AuthingGuard/locales/zh/map.json +4 -0
  124. package/src/components/AuthingGuard/locales/zh/user.json +81 -0
  125. package/src/components/AuthingGuard/style.less +108 -0
  126. package/src/components/AuthingGuard/types/Forms.ts +95 -0
  127. package/src/components/AuthingGuard/types/GuardConfig.ts +424 -0
  128. package/src/components/AuthingGuard/types/GuardState.ts +7 -0
  129. package/src/components/AuthingGuard/types/Locales.ts +12 -0
  130. package/src/components/AuthingGuard/types/index.ts +4 -0
  131. package/src/components/index.ts +7 -0
  132. package/src/context/base.tsx +28 -0
  133. package/src/context/global/context.tsx +39 -0
  134. package/src/context/global/reducer.tsx +56 -0
  135. package/src/index.tsx +142 -0
  136. package/src/logo.svg +7 -0
  137. package/src/react-app-env.d.ts +71 -0
  138. package/src/reportWebVitals.ts +15 -0
  139. package/src/setupTests.ts +5 -0
  140. package/src/utils/clipboard.ts +27 -0
  141. package/src/utils/index.ts +180 -0
  142. package/src/utils/popupCenter.ts +48 -0
  143. package/tsconfig.json +24 -0
  144. package/lib/static/media/loading.4a67a5f3.svg +0 -29
@@ -0,0 +1,141 @@
1
+ import React, { FC, useState } from 'react'
2
+ import { Form, Button, Input, message } from 'antd'
3
+ import { SafetyOutlined, LockOutlined } from '@ant-design/icons'
4
+ import { EmailScene } from 'authing-js-sdk'
5
+
6
+ import { getRequiredRules } from '../../../../utils'
7
+ import { useGuardContext } from '../../../../context/global/context'
8
+ import { ResetPasswordStep3Props } from '../../../../components/AuthingGuard/types'
9
+ import { useTranslation } from 'react-i18next'
10
+
11
+ export const ResetPasswordStep3: FC<ResetPasswordStep3Props> = ({
12
+ email,
13
+ onSuccess = () => {},
14
+ onFail,
15
+ }) => {
16
+ const {
17
+ state: { authClient, guardEvents },
18
+ } = useGuardContext()
19
+ const [rawForm] = Form.useForm()
20
+ const { t } = useTranslation()
21
+
22
+ const [reseting, setReseting] = useState(false)
23
+ const [sending, setSending] = useState(false)
24
+
25
+ const onSendResetMail = async () => {
26
+ setSending(true)
27
+ try {
28
+ await authClient.sendEmail(email, EmailScene.ResetPassword)
29
+ message.success(t('login.emailSent'))
30
+ guardEvents.onPwdEmailSend?.(authClient)
31
+ } catch (e) {
32
+ guardEvents.onPwdEmailSendError?.(e, authClient)
33
+ } finally {
34
+ setSending(false)
35
+ }
36
+ }
37
+
38
+ const onStep3Finish = async (values: any) => {
39
+ const code = values.code
40
+ const password = values.password
41
+ try {
42
+ await authClient.resetPasswordByEmailCode(email, code, password)
43
+ onSuccess()
44
+ } catch (error) {
45
+ onFail?.(error)
46
+ } finally {
47
+ setReseting(false)
48
+ }
49
+ }
50
+
51
+ return (
52
+ <>
53
+ <p
54
+ style={{
55
+ marginBottom: 24,
56
+ padding: '0 12px',
57
+ }}
58
+ >
59
+ {t('login.resetEmailSent', {
60
+ email: email,
61
+ })}
62
+ </p>
63
+
64
+ <Form
65
+ form={rawForm}
66
+ onFinishFailed={() => setReseting(false)}
67
+ onSubmitCapture={() => setReseting(true)}
68
+ onFinish={onStep3Finish}
69
+ >
70
+ <Form.Item
71
+ name="code"
72
+ rules={getRequiredRules(t('common.repeatPassword')).concat({
73
+ len: 4,
74
+ message: t('common.inputFourVerifyCode', {
75
+ length: 4,
76
+ }),
77
+ })}
78
+ >
79
+ <Input
80
+ name="code"
81
+ size="large"
82
+ placeholder={t('login.fourVerifyCode', {
83
+ length: 4,
84
+ })}
85
+ prefix={<SafetyOutlined style={{ color: '#ddd' }} />}
86
+ />
87
+ </Form.Item>
88
+ <Form.Item
89
+ name="password"
90
+ rules={getRequiredRules(t('common.passwordNotNull'))}
91
+ >
92
+ <Input.Password
93
+ name="password"
94
+ size="large"
95
+ placeholder={t('user.newPwd')}
96
+ prefix={<LockOutlined style={{ color: '#ddd' }} />}
97
+ />
98
+ </Form.Item>
99
+ <Form.Item
100
+ name="repeat-password"
101
+ rules={getRequiredRules(t('common.repeatPassword')).concat({
102
+ validator: async (rule, value) => {
103
+ if (rawForm.getFieldValue('password') !== value) {
104
+ throw new Error(t('login.twoPwdNeedSame'))
105
+ }
106
+ },
107
+ })}
108
+ >
109
+ <Input.Password
110
+ name="repeat-password"
111
+ size="large"
112
+ placeholder={t('login.inputPwdAgain')}
113
+ prefix={<LockOutlined style={{ color: '#ddd' }} />}
114
+ />
115
+ </Form.Item>
116
+ <Form.Item>
117
+ <Button
118
+ className="authing-reset-pwd-btn"
119
+ block
120
+ loading={reseting}
121
+ type="primary"
122
+ size="large"
123
+ htmlType="submit"
124
+ >
125
+ {t('login.resetPwd')}
126
+ </Button>
127
+ </Form.Item>
128
+ <Button
129
+ block
130
+ type="primary"
131
+ ghost
132
+ loading={sending}
133
+ size="large"
134
+ onClick={onSendResetMail}
135
+ >
136
+ {t('login.resetPwdEmail')}
137
+ </Button>
138
+ </Form>
139
+ </>
140
+ )
141
+ }
@@ -0,0 +1,51 @@
1
+ import { useGuardContext } from '../../../../context/global/context'
2
+ import React, { FC, useEffect, useRef, useState } from 'react'
3
+
4
+ import { GuardScenes } from '../../../../components/AuthingGuard/types'
5
+ import { useTranslation } from 'react-i18next'
6
+
7
+ const TIME = 3
8
+ export const ResetPasswordStep4: FC = () => {
9
+ const [countDown, setCountDown] = useState(TIME)
10
+ const timerRef = useRef<any>(0)
11
+ const { t } = useTranslation()
12
+ const { setValue } = useGuardContext()
13
+
14
+ useEffect(() => {
15
+ timerRef.current = setInterval(() => {
16
+ setCountDown((prev) => {
17
+ return prev - 1
18
+ })
19
+ }, 1000)
20
+
21
+ return () => clearInterval(timerRef.current)
22
+ }, [])
23
+
24
+ useEffect(() => {
25
+ if (countDown <= 0) {
26
+ clearInterval(timerRef.current)
27
+
28
+ setValue('guardScenes', GuardScenes.Login)
29
+ }
30
+ // eslint-disable-next-line react-hooks/exhaustive-deps
31
+ }, [countDown])
32
+
33
+ return (
34
+ <div style={{ textAlign: 'center' }}>
35
+ <h3
36
+ style={{
37
+ fontSize: 20,
38
+ fontFamily: 'PingFangSC-Medium',
39
+ marginBottom: 0,
40
+ }}
41
+ >
42
+ {t('common.pwdModifySuccess')}
43
+ </h3>
44
+ <p style={{ fontSize: 12 }}>
45
+ {t('common.jumpAfterCount', {
46
+ number: countDown,
47
+ })}
48
+ </p>
49
+ </div>
50
+ )
51
+ }
@@ -0,0 +1,96 @@
1
+ import React, { FC, useEffect, useState } from 'react'
2
+
3
+ import { ResetPasswordStep1 } from './Step1'
4
+ import { ResetPasswordStep2 } from './Step2'
5
+ import { ResetPasswordStep3 } from './Step3'
6
+ import { ResetPasswordStep4 } from './Step4'
7
+ import { ResetPwdFormFooter } from './Footer'
8
+ import { useGuardContext } from '../../../../context/global/context'
9
+ import { ResetPasswordFormProps } from '../../../../components/AuthingGuard/types'
10
+
11
+ import './style.less'
12
+ import { useTranslation } from 'react-i18next'
13
+
14
+ export const ResetPasswordForm: FC<ResetPasswordFormProps> = ({
15
+ onSuccess,
16
+ onFail,
17
+ }) => {
18
+ const { setValue } = useGuardContext()
19
+ const { t } = useTranslation()
20
+
21
+ const [step, setStep] = useState(1)
22
+ const [phone, setPhone] = useState('')
23
+ const [email, setEmail] = useState('')
24
+
25
+ useEffect(() => {
26
+ switch (step) {
27
+ case 1:
28
+ setValue('guardTitle', t('common.retrievePassword'))
29
+ break
30
+ case 2:
31
+ case 3:
32
+ case 4:
33
+ setValue('guardTitle', t('login.resetPwd'))
34
+ break
35
+ default:
36
+ setValue('guardTitle', t('common.retrievePassword'))
37
+ break
38
+ }
39
+ // eslint-disable-next-line react-hooks/exhaustive-deps
40
+ }, [step])
41
+
42
+ const onStep1Finish = async (type: string, value: string) => {
43
+ if (type === 'phone') {
44
+ setPhone(value)
45
+ setStep(2)
46
+ } else if (type === 'email') {
47
+ setEmail(value)
48
+ setStep(3)
49
+ }
50
+ }
51
+
52
+ const onStep2OrStep3Finish = () => {
53
+ setStep(4)
54
+ onSuccess?.()
55
+ }
56
+
57
+ const onStep2OrStep3Fail = (error: any) => {
58
+ onFail?.(error)
59
+ }
60
+
61
+ const getForm = () => {
62
+ switch (step) {
63
+ case 1:
64
+ return (
65
+ <ResetPasswordStep1 onSuccess={onStep1Finish}></ResetPasswordStep1>
66
+ )
67
+ case 2:
68
+ return (
69
+ <ResetPasswordStep2
70
+ phone={phone}
71
+ onFail={onStep2OrStep3Fail}
72
+ onSuccess={onStep2OrStep3Finish}
73
+ ></ResetPasswordStep2>
74
+ )
75
+ case 3:
76
+ return (
77
+ <ResetPasswordStep3
78
+ email={email}
79
+ onFail={onStep2OrStep3Fail}
80
+ onSuccess={onStep2OrStep3Finish}
81
+ ></ResetPasswordStep3>
82
+ )
83
+ case 4:
84
+ return <ResetPasswordStep4></ResetPasswordStep4>
85
+ default:
86
+ return null
87
+ }
88
+ }
89
+
90
+ return (
91
+ <div>
92
+ {getForm()}
93
+ {step !== 4 && <ResetPwdFormFooter />}
94
+ </div>
95
+ )
96
+ }
@@ -0,0 +1,3 @@
1
+ .authing-reset-pwd-btn {
2
+ margin-top: 24px;
3
+ }
@@ -0,0 +1,88 @@
1
+ import { Spin } from 'antd'
2
+ import React, { FC, useState, useRef, useEffect, useMemo } from 'react'
3
+ import { useTranslation } from 'react-i18next'
4
+ import { LoadingOutlined } from '@ant-design/icons'
5
+
6
+ const TIME = 60
7
+ export interface SendCodeProps {
8
+ beforeSend: () => Promise<boolean>
9
+ btnRef?: React.RefObject<HTMLButtonElement>
10
+ }
11
+
12
+ const useSentCounter = () => {
13
+ const [countDown, setCountDown] = useState(0)
14
+ const timerRef = useRef<any>(0)
15
+
16
+ useEffect(() => {
17
+ return () => clearInterval(timerRef.current)
18
+ }, [])
19
+
20
+ useEffect(() => {
21
+ if (countDown <= 0) {
22
+ clearInterval(timerRef.current)
23
+ }
24
+ }, [countDown])
25
+
26
+ const enabled = useMemo(() => countDown <= 0, [countDown])
27
+
28
+ const send = () => {
29
+ setCountDown(TIME)
30
+
31
+ timerRef.current = setInterval(() => {
32
+ setCountDown((prev) => {
33
+ return prev - 1
34
+ })
35
+ }, 1000)
36
+ }
37
+
38
+ return {
39
+ enabled,
40
+ send,
41
+ countDown,
42
+ }
43
+ }
44
+
45
+ export const SendCodeBtn: FC<SendCodeProps> = ({ beforeSend, btnRef }) => {
46
+ const { enabled, send, countDown } = useSentCounter()
47
+ const [loading, setLoading] = useState(false)
48
+ const { t } = useTranslation()
49
+ const disabled = useMemo(() => {
50
+ return !enabled || loading
51
+ }, [enabled, loading])
52
+
53
+ const onClick = async (e: React.MouseEvent<HTMLButtonElement>) => {
54
+ setLoading(true)
55
+ if (disabled) {
56
+ return
57
+ }
58
+
59
+ if (!(await beforeSend())) {
60
+ setLoading(false)
61
+ return
62
+ }
63
+ setLoading(false)
64
+ send()
65
+ }
66
+
67
+ return (
68
+ <button
69
+ type="button"
70
+ className="authing-send-code-btn"
71
+ disabled={disabled}
72
+ onClick={onClick}
73
+ ref={btnRef}
74
+ >
75
+ <Spin
76
+ spinning={loading}
77
+ indicator={<LoadingOutlined spin />}
78
+ size="small"
79
+ >
80
+ {enabled
81
+ ? t('common.sendVerifyCode')
82
+ : t('common.retryAfterTime', {
83
+ time: countDown,
84
+ })}
85
+ </Spin>
86
+ </button>
87
+ )
88
+ }
@@ -0,0 +1,50 @@
1
+ import { message } from 'antd'
2
+ import React, { FC } from 'react'
3
+ import { CommonMessage } from 'authing-js-sdk'
4
+
5
+ import { SendCodeBtn } from './SendCodeBtn'
6
+ import { validate } from '../../../../utils'
7
+ import { useGuardContext } from '../../../../context/global/context'
8
+
9
+ import './style.less'
10
+ import { useTranslation } from 'react-i18next'
11
+
12
+ export interface SendPhoneCodeProps {
13
+ phone: string
14
+ onSend?: () => void
15
+ onError?: (error: CommonMessage) => void
16
+ }
17
+
18
+ export const SendPhoneCode: FC<SendPhoneCodeProps> = ({
19
+ phone,
20
+ onSend,
21
+ onError,
22
+ }) => {
23
+ const { t } = useTranslation()
24
+ const {
25
+ state: { authClient },
26
+ } = useGuardContext()
27
+
28
+ return (
29
+ <SendCodeBtn
30
+ beforeSend={async () => {
31
+ if (!phone) {
32
+ message.error(t('login.inputPhone'))
33
+ return false
34
+ }
35
+ if (!validate('phone', phone)) {
36
+ message.error(t('common.phoneFormateError'))
37
+ return false
38
+ }
39
+ try {
40
+ await authClient.sendSmsCode(phone)
41
+ onSend?.()
42
+ return true
43
+ } catch (error) {
44
+ onError?.(error)
45
+ return false
46
+ }
47
+ }}
48
+ />
49
+ )
50
+ }
@@ -0,0 +1,26 @@
1
+ .authing-send-code-btn {
2
+ display: inline-block;
3
+ border: none;
4
+ background-color: transparent;
5
+ font-size: inherit;
6
+ color: #396aff;
7
+ cursor: pointer;
8
+ padding: 0;
9
+ user-select: none;
10
+ outline: none;
11
+ }
12
+
13
+ .authing-send-code-btn:focus {
14
+ outline: none;
15
+ }
16
+
17
+ .authing-send-code-btn[disabled] {
18
+ cursor: not-allowed;
19
+ filter: opacity(0.5);
20
+ }
21
+
22
+ @media only screen and (max-width: 719px) {
23
+ .authing-send-code-btn {
24
+ font-size: 14px;
25
+ }
26
+ }
@@ -0,0 +1,86 @@
1
+ import React, { FC, useState } from 'react'
2
+ import { Button, Form, Input, message } from 'antd'
3
+
4
+ import { useGuardContext } from '../../../../context/global/context'
5
+ import { MFACheckPhoneFormProps } from '../../types'
6
+
7
+ import './style.less'
8
+ import { VALIDATE_PATTERN } from '../../../../utils'
9
+ import { useTranslation } from 'react-i18next'
10
+
11
+ export const CheckPhoneForm: FC<MFACheckPhoneFormProps> = ({
12
+ onSuccess,
13
+ mfaToken,
14
+ }) => {
15
+ const {
16
+ state: { authClient },
17
+ } = useGuardContext()
18
+ const { t } = useTranslation()
19
+
20
+ const [rawForm] = Form.useForm()
21
+
22
+ const [loading, setLoading] = useState(false)
23
+
24
+ const onFinish = async ({ phone }: any) => {
25
+ try {
26
+ const bindable = await authClient.mfa.phoneOrEmailBindable({
27
+ mfaToken,
28
+ phone,
29
+ })
30
+ if (!bindable) {
31
+ message.error(
32
+ t('common.unBindEmaileDoc', {
33
+ email: phone,
34
+ })
35
+ )
36
+ return
37
+ }
38
+ onSuccess(phone!)
39
+ } catch (e) {
40
+ // do nothing
41
+ } finally {
42
+ setLoading(false)
43
+ }
44
+ }
45
+
46
+ return (
47
+ <>
48
+ <h3 className="authing-guard-mfa-title">{t('common.bindPhone')}</h3>
49
+ <p className="authing-guard-mfa-tips">{t('login.bindPhoneInfo')}</p>
50
+ <Form
51
+ className="authing-mfa-check-phone-from"
52
+ form={rawForm}
53
+ onSubmitCapture={() => setLoading(true)}
54
+ onFinish={onFinish}
55
+ onFinishFailed={() => setLoading(false)}
56
+ >
57
+ <Form.Item
58
+ name="phone"
59
+ rules={[
60
+ {
61
+ required: true,
62
+ message: t('login.inputPhone'),
63
+ },
64
+ {
65
+ pattern: VALIDATE_PATTERN.phone,
66
+ message: t('login.phoneError'),
67
+ },
68
+ ]}
69
+ >
70
+ <Input size="large" placeholder={t('login.inputPhone')} />
71
+ </Form.Item>
72
+
73
+ <Button
74
+ className="authing-guard-mfa-confirm-btn"
75
+ loading={loading}
76
+ block
77
+ htmlType="submit"
78
+ type="primary"
79
+ size="large"
80
+ >
81
+ {t('common.sure')}
82
+ </Button>
83
+ </Form>
84
+ </>
85
+ )
86
+ }
@@ -0,0 +1,113 @@
1
+ import { User } from 'authing-js-sdk'
2
+ import React, { FC, useState } from 'react'
3
+ import { Button, Form } from 'antd'
4
+
5
+ import { VerifyCodeInput } from '../../../../common/VerifyCodeInput'
6
+ import { useGuardContext } from '../../../../context/global/context'
7
+ import { SmsMFAVerifyFormProps } from '../../../../components/AuthingGuard/types'
8
+
9
+ import './style.less'
10
+ import { SendCodeBtn } from '../SendPhoneCode/SendCodeBtn'
11
+ import { useTranslation } from 'react-i18next'
12
+
13
+ const CODE_LEN = 4
14
+
15
+ export const VerifyCodeForm: FC<SmsMFAVerifyFormProps> = ({
16
+ onSuccess,
17
+ onFail,
18
+ phone,
19
+ mfaToken,
20
+ sendCodeRef,
21
+ }) => {
22
+ const {
23
+ state: { authClient },
24
+ } = useGuardContext()
25
+ const { t } = useTranslation()
26
+
27
+ const [rawForm] = Form.useForm()
28
+
29
+ const [MfaCode, setMFACode] = useState(new Array(CODE_LEN).fill(''))
30
+ const [loading, setLoading] = useState(false)
31
+ const [sending, setSending] = useState(false)
32
+ const [sent, setSent] = useState(false)
33
+
34
+ const onFinish = async (values: any) => {
35
+ try {
36
+ const user: User = await authClient.mfa.verifyAppSmsMfa({
37
+ mfaToken,
38
+ phone: phone!,
39
+ code: MfaCode.join(''),
40
+ })
41
+ onSuccess && onSuccess(user)
42
+ } catch (e) {
43
+ onFail?.(e)
44
+ } finally {
45
+ setLoading(false)
46
+ }
47
+ }
48
+
49
+ const sendVerifyCode = async () => {
50
+ setSending(true)
51
+ try {
52
+ await authClient.sendSmsCode(phone!)
53
+ setSent(true)
54
+ return true
55
+ } catch (e) {
56
+ return false
57
+ } finally {
58
+ setSending(false)
59
+ }
60
+ }
61
+
62
+ return (
63
+ <>
64
+ <h3 className="authing-guard-mfa-title">{t('login.inputPhoneCode')}</h3>
65
+ <p className="authing-guard-mfa-tips">
66
+ {sending
67
+ ? t('login.sendingVerifyCode')
68
+ : sent
69
+ ? `${t('login.verifyCodeSended')} ${phone}`
70
+ : t('login.clickSent')}
71
+ </p>
72
+ <Form
73
+ form={rawForm}
74
+ onSubmitCapture={() => setLoading(true)}
75
+ onFinish={onFinish}
76
+ onFinishFailed={() => setLoading(false)}
77
+ >
78
+ <Form.Item
79
+ name="mfaCode"
80
+ rules={[
81
+ {
82
+ validateTrigger: [],
83
+ validator() {
84
+ if (MfaCode.some((item) => !item)) {
85
+ return Promise.reject(t('login.inputFullMfaCode'))
86
+ }
87
+ return Promise.resolve()
88
+ },
89
+ },
90
+ ]}
91
+ >
92
+ <VerifyCodeInput
93
+ length={CODE_LEN}
94
+ verifyCode={MfaCode}
95
+ setVerifyCode={setMFACode}
96
+ />
97
+ </Form.Item>
98
+ <SendCodeBtn btnRef={sendCodeRef} beforeSend={() => sendVerifyCode()} />
99
+
100
+ <Button
101
+ className="authing-guard-mfa-confirm-btn"
102
+ loading={loading}
103
+ block
104
+ htmlType="submit"
105
+ type="primary"
106
+ size="large"
107
+ >
108
+ {t('common.sure')}
109
+ </Button>
110
+ </Form>
111
+ </>
112
+ )
113
+ }
@@ -0,0 +1,40 @@
1
+ import React, { FC, useRef, useState } from 'react'
2
+ import { useGuardContext } from 'src/context/global/context'
3
+ import { CheckPhoneForm } from './CheckPhoneForm'
4
+ import { VerifyCodeForm } from './VerifyCodeForm'
5
+ import { SmsMFAFormProps } from '../../types'
6
+
7
+ export const SmsMfaVerifyForm: FC<SmsMFAFormProps> = ({
8
+ onSuccess,
9
+ onFail,
10
+ }) => {
11
+ const {
12
+ state: {
13
+ mfaData: { mfaToken, phone: userPhone },
14
+ },
15
+ } = useGuardContext()
16
+ const [phone, setPhone] = useState(userPhone)
17
+ const sendCodeRef = useRef<HTMLButtonElement>(null)
18
+
19
+ return (
20
+ <>
21
+ {phone ? (
22
+ <VerifyCodeForm
23
+ sendCodeRef={sendCodeRef}
24
+ onSuccess={onSuccess}
25
+ onFail={onFail}
26
+ phone={phone!}
27
+ mfaToken={mfaToken}
28
+ ></VerifyCodeForm>
29
+ ) : (
30
+ <CheckPhoneForm
31
+ mfaToken={mfaToken}
32
+ onSuccess={(phone) => {
33
+ setPhone(phone)
34
+ sendCodeRef.current?.click()
35
+ }}
36
+ ></CheckPhoneForm>
37
+ )}
38
+ </>
39
+ )
40
+ }
@@ -0,0 +1,3 @@
1
+ .authing-mfa-check-phone-from {
2
+ text-align: center;
3
+ }