goobs-frontend 0.8.4 → 0.8.5
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 +17 -16
- package/src/components/Card/variants/product/index.tsx +2 -0
- package/src/components/Card/variants/productsummary/index.tsx +2 -0
- package/src/components/CodeCopy/index.tsx +2 -0
- package/src/components/Content/Structure/qrcode/useQRCode.tsx +7 -3
- package/src/components/Dropdown/index.tsx +2 -0
- package/src/components/Grid/index.tsx +3 -1
- package/src/components/Icons/FavoriteIcon.tsx +2 -0
- package/src/components/QRCode/index.tsx +47 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "goobs-frontend",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"description": "A comprehensive React-based UI library built on Material-UI, offering a wide range of customizable components including grids, typography, buttons, cards, forms, navigation, pricing tables, steppers, tooltips, accordions, and more. Designed for building responsive and consistent user interfaces with advanced features like form validation, theming, and code syntax highlighting.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -21,31 +21,32 @@
|
|
|
21
21
|
"@emotion/cache": "^11.13.1",
|
|
22
22
|
"@emotion/react": "^11.13.3",
|
|
23
23
|
"@emotion/styled": "^11.13.0",
|
|
24
|
-
"@mui/icons-material": "^6.1.
|
|
25
|
-
"@mui/material": "^6.1.
|
|
26
|
-
"@types/lodash": "^4.17.
|
|
24
|
+
"@mui/icons-material": "^6.1.4",
|
|
25
|
+
"@mui/material": "^6.1.4",
|
|
26
|
+
"@types/lodash": "^4.17.12",
|
|
27
27
|
"highlight.js": "^11.10.0",
|
|
28
|
-
"jotai": "^2.10.
|
|
28
|
+
"jotai": "^2.10.1",
|
|
29
29
|
"lodash": "^4.17.21",
|
|
30
|
-
"next": "14.2.
|
|
31
|
-
"
|
|
30
|
+
"next": "14.2.15",
|
|
31
|
+
"otplib": "^12.0.1",
|
|
32
|
+
"react-datepicker": "^7.5.0",
|
|
32
33
|
"react-qr-code": "^2.0.15"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
|
-
"@next/eslint-plugin-next": "^14.2.
|
|
36
|
-
"@types/node": "^22.7.
|
|
37
|
-
"@types/react": "18.3.
|
|
38
|
-
"@types/react-dom": "^18.3.
|
|
39
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
40
|
-
"@typescript-eslint/parser": "^8.
|
|
41
|
-
"eslint": "^9.
|
|
42
|
-
"eslint-config-next": "^14.2.
|
|
36
|
+
"@next/eslint-plugin-next": "^14.2.15",
|
|
37
|
+
"@types/node": "^22.7.7",
|
|
38
|
+
"@types/react": "18.3.11",
|
|
39
|
+
"@types/react-dom": "^18.3.1",
|
|
40
|
+
"@typescript-eslint/eslint-plugin": "^8.10.0",
|
|
41
|
+
"@typescript-eslint/parser": "^8.10.0",
|
|
42
|
+
"eslint": "^9.13.0",
|
|
43
|
+
"eslint-config-next": "^14.2.15",
|
|
43
44
|
"eslint-config-prettier": "^9.1.0",
|
|
44
45
|
"eslint-plugin-prettier": "^5.2.1",
|
|
45
46
|
"prettier": "^3.3.3",
|
|
46
47
|
"react": "^18.3.1",
|
|
47
48
|
"react-dom": "^18.3.1",
|
|
48
|
-
"typescript": "^5.6.
|
|
49
|
+
"typescript": "^5.6.3"
|
|
49
50
|
},
|
|
50
51
|
"files": [
|
|
51
52
|
"src"
|
|
@@ -6,7 +6,7 @@ type ExtendedColumnConfig = Omit<columnconfig, 'component'> & {
|
|
|
6
6
|
component?: columnconfig['component']
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export interface ExtendedQRCodeProps extends
|
|
9
|
+
export interface ExtendedQRCodeProps extends QRCodeProps {
|
|
10
10
|
columnconfig?: ExtendedColumnConfig
|
|
11
11
|
cellconfig?: cellconfig
|
|
12
12
|
}
|
|
@@ -21,9 +21,11 @@ const useQRCode = (grid: {
|
|
|
21
21
|
index: number
|
|
22
22
|
): columnconfig => {
|
|
23
23
|
const {
|
|
24
|
-
|
|
24
|
+
username,
|
|
25
|
+
appName,
|
|
25
26
|
size,
|
|
26
27
|
title,
|
|
28
|
+
onSecretGenerated,
|
|
27
29
|
columnconfig: itemColumnConfig,
|
|
28
30
|
cellconfig,
|
|
29
31
|
...restProps
|
|
@@ -48,9 +50,11 @@ const useQRCode = (grid: {
|
|
|
48
50
|
component: (
|
|
49
51
|
<QRCodeComponent
|
|
50
52
|
key={`qrcode-${index}`}
|
|
51
|
-
|
|
53
|
+
username={username}
|
|
54
|
+
appName={appName}
|
|
52
55
|
size={size}
|
|
53
56
|
title={title}
|
|
57
|
+
onSecretGenerated={onSecretGenerated}
|
|
54
58
|
{...restProps}
|
|
55
59
|
/>
|
|
56
60
|
),
|
|
@@ -247,7 +247,9 @@ const CustomGrid: React.FC<CustomGridProps> = ({
|
|
|
247
247
|
: '100%',
|
|
248
248
|
}}
|
|
249
249
|
>
|
|
250
|
-
{currentColumnConfig?.component
|
|
250
|
+
{React.isValidElement(currentColumnConfig?.component)
|
|
251
|
+
? currentColumnConfig?.component
|
|
252
|
+
: null}
|
|
251
253
|
</Box>
|
|
252
254
|
</Grid2>
|
|
253
255
|
)
|
|
@@ -2,52 +2,58 @@ import React, { useMemo } from 'react'
|
|
|
2
2
|
import QRCode from 'react-qr-code'
|
|
3
3
|
import { Box, Typography, Paper, Theme, CircularProgress } from '@mui/material'
|
|
4
4
|
import { SxProps } from '@mui/system'
|
|
5
|
+
import { authenticator } from 'otplib'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Props for the QRCodeComponent
|
|
8
9
|
* @typedef {Object} QRCodeProps
|
|
9
|
-
* @property {string}
|
|
10
|
+
* @property {string} username - The username for the MFA setup
|
|
11
|
+
* @property {string} appName - The name of the application for MFA
|
|
10
12
|
* @property {number} [size] - The size of the QR code in pixels
|
|
11
13
|
* @property {string} [title] - An optional title to display above the QR code
|
|
12
14
|
* @property {SxProps<Theme>} [sx] - Custom styles to apply to the component
|
|
15
|
+
* @property {(secret: string) => void} [onSecretGenerated] - Callback function to receive the generated secret
|
|
13
16
|
*/
|
|
14
17
|
export interface QRCodeProps {
|
|
15
|
-
|
|
18
|
+
username: string
|
|
19
|
+
appName: string
|
|
16
20
|
size?: number
|
|
17
21
|
title?: string
|
|
18
22
|
sx?: SxProps<Theme>
|
|
23
|
+
onSecretGenerated?: (secret: string) => void
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
/**
|
|
22
|
-
* A component that displays a QR code with Material-UI styling
|
|
27
|
+
* A component that displays a QR code for MFA setup with Material-UI styling
|
|
23
28
|
* @param {QRCodeProps} props - The props for the component
|
|
24
29
|
* @returns {React.ReactElement} The rendered QR code component
|
|
25
30
|
*/
|
|
26
31
|
const QRCodeComponent: React.FC<QRCodeProps> = React.memo(
|
|
27
|
-
({
|
|
28
|
-
//
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
({ username, appName, size = 256, title, sx, onSecretGenerated }) => {
|
|
33
|
+
// Generate the secret and OTP auth URL
|
|
34
|
+
const { secret, otpAuth } = useMemo(() => {
|
|
35
|
+
const generatedSecret = authenticator.generateSecret()
|
|
36
|
+
const otpAuthUrl = authenticator.keyuri(
|
|
37
|
+
encodeURIComponent(username),
|
|
38
|
+
encodeURIComponent(appName),
|
|
39
|
+
generatedSecret
|
|
40
|
+
)
|
|
41
|
+
if (onSecretGenerated) {
|
|
42
|
+
onSecretGenerated(generatedSecret)
|
|
38
43
|
}
|
|
39
|
-
|
|
44
|
+
return { secret: generatedSecret, otpAuth: otpAuthUrl }
|
|
45
|
+
}, [username, appName, onSecretGenerated])
|
|
40
46
|
|
|
41
47
|
// Calculate responsive size
|
|
42
48
|
const responsiveSize = useMemo(() => {
|
|
43
49
|
return Math.min(size, window.innerWidth - 32) // 32px for padding
|
|
44
50
|
}, [size])
|
|
45
51
|
|
|
46
|
-
if (!
|
|
52
|
+
if (!otpAuth) {
|
|
47
53
|
return (
|
|
48
54
|
<Box sx={{ ...sx, p: 2 }} role="alert">
|
|
49
55
|
<Typography color="error">
|
|
50
|
-
Error:
|
|
56
|
+
Error: Failed to generate QR code
|
|
51
57
|
</Typography>
|
|
52
58
|
</Box>
|
|
53
59
|
)
|
|
@@ -88,13 +94,16 @@ const QRCodeComponent: React.FC<QRCodeProps> = React.memo(
|
|
|
88
94
|
}
|
|
89
95
|
>
|
|
90
96
|
<QRCode
|
|
91
|
-
value={
|
|
97
|
+
value={otpAuth}
|
|
92
98
|
size={responsiveSize}
|
|
93
99
|
style={{ height: 'auto', maxWidth: '100%', width: '100%' }}
|
|
94
|
-
aria-label={`QR Code for ${title ||
|
|
100
|
+
aria-label={`QR Code for ${title || 'MFA Setup'}`}
|
|
95
101
|
/>
|
|
96
102
|
</React.Suspense>
|
|
97
103
|
</Box>
|
|
104
|
+
<Typography variant="body2" align="center" sx={{ mt: 2 }}>
|
|
105
|
+
Secret: {secret}
|
|
106
|
+
</Typography>
|
|
98
107
|
</Paper>
|
|
99
108
|
)
|
|
100
109
|
}
|
|
@@ -103,3 +112,22 @@ const QRCodeComponent: React.FC<QRCodeProps> = React.memo(
|
|
|
103
112
|
QRCodeComponent.displayName = 'QRCodeComponent'
|
|
104
113
|
|
|
105
114
|
export default QRCodeComponent
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Verifies a MFA token against a secret.
|
|
118
|
+
*
|
|
119
|
+
* @param token - The token to verify.
|
|
120
|
+
* @param secret - The secret key to verify against.
|
|
121
|
+
* @returns A boolean indicating whether the token is valid.
|
|
122
|
+
* @throws Error if inputs are invalid.
|
|
123
|
+
*/
|
|
124
|
+
export function verifyMFAToken(token: string, secret: string): boolean {
|
|
125
|
+
if (!token || typeof token !== 'string') {
|
|
126
|
+
throw new Error('Invalid token')
|
|
127
|
+
}
|
|
128
|
+
if (!secret || typeof secret !== 'string') {
|
|
129
|
+
throw new Error('Invalid secret')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return authenticator.verify({ token, secret })
|
|
133
|
+
}
|