Compare commits

...

28 Commits

Author SHA1 Message Date
0b56813ece Add hyphen and underscore as valid password symbols, increase valid password to 6 chars, add password validity and strength indicator 2024-02-14 09:55:11 +03:00
8d205d9d93 Merge pull request #53 from nostrband/develop
Allow import w/ existing name
2024-02-13 11:48:24 +03:00
9a18e79862 Allow importing nsec w/ existing name, improve import form 2024-02-13 11:47:35 +03:00
ab2df05d50 Merge pull request #33 from nostrband/main
Merge w/ main
2024-02-13 08:21:17 +03:00
163de16a84 Merge pull request #32 from nostrband/fix/referrer
Don't use referrer if it's our domain
2024-02-13 08:20:39 +03:00
e9b290db30 Don't use referrer if it's our domain 2024-02-13 08:19:42 +03:00
544ac18b59 Merge pull request #31 from nostrband/develop
New logo
2024-02-12 14:56:23 +03:00
2551022d5e Merge pull request #30 from nostrband/feature/app-logo
change app logo
2024-02-12 14:33:13 +03:00
45c39ca904 change app logo 2024-02-12 17:26:30 +06:00
041b84eb0b Merge pull request #29 from nostrband/develop
Remove redirect to initial=true
2024-02-12 14:14:28 +03:00
69166ff501 Remove redirect to initial=true 2024-02-12 14:13:50 +03:00
043e159e53 Merge pull request #28 from nostrband/develop
Fix bad validity checks on confirm modal
2024-02-12 12:43:07 +03:00
13d0a62fec Fix bad validity checks on confirm modal 2024-02-12 12:42:32 +03:00
d11cccec35 Merge pull request #27 from nostrband/develop
Fix connect modal without pending request id
2024-02-12 10:58:28 +03:00
81b8624bd1 Fix connect modal without pending request id 2024-02-12 10:57:55 +03:00
f45300583c Merge pull request #26 from nostrband/develop
Lots of minor fixes
2024-02-12 10:29:57 +03:00
0cf042e5d9 Merge pull request #23 from nostrband/feature/app-details
Feature/app details
2024-02-12 10:28:23 +03:00
977a4b5c93 Merge pull request #25 from nostrband/develop
Fix enablePush at connectModal
2024-02-09 15:58:52 +03:00
8ccdc06f49 Fix enablePush at connectModal 2024-02-09 15:57:03 +03:00
6589a98d52 Merge pull request #24 from nostrband/develop
Save app url from referrer on connect request by bunker url
2024-02-09 15:23:14 +03:00
fed1ece2d4 Save app url from referrer on connect request by bunker url 2024-02-09 15:09:17 +03:00
e7e3b871e4 Merge pull request #22 from nostrband/develop
Add proper app name to app page
2024-02-08 21:18:17 +03:00
063213cb89 Merge pull request #21 from nostrband/develop
Add referrer log
2024-02-08 21:01:47 +03:00
0bf6fafb3e Merge pull request #20 from nostrband/develop
Add referrer parsing to connect modal
2024-02-08 20:38:05 +03:00
14a83ec721 Merge pull request #19 from nostrband/develop
Add text to enable notifications, add account created message
2024-02-08 19:52:46 +03:00
dfb8889b9d Merge pull request #18 from nostrband/develop
Implement connectApp logic, add app url and icon
2024-02-08 14:53:10 +03:00
b24e3d31b0 Merge pull request #17 from nostrband/develop
Fix app avatars, fix perm names in App page, fix time format
2024-02-08 08:52:25 +03:00
b27fb5ec07 Merge pull request #16 from nostrband/develop
Develop
2024-02-07 10:46:04 +03:00
23 changed files with 197 additions and 127 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 502 B

After

Width:  |  Height:  |  Size: 536 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,39 +1,22 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="%PUBLIC_URL%/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="%PUBLIC_URL%/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="%PUBLIC_URL%/favicon-16x16.png"
/>
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@200;300;400;500;600;700&display=swap"
rel="stylesheet"
/>
<!--
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" sizes="180x180" href="%PUBLIC_URL%/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="%PUBLIC_URL%/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="%PUBLIC_URL%/favicon-16x16.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@200;300;400;500;600;700&display=swap"
rel="stylesheet"
/>
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
@ -42,12 +25,12 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Nsec.app</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
<title>Nsec.app</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
@ -56,5 +39,6 @@
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
--></body>
-->
</body>
</html>

View File

@ -1,20 +1,20 @@
{
"name": "Nsec.app",
"short_name": "Nsec.app - Nostr key management tool",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": ".",
"theme_color": "#000000",
"background_color": "#ffffff",
"display": "standalone"
"name": "Nsec.app",
"short_name": "Nsec.app - Nostr key management tool",
"start_url": ".",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#000000",
"background_color": "#ffffff",
"display": "standalone"
}

View File

@ -58,7 +58,7 @@ function App() {
// rerender
// setRender((r) => r + 1)
if (!keys.length) handleOpen(MODAL_PARAMS_KEYS.INITIAL)
// if (!keys.length) handleOpen(MODAL_PARAMS_KEYS.INITIAL)
// eslint-disable-next-line
}, [dispatch])

View File

@ -1,3 +1,3 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25.5479 20.2413C24.4588 19.0649 22.6437 18.9473 21.3126 19.7708L18.8925 17.8885C19.8606 16.2416 19.9816 14.2417 19.0135 12.5948L21.4337 10.8302C23.0067 12.1242 25.3059 12.0066 26.7579 10.5949C28.331 9.06555 28.452 6.59511 26.8789 5.06579C25.3059 3.53647 22.7647 3.41883 21.1916 4.94815C19.9816 6.12455 19.6186 7.88915 20.3446 9.41847L18.0455 11.1831C16.1094 9.41847 13.0842 9.18319 11.0271 10.5949L8.72796 8.35971C10.059 6.59511 9.93803 4.00703 8.24394 2.36007C6.42884 0.477835 3.28267 0.477835 1.46757 2.24243C-0.468534 4.00703 -0.468534 7.06567 1.34656 8.83027C3.04066 10.4772 5.58179 10.7125 7.5179 9.53611L9.81702 11.7713C8.36494 13.7712 8.36495 16.4769 9.93803 18.3591L7.27589 21.1825C6.06582 20.5943 4.61374 20.8295 3.64569 21.7707C2.43562 22.9471 2.31462 24.9469 3.52468 26.1233C4.73475 27.2997 6.79186 27.4174 8.00192 26.241C9.09098 25.1822 9.21199 23.6529 8.48595 22.4765L11.0271 19.6531C13.0842 20.9472 15.8674 20.8295 17.6824 19.1826L20.1026 21.0648C19.6186 22.2412 19.7396 23.6529 20.7076 24.594C21.9177 25.8881 24.0958 25.8881 25.3059 24.7117C26.7579 23.5353 26.7579 21.5354 25.5479 20.2413Z" fill="currentColor"/>
<svg width="32" height="36" viewBox="0 0 32 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M28.2711 21.2958C27.1084 21.2958 26.0749 21.8372 25.4037 22.6797L21.8702 20.6405C22.2577 19.8106 22.4755 18.8846 22.4755 17.908C22.4755 16.9314 22.2577 16.0053 21.8702 15.1755L25.3404 13.1742C26.0091 14.0648 27.0704 14.6442 28.2711 14.6442C30.2949 14.6442 31.9363 13.0047 31.9363 10.9831C31.9363 8.96158 30.2949 7.32208 28.2711 7.32208C26.2472 7.32208 24.6058 8.96158 24.6058 10.9831C24.6058 11.4006 24.6793 11.8003 24.8084 12.1748L21.3028 14.1963C20.2338 12.6732 18.5241 11.6333 16.5635 11.4638V7.274C18.3189 7.00076 19.6639 5.49029 19.6639 3.66104C19.6639 1.6395 18.0225 0 15.9987 0C13.9748 0 12.3334 1.6395 12.3334 3.66104C12.3334 5.49029 13.6784 7.00329 15.4338 7.274V11.4638C13.4733 11.6333 11.7635 12.6732 10.6946 14.1963L7.1889 12.1748C7.31808 11.8003 7.39154 11.4006 7.39154 10.9831C7.39154 8.96158 5.75015 7.32208 3.72629 7.32208C1.70242 7.32208 0.0610352 8.96158 0.0610352 10.9831C0.0610352 13.0047 1.70242 14.6442 3.72629 14.6442C4.92693 14.6442 5.98825 14.0648 6.65697 13.1742L10.1272 15.1755C9.73963 16.0053 9.52179 16.9314 9.52179 17.908C9.52179 18.8846 9.73963 19.8106 10.1272 20.643L6.59364 22.6822C5.9224 21.8397 4.88893 21.2983 3.72629 21.2983C1.70242 21.2983 0.0610352 22.9378 0.0610352 24.9593C0.0610352 26.9809 1.70242 28.6204 3.72629 28.6204C5.75015 28.6204 7.39154 26.9809 7.39154 24.9593C7.39154 24.5039 7.30542 24.0687 7.1509 23.6639L10.6946 21.6196C11.3329 22.5279 12.1992 23.2667 13.2098 23.7499V32.3497C13.2098 32.9721 13.5569 33.8551 13.9799 34.3131L15.2286 35.6565C15.6516 36.1145 16.3457 36.1145 16.7712 35.6565L18.02 34.3131C18.443 33.8551 18.79 32.9721 18.79 32.3497V23.7499C19.8007 23.2667 20.667 22.5304 21.3053 21.6196L24.849 23.6639C24.697 24.0662 24.6083 24.5014 24.6083 24.9593C24.6083 26.9809 26.2497 28.6204 28.2736 28.6204C30.2975 28.6204 31.9388 26.9809 31.9388 24.9593C31.9388 22.9378 30.2975 21.2983 28.2736 21.2983L28.2711 21.2958Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,7 +1,7 @@
import { useModalSearchParams } from '@/hooks/useModalSearchParams'
import { Modal } from '@/shared/Modal/Modal'
import { MODAL_PARAMS_KEYS } from '@/types/modal'
import { call, getAppIconTitle, getDomain, getShortenNpub } from '@/utils/helpers/helpers'
import { askNotificationPermission, call, getAppIconTitle, getDomain, getReferrerAppUrl, getShortenNpub } from '@/utils/helpers/helpers'
import { Avatar, Box, Stack, Typography } from '@mui/material'
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
import { useAppSelector } from '@/store/hooks/redux'
@ -37,15 +37,7 @@ export const ModalConfirmConnect = () => {
const triggerApp = apps.find((app) => app.appNpub === appNpub)
const { name, url = '', icon = '' } = triggerApp || {}
let appUrl = url || searchParams.get('appUrl') || ''
// console.log('referrer', window.document.referrer, appUrl)
if (!appUrl && window.document.referrer) {
try {
const u = new URL(window.document.referrer)
appUrl = u.origin
} catch {}
}
const appUrl = url || searchParams.get('appUrl') || getReferrerAppUrl();
const appDomain = getDomain(appUrl)
const appName = name || appDomain || getShortenNpub(appNpub)
const appAvatarTitle = getAppIconTitle(name || appDomain, appNpub)
@ -62,9 +54,11 @@ export const ModalConfirmConnect = () => {
})
const isNpubExists = npub.trim().length && keys.some((key) => key.npub === npub)
const isAppNpubExists = appNpub.trim().length && apps.some((app) => app.appNpub === appNpub)
// App doesn't exist yet!
// const isAppNpubExists = appNpub.trim().length && apps.some((app) => app.appNpub === appNpub)
const isPendingReqIdExists = pendingReqId.trim().length && pending.some((p) => p.id === pendingReqId)
if (isModalOpened && (!isNpubExists || !isAppNpubExists || !isPendingReqIdExists)) {
console.log("pending", {isModalOpened, isPendingReqIdExists, isNpubExists, /*isAppNpubExists,*/ pendingReqId, pending});
if (!isPopup && isModalOpened && (!isNpubExists /*|| !isAppNpubExists*/ || (pendingReqId && !isPendingReqIdExists))) {
closeModalAfterRequest()
return null
}
@ -88,11 +82,13 @@ export const ModalConfirmConnect = () => {
if (selectedActionType === ACTION_TYPE.BASIC) perms = [ACTION_TYPE.BASIC]
if (pendingReqId) {
const options = { perms }
const options = { perms, appUrl }
await confirmPending(pendingReqId, true, true, options)
} else {
try {
await swicCall('enablePush')
await askNotificationPermission()
const result = await swicCall('enablePush')
if (!result) throw new Error('Failed to activate the push subscription')
console.log('enablePush done')
} catch (e: any) {
console.log('error', e)

View File

@ -17,6 +17,7 @@ import { useDebounce } from 'use-debounce'
import { fetchNip05 } from '@/utils/helpers/helpers'
import { DOMAIN } from '@/utils/consts'
import { CheckmarkIcon } from '@/assets'
import { getPublicKey, nip19 } from 'nostr-tools'
const FORM_DEFAULT_VALUES = {
username: '',
@ -42,35 +43,71 @@ export const ModalImportKeys = () => {
mode: 'onSubmit',
})
const [isLoading, setIsLoading] = useState(false)
const [isAvailable, setIsAvailable] = useState(false)
const [nameNpub, setNameNpub] = useState('')
const [isTakenByNsec, setIsTakenByNsec] = useState(false)
const [isBadNsec, setIsBadNsec] = useState(false)
const enteredUsername = watch('username')
const enteredNsec = watch('nsec')
const [debouncedUsername] = useDebounce(enteredUsername, 100)
const [debouncedNsec] = useDebounce(enteredNsec, 100)
const checkIsUsernameAvailable = useCallback(async () => {
if (!debouncedUsername.trim().length) return undefined
const npubNip05 = await fetchNip05(`${debouncedUsername}@${DOMAIN}`)
setIsAvailable(!npubNip05)
setNameNpub(npubNip05 || '')
}, [debouncedUsername])
useEffect(() => {
checkIsUsernameAvailable()
}, [checkIsUsernameAvailable])
const checkNsecUsername = useCallback(async () => {
if (!debouncedNsec.trim().length) {
setIsTakenByNsec(false)
setIsBadNsec(false)
return
}
try {
const { type, data } = nip19.decode(debouncedNsec)
const ok = type === 'nsec';
setIsBadNsec(!ok)
if (ok) {
const npub = nip19.npubEncode(
// @ts-ignore
getPublicKey(data))
setIsTakenByNsec(!!nameNpub && nameNpub === npub)
} else {
setIsTakenByNsec(false)
}
} catch {
setIsBadNsec(true)
setIsTakenByNsec(false)
return
}
}, [debouncedNsec])
useEffect(() => {
checkNsecUsername()
}, [checkNsecUsername])
const cleanUpStates = useCallback(() => {
hidePassword()
reset()
setIsLoading(false)
setIsAvailable(false)
setNameNpub('')
setIsTakenByNsec(false)
setIsBadNsec(false)
}, [reset, hidePassword])
const notify = useEnqueueSnackbar()
const navigate = useNavigate()
const submitHandler = async (values: FormInputType) => {
if (isLoading || !isAvailable) return undefined
if (isLoading) return undefined
try {
const { nsec, username } = values
if (!nsec || !username) throw new Error("Enter username and nsec")
if (nameNpub && !isTakenByNsec) throw new Error("Name taken")
setIsLoading(true)
const k: any = await swicCall('importKey', username, nsec)
notify('Key imported!', 'success')
@ -88,9 +125,11 @@ export const ModalImportKeys = () => {
}
}, [isModalOpened, cleanUpStates])
const getInputHelperText = () => {
const getNameHelperText = () => {
if (!enteredUsername) return "Don't worry, username can be changed later."
if (!isAvailable) return 'Already taken'
if (isTakenByNsec) return 'Name matches your key'
if (isBadNsec) return 'Invalid nsec'
if (nameNpub) return 'Already taken'
return (
<>
<CheckmarkIcon /> Available
@ -98,7 +137,13 @@ export const ModalImportKeys = () => {
)
}
const inputHelperText = getInputHelperText()
const getNsecHelperText = () => {
if (isBadNsec) return 'Invalid nsec'
return 'Keys stay on your device.'
}
const nameHelperText = getNameHelperText()
const nsecHelperText = getNsecHelperText()
return (
<Modal open={isModalOpened} onClose={handleCloseModal}>
@ -116,14 +161,14 @@ export const ModalImportKeys = () => {
endAdornment={<Typography color={'#FFFFFFA8'}>@{DOMAIN}</Typography>}
{...register('username')}
error={!!errors.username}
helperText={inputHelperText}
helperText={nameHelperText}
helperTextProps={{
sx: {
'&.helper_text': {
color:
enteredUsername && isAvailable
enteredUsername && (isTakenByNsec || !nameNpub)
? theme.palette.success.main
: enteredUsername && !isAvailable
: enteredUsername && nameNpub
? theme.palette.error.main
: theme.palette.textSecondaryDecorate.main,
},
@ -137,11 +182,13 @@ export const ModalImportKeys = () => {
{...register('nsec')}
error={!!errors.nsec}
{...inputProps}
helperText="Keys stay on your device."
helperText={nsecHelperText}
helperTextProps={{
sx: {
'&.helper_text': {
color: theme.palette.textSecondaryDecorate.main,
color: isBadNsec
? theme.palette.error.main
: theme.palette.textSecondaryDecorate.main,
},
},
}}

View File

@ -5,10 +5,10 @@ export const StyledAppLogo = styled((props) => (
<Box {...props}>
<AppLogo />
</Box>
))({
background: '#00000054',
))(() => ({
background: '#0d0d0d',
padding: '0.75rem',
borderRadius: '16px',
display: 'grid',
placeItems: 'center',
})
}))

View File

@ -6,7 +6,7 @@ export const StyledAppLogo = styled((props) => (
<AppLogo />
</Box>
))({
background: '#00000054',
background: '#0d0d0d',
padding: '0.75rem',
borderRadius: '16px',
display: 'grid',

View File

@ -16,6 +16,7 @@ import { dbi } from '@/modules/db'
import { usePassword } from '@/hooks/usePassword'
import { useAppSelector } from '@/store/hooks/redux'
import { selectKeys } from '@/store'
import { isValidPassphase, isWeakPassphase } from '@/modules/keys'
type ModalSettingsProps = {
isSynced: boolean
@ -58,8 +59,9 @@ export const ModalSettings: FC<ModalSettingsProps> = ({ isSynced }) => {
}
const handlePasswordChange = (e: ChangeEvent<HTMLInputElement>) => {
setIsPasswordInvalid(false)
setEnteredPassword(e.target.value)
const password = e.target.value
setIsPasswordInvalid(!!password && !isValidPassphase(password))
setEnteredPassword(password)
}
const onClose = () => {
@ -76,7 +78,7 @@ export const ModalSettings: FC<ModalSettingsProps> = ({ isSynced }) => {
e.preventDefault()
setIsPasswordInvalid(false)
if (enteredPassword.trim().length < 6) {
if (!isValidPassphase(enteredPassword)) {
return setIsPasswordInvalid(true)
}
try {
@ -114,18 +116,30 @@ export const ModalSettings: FC<ModalSettingsProps> = ({ isSynced }) => {
{...inputProps}
onChange={handlePasswordChange}
value={enteredPassword}
helperText={isPasswordInvalid ? 'Invalid password' : ''}
// helperText={isPasswordInvalid ? 'Invalid password' : ''}
placeholder="Enter a password"
helperTextProps={{
sx: {
'&.helper_text': {
color: 'red',
},
},
}}
// helperTextProps={{
// sx: {
// '&.helper_text': {
// color: 'red',
// },
// },
// }}
disabled={!isChecked}
/>
{isSynced ? (
{isPasswordInvalid ? (
<Typography variant="body2" color={'red'}>
Password must include 6+ English letters, numbers or punctuation marks.
</Typography>
) : !!enteredPassword && isWeakPassphase(enteredPassword) ? (
<Typography variant="body2" color={'orange'}>
Weak password
</Typography>
) : !!enteredPassword && !isPasswordInvalid ? (
<Typography variant="body2" color={'green'}>
Good password
</Typography>
) : isSynced ? (
<Typography variant="body2" color={'GrayText'}>
To change your password, type a new one and sync.
</Typography>
@ -139,7 +153,7 @@ export const ModalSettings: FC<ModalSettingsProps> = ({ isSynced }) => {
Sync {isLoading && <CircularProgress sx={{ marginLeft: '0.5rem' }} size={'1rem'} />}
</StyledButton>
</StyledSettingContainer>
<Button onClick={onClose}>Done</Button>
{/* <Button onClick={onClose}>Done</Button> */}
</Stack>
</Modal>
)

View File

@ -6,7 +6,7 @@ export const StyledAppLogo = styled((props) => (
<AppLogo />
</Box>
))({
background: '#00000054',
background: '#0d0d0d',
padding: '0.75rem',
borderRadius: '16px',
display: 'grid',

View File

@ -1,6 +1,5 @@
import { Avatar, Stack, Toolbar, Typography } from '@mui/material'
import { AppLogo } from '../../assets'
import { StyledAppBar, StyledAppName, StyledProfileContainer, StyledThemeButton } from './styled'
import { StyledAppBar, StyledAppLogo, StyledAppName, StyledProfileContainer, StyledThemeButton } from './styled'
import { Menu } from './components/Menu'
import { useNavigate, useParams } from 'react-router-dom'
import { ProfileMenu } from './components/ProfileMenu'
@ -47,7 +46,7 @@ export const Header = () => {
{!showProfile && (
<StyledAppName>
<AppLogo />
<StyledAppLogo />
<span>Nsec.app</span>
</StyledAppName>
)}

View File

@ -1,3 +1,4 @@
import { AppLogo } from '@/assets'
import { AppBar, IconButton, Stack, StackProps, Typography, TypographyProps, styled } from '@mui/material'
import { Link } from 'react-router-dom'
@ -47,3 +48,9 @@ export const StyledProfileContainer = styled((props: StackProps) => <Stack {...p
export const StyledThemeButton = styled(IconButton)({
margin: '0 0.5rem',
})
export const StyledAppLogo = styled(AppLogo)(({ theme }) => ({
'& path': {
fill: theme.palette.text.primary,
},
}))

View File

@ -661,7 +661,7 @@ export class NoauthBackend {
timestamp: Date.now(),
name: '',
icon: '',
url: '',
url: options.appUrl || '',
})
// reload
@ -745,6 +745,7 @@ export class NoauthBackend {
// OAuth flow
const confirmMethod = method === 'connect' ? 'confirm-connect' : 'confirm-event'
const authUrl = `${self.swg.location.origin}/key/${npub}?${confirmMethod}=true&appNpub=${appNpub}&reqId=${id}&popup=true`
// const authUrl = `${self.swg.location.origin}/key/${npub}?popup=true`
console.log('sending authUrl', authUrl, 'for', req)
// NOTE: if you set 'Update on reload' in the Chrome SW console
// then this message will cause a new tab opened by the peer,

View File

@ -21,11 +21,31 @@ const ALGO = 'aes-256-cbc'
const IV_SIZE = 16
// valid passwords are a limited ASCII only, see notes below
const ASCII_REGEX = /^[A-Za-z0-9!@#$%^&*()]{4,}$/
const ASCII_REGEX = /^[A-Za-z0-9!@#$%^&*()\-_]{6,}$/
const ALGO_LOCAL = 'AES-CBC'
const KEY_SIZE_LOCAL = 256
export function isValidPassphase(passphrase: string): boolean {
return ASCII_REGEX.test(passphrase)
}
export function isWeakPassphase(passphrase: string): boolean {
const BIG_LETTER_REGEX = /[A-Z]+/
const SMALL_LETTER_REGEX = /[a-z]+/
const NUMBER_REGEX = /[0-9]+/
const PUNCT_REGEX = /[!@#$%^&*()\-_]+/
const big = BIG_LETTER_REGEX.test(passphrase) ? 1 : 0
const small = SMALL_LETTER_REGEX.test(passphrase) ? 1 : 0
const number = NUMBER_REGEX.test(passphrase) ? 1 : 0
const punct = PUNCT_REGEX.test(passphrase) ? 1 : 0
const base = big * 26 + small * 26 + number * 10 + punct * 12
const compl = Math.pow(base, passphrase.length)
const thresh = Math.pow(11, 14)
// console.log({ big, small, number, punct, base, compl, thresh });
return compl < thresh;
}
export class Keys {
subtle: any
@ -33,10 +53,6 @@ export class Keys {
this.subtle = cryptoSubtle
}
public isValidPassphase(passphrase: string): boolean {
return ASCII_REGEX.test(passphrase)
}
public async generatePassKey(pubkey: string, passphrase: string): Promise<{ passkey: Buffer; pwh: string }> {
const salt = Buffer.from(pubkey, 'hex')
@ -45,7 +61,7 @@ export class Keys {
// We could use string.normalize() to make sure all JS implementations
// are compatible, but since we're looking to make this thing a standard
// then the simplest way is to exclude unicode and only work with ASCII
if (!this.isValidPassphase(passphrase)) throw new Error('Password must be 4+ ASCII chars')
if (!isValidPassphase(passphrase)) throw new Error('Password must be 4+ ASCII chars')
return new Promise((ok, fail) => {
// NOTE: we should use Argon2 or scrypt later, for now

View File

@ -24,7 +24,7 @@ export const StyledAppLogo = styled((props) => (
<AppLogo />
</Box>
))({
background: '#00000054',
background: '#0d0d0d',
padding: '0.75rem',
borderRadius: '16px',
display: 'grid',

View File

@ -8,6 +8,7 @@ import { ModalConfirmConnect } from '@/components/Modal/ModalConfirmConnect/Moda
import { useModalSearchParams } from '@/hooks/useModalSearchParams'
import { MODAL_PARAMS_KEYS } from '@/types/modal'
import { useState } from 'react'
import { getReferrerAppUrl } from '@/utils/helpers/helpers'
const CreatePage = () => {
const notify = useEnqueueSnackbar()
@ -32,13 +33,7 @@ const CreatePage = () => {
try {
const key: any = await swicCall('generateKey', name)
let appUrl = ''
if (window.document.referrer) {
try {
const u = new URL(window.document.referrer)
appUrl = u.origin
} catch {}
}
const appUrl = getReferrerAppUrl();
console.log('Created', key.npub, 'app', appUrl)
setCreated(true)

View File

@ -1,5 +1,5 @@
import { nip19 } from 'nostr-tools'
import { ACTIONS, ACTION_TYPE, NIP46_RELAYS } from '../consts'
import { ACTIONS, ACTION_TYPE, DOMAIN, NIP46_RELAYS } from '../consts'
import { DbPending, DbPerm } from '@/modules/db'
import { MetaEvent } from '@/types/meta-event'
@ -105,6 +105,17 @@ export const getDomain = (url: string) => {
}
}
export const getReferrerAppUrl = () => {
console.log('referrer', window.document.referrer)
if (!window.document.referrer) return ''
try {
const u = new URL(window.document.referrer.toLocaleLowerCase())
if (u.hostname != DOMAIN && !u.hostname.endsWith("."+DOMAIN))
return u.origin
} catch {}
return ''
}
export const getAppIconTitle = (name: string | undefined, appNpub: string) => {
return name ? name[0].toLocaleUpperCase() : appNpub.substring(4, 7)
}