goobs-frontend 0.9.15 → 0.9.16
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.
package/package.json
CHANGED
|
@@ -4,6 +4,11 @@ import { Box, Paper } from '@mui/material'
|
|
|
4
4
|
import { SxProps } from '@mui/system'
|
|
5
5
|
import { authenticator } from 'otplib'
|
|
6
6
|
import Typography from '../Typography'
|
|
7
|
+
import CustomButton, { CustomButtonProps } from '../Button'
|
|
8
|
+
import { CheckCircleOutline } from '@mui/icons-material'
|
|
9
|
+
import ConfirmationCodeInputs, {
|
|
10
|
+
ConfirmationCodeInputsProps,
|
|
11
|
+
} from '../ConfirmationCodeInput'
|
|
7
12
|
|
|
8
13
|
/**
|
|
9
14
|
* Props for the QRCodeComponent
|
|
@@ -14,6 +19,18 @@ import Typography from '../Typography'
|
|
|
14
19
|
* @property {string} [title] - An optional title to display above the QR code
|
|
15
20
|
* @property {SxProps} [sx] - Custom styles to apply to the component
|
|
16
21
|
* @property {(secret: string) => void} [onSecretGenerated] - Callback function to receive the generated secret
|
|
22
|
+
* @property {boolean} [showVerifyButton] - Whether to show the verify button
|
|
23
|
+
* @property {() => void | Promise<void>} [onVerify] - Callback function for when the Verify button is clicked
|
|
24
|
+
* @property {() => void | Promise<void>} [onDisableVerification] - Required callback function for when verification is disabled
|
|
25
|
+
* @property {Partial<CustomButtonProps>} [verifyButtonProps] - Custom props for the Verify button
|
|
26
|
+
* @property {Partial<CustomButtonProps>} [disableVerificationButtonProps] - Custom props for the Disable Verification button
|
|
27
|
+
* @property {boolean} [showSuccessState] - Whether to show the success state UI
|
|
28
|
+
* @property {string} [successMessage] - Custom success message to display
|
|
29
|
+
* @property {boolean} [showConfirmationInput] - Whether to show the confirmation code input
|
|
30
|
+
* @property {string} [confirmationCode] - The current confirmation code value
|
|
31
|
+
* @property {(value: string) => void} [onConfirmationCodeChange] - Callback for when confirmation code changes
|
|
32
|
+
* @property {ConfirmationCodeInputsProps} [confirmationCodeProps] - Custom props for the confirmation code input
|
|
33
|
+
* @property {boolean} [showDisableConfirmation] - Whether to show the disable confirmation state
|
|
17
34
|
*/
|
|
18
35
|
export interface QRCodeProps {
|
|
19
36
|
username: string
|
|
@@ -22,6 +39,18 @@ export interface QRCodeProps {
|
|
|
22
39
|
title?: string
|
|
23
40
|
sx?: SxProps
|
|
24
41
|
onSecretGenerated?: (secret: string) => void
|
|
42
|
+
showVerifyButton?: boolean
|
|
43
|
+
onVerify?: () => void | Promise<void>
|
|
44
|
+
onDisableVerification?: () => void | Promise<void>
|
|
45
|
+
verifyButtonProps?: Partial<CustomButtonProps>
|
|
46
|
+
disableVerificationButtonProps?: Partial<CustomButtonProps>
|
|
47
|
+
showSuccessState?: boolean
|
|
48
|
+
successMessage?: string
|
|
49
|
+
showConfirmationInput?: boolean
|
|
50
|
+
confirmationCode?: string
|
|
51
|
+
onConfirmationCodeChange?: (value: string) => void
|
|
52
|
+
confirmationCodeProps?: Partial<ConfirmationCodeInputsProps>
|
|
53
|
+
showDisableConfirmation?: boolean
|
|
25
54
|
}
|
|
26
55
|
|
|
27
56
|
/**
|
|
@@ -37,6 +66,18 @@ const QRCodeComponent: React.FC<QRCodeProps> = React.memo(
|
|
|
37
66
|
title,
|
|
38
67
|
sx,
|
|
39
68
|
onSecretGenerated,
|
|
69
|
+
showVerifyButton = false,
|
|
70
|
+
onVerify,
|
|
71
|
+
onDisableVerification,
|
|
72
|
+
verifyButtonProps = {},
|
|
73
|
+
disableVerificationButtonProps = {},
|
|
74
|
+
showSuccessState = false,
|
|
75
|
+
successMessage = 'Verification Successful',
|
|
76
|
+
showConfirmationInput = false,
|
|
77
|
+
confirmationCode = '',
|
|
78
|
+
onConfirmationCodeChange,
|
|
79
|
+
confirmationCodeProps = {},
|
|
80
|
+
showDisableConfirmation = false,
|
|
40
81
|
}) => {
|
|
41
82
|
// Generate the secret and OTP auth URL
|
|
42
83
|
const { secret, otpAuth } = useMemo(() => {
|
|
@@ -75,6 +116,128 @@ const QRCodeComponent: React.FC<QRCodeProps> = React.memo(
|
|
|
75
116
|
)
|
|
76
117
|
}
|
|
77
118
|
|
|
119
|
+
// If showing success state, render the success UI
|
|
120
|
+
if (showSuccessState) {
|
|
121
|
+
return (
|
|
122
|
+
<Box
|
|
123
|
+
display="flex"
|
|
124
|
+
flexDirection="column"
|
|
125
|
+
alignItems="center"
|
|
126
|
+
gap={2}
|
|
127
|
+
padding={3}
|
|
128
|
+
width="100%"
|
|
129
|
+
>
|
|
130
|
+
<CheckCircleOutline sx={{ fontSize: 60, color: 'green' }} />
|
|
131
|
+
<Typography
|
|
132
|
+
text={successMessage}
|
|
133
|
+
fontvariant="merrih5"
|
|
134
|
+
align="center"
|
|
135
|
+
/>
|
|
136
|
+
<Box sx={{ display: 'flex', gap: 2, width: '100%' }}>
|
|
137
|
+
<CustomButton
|
|
138
|
+
text="Disable Verification"
|
|
139
|
+
fontcolor="white"
|
|
140
|
+
backgroundcolor="black"
|
|
141
|
+
width="100%"
|
|
142
|
+
height="40px"
|
|
143
|
+
variant="outlined"
|
|
144
|
+
{...disableVerificationButtonProps}
|
|
145
|
+
onClick={() => {
|
|
146
|
+
if (onDisableVerification) void onDisableVerification()
|
|
147
|
+
}}
|
|
148
|
+
/>
|
|
149
|
+
</Box>
|
|
150
|
+
</Box>
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// If showing disable confirmation state, render QR with confirmation input
|
|
155
|
+
if (showDisableConfirmation) {
|
|
156
|
+
return (
|
|
157
|
+
<Paper
|
|
158
|
+
elevation={3}
|
|
159
|
+
sx={{
|
|
160
|
+
p: 3,
|
|
161
|
+
display: 'inline-block',
|
|
162
|
+
maxWidth: '100%',
|
|
163
|
+
boxSizing: 'border-box',
|
|
164
|
+
...sx,
|
|
165
|
+
}}
|
|
166
|
+
>
|
|
167
|
+
{title && (
|
|
168
|
+
<Typography
|
|
169
|
+
text={title}
|
|
170
|
+
fontvariant="merrih5"
|
|
171
|
+
align="center"
|
|
172
|
+
gutterBottom
|
|
173
|
+
/>
|
|
174
|
+
)}
|
|
175
|
+
<Box
|
|
176
|
+
sx={{
|
|
177
|
+
display: 'flex',
|
|
178
|
+
justifyContent: 'center',
|
|
179
|
+
alignItems: 'center',
|
|
180
|
+
width: responsiveSize,
|
|
181
|
+
height: responsiveSize,
|
|
182
|
+
margin: 'auto',
|
|
183
|
+
}}
|
|
184
|
+
>
|
|
185
|
+
<QRCode
|
|
186
|
+
value={otpAuth}
|
|
187
|
+
size={responsiveSize}
|
|
188
|
+
style={{ height: 'auto', maxWidth: '100%', width: '100%' }}
|
|
189
|
+
aria-label={`QR Code for ${title || 'MFA Setup'}`}
|
|
190
|
+
data-testid="mfa-qrcode"
|
|
191
|
+
/>
|
|
192
|
+
</Box>
|
|
193
|
+
<Box sx={{ mt: 2, textAlign: 'center' }}>
|
|
194
|
+
<Typography
|
|
195
|
+
text={`${appName}: ${username}`}
|
|
196
|
+
fontvariant="merriparagraph"
|
|
197
|
+
align="center"
|
|
198
|
+
/>
|
|
199
|
+
</Box>
|
|
200
|
+
|
|
201
|
+
<Box
|
|
202
|
+
sx={{
|
|
203
|
+
mt: 3,
|
|
204
|
+
display: 'flex',
|
|
205
|
+
flexDirection: 'column',
|
|
206
|
+
alignItems: 'center',
|
|
207
|
+
gap: 2,
|
|
208
|
+
}}
|
|
209
|
+
>
|
|
210
|
+
<ConfirmationCodeInputs
|
|
211
|
+
isValid={false}
|
|
212
|
+
codeLength={6}
|
|
213
|
+
value={confirmationCode}
|
|
214
|
+
onChange={onConfirmationCodeChange}
|
|
215
|
+
showActionButtons={false}
|
|
216
|
+
onDisableVerification={() => {}}
|
|
217
|
+
{...confirmationCodeProps}
|
|
218
|
+
/>
|
|
219
|
+
|
|
220
|
+
<CustomButton
|
|
221
|
+
text="Verify & Disable"
|
|
222
|
+
fontcolor="white"
|
|
223
|
+
backgroundcolor="black"
|
|
224
|
+
width="100%"
|
|
225
|
+
height="40px"
|
|
226
|
+
{...verifyButtonProps}
|
|
227
|
+
onClick={() => {
|
|
228
|
+
if (onVerify) void onVerify()
|
|
229
|
+
}}
|
|
230
|
+
disableButton={
|
|
231
|
+
verifyButtonProps?.disableButton ||
|
|
232
|
+
(confirmationCode.length < 6 ? 'true' : 'false')
|
|
233
|
+
}
|
|
234
|
+
/>
|
|
235
|
+
</Box>
|
|
236
|
+
</Paper>
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Default QR code view with optional confirmation input and buttons
|
|
78
241
|
return (
|
|
79
242
|
<Paper
|
|
80
243
|
elevation={3}
|
|
@@ -119,6 +282,44 @@ const QRCodeComponent: React.FC<QRCodeProps> = React.memo(
|
|
|
119
282
|
align="center"
|
|
120
283
|
/>
|
|
121
284
|
</Box>
|
|
285
|
+
|
|
286
|
+
{showConfirmationInput && (
|
|
287
|
+
<Box sx={{ mt: 3, display: 'flex', justifyContent: 'center' }}>
|
|
288
|
+
<ConfirmationCodeInputs
|
|
289
|
+
isValid={false}
|
|
290
|
+
codeLength={6}
|
|
291
|
+
value={confirmationCode}
|
|
292
|
+
onChange={onConfirmationCodeChange}
|
|
293
|
+
showActionButtons={false}
|
|
294
|
+
onDisableVerification={() => {}}
|
|
295
|
+
{...confirmationCodeProps}
|
|
296
|
+
/>
|
|
297
|
+
</Box>
|
|
298
|
+
)}
|
|
299
|
+
|
|
300
|
+
{showVerifyButton && (
|
|
301
|
+
<Box
|
|
302
|
+
sx={{ mt: 3, display: 'flex', justifyContent: 'center', gap: 2 }}
|
|
303
|
+
>
|
|
304
|
+
<CustomButton
|
|
305
|
+
text="Verify Code"
|
|
306
|
+
fontcolor="white"
|
|
307
|
+
backgroundcolor="black"
|
|
308
|
+
width="100%"
|
|
309
|
+
height="40px"
|
|
310
|
+
{...verifyButtonProps}
|
|
311
|
+
onClick={() => {
|
|
312
|
+
if (onVerify) void onVerify()
|
|
313
|
+
}}
|
|
314
|
+
disableButton={
|
|
315
|
+
verifyButtonProps?.disableButton ||
|
|
316
|
+
(showConfirmationInput && confirmationCode.length < 6
|
|
317
|
+
? 'true'
|
|
318
|
+
: 'false')
|
|
319
|
+
}
|
|
320
|
+
/>
|
|
321
|
+
</Box>
|
|
322
|
+
)}
|
|
122
323
|
</Paper>
|
|
123
324
|
)
|
|
124
325
|
}
|
|
@@ -148,8 +349,8 @@ export function verifyMFAToken(token: string, secret: string): boolean {
|
|
|
148
349
|
// Configure authenticator options to match Microsoft Authenticator
|
|
149
350
|
authenticator.options = {
|
|
150
351
|
window: 1, // Allow codes from 1 step before and after
|
|
151
|
-
digits: 6, // Microsoft Authenticator uses 6-digit codes
|
|
152
352
|
step: 30, // 30-second interval for code generation
|
|
353
|
+
digits: 6, // Microsoft Authenticator uses 6-digit codes
|
|
153
354
|
}
|
|
154
355
|
|
|
155
356
|
return authenticator.verify({ token, secret })
|
|
@@ -229,9 +229,6 @@ const Typography = ({
|
|
|
229
229
|
if (typeof actualVariant === 'string' && actualVariant.length > 0) {
|
|
230
230
|
// First, try to get the variant from the theme
|
|
231
231
|
try {
|
|
232
|
-
// Log the actual variant being used
|
|
233
|
-
console.log('Using variant:', actualVariant)
|
|
234
|
-
|
|
235
232
|
// Check if we're using a custom font variant (e.g., 'merrih2')
|
|
236
233
|
if (/^(arapey|inter|merri)/.test(actualVariant)) {
|
|
237
234
|
// For custom variants, we need to check if they exist in the theme
|
|
@@ -246,7 +243,6 @@ const Typography = ({
|
|
|
246
243
|
const themeVariant = themeTypography[
|
|
247
244
|
actualVariant
|
|
248
245
|
] as TypographyVariantStyle
|
|
249
|
-
console.log('Found theme variant:', themeVariant)
|
|
250
246
|
|
|
251
247
|
if (themeVariant) {
|
|
252
248
|
variantStyle = {
|
|
@@ -259,8 +255,6 @@ const Typography = ({
|
|
|
259
255
|
}
|
|
260
256
|
} else {
|
|
261
257
|
// Custom variant not in theme, fallback to hardcoded styles
|
|
262
|
-
console.log('Custom variant not found in theme, using fallback')
|
|
263
|
-
|
|
264
258
|
const fontFamily = actualVariant.startsWith('arapey')
|
|
265
259
|
? 'arapey'
|
|
266
260
|
: actualVariant.startsWith('inter')
|