The Merge 🌊

This commit is contained in:
3lLobo
2022-09-18 19:14:51 +02:00
20 changed files with 651 additions and 163 deletions

View File

@@ -3,4 +3,4 @@
"singleQuote": true,
"tabWidth": 2,
"endOfLine": "auto"
}
}

View File

@@ -1,10 +1,9 @@
# Zero-Knowledge 2-Factor Authentication 🗝️ (zk-2FA)
The goal of this project is to provide 2FA for EVM compatible blockchains.
We follow a parallel approach for a twofold Authentication solution. The first implements the popular and broadly adopted TOTP 2FA with a trusted validator. The second solution implements a password-generator based zk proof, which is validated onChain providing a zero-trust security level.
We follow a parallel approach for a twofold Authentication solution. The first implements the popular and broadly adopted TOTP 2FA with a trusted validator. The second solution implements a password-generator based zk proof, which is validated onChain providing a zero-trust security level.
Further we provide a dapp to facilitate user-interaction with our smrt-contracts. All dapp interactions can likewise be performed manually per console.
Further we provide a dapp to facilitate user-interaction with our smrt-contracts. All dapp interactions can likewise be performed manually per console.
## TOTP 2FA
@@ -15,7 +14,6 @@ A picturesque flow-chart of our TOTP 2FA solution:
**Artworq in the making**
## Contribute
Feedback and contributions are always welcome 🤗

View File

@@ -1,7 +1,7 @@
{
"name": "hardhat-project",
"scripts": {
"prettier": "prettier --write . --config ../.prettierrc"
"prettier": "prettier --write ../ --config ../.prettierrc"
},
"devDependencies": {
"@nomicfoundation/hardhat-chai-matchers": "^1.0.3",

View File

@@ -0,0 +1,69 @@
import { useTheme } from 'next-themes'
import Image from 'next/image'
interface CardChoiceProps {
authType: string
setAuthType: (arg: string) => void
}
const CardChoice = (props: CardChoiceProps) => {
const { theme } = useTheme()
return (
{
totp: (
<button
className="w-full max-w-sm bg-white rounded-lg border border-gray-200 shadow-md p-6 lg:p-8 dark:bg-gray-800 dark:border-gray-700 hover:scale-110 transition ease-in-out duration-500 grid grid-cols-12 gap-4"
onClick={() => props.setAuthType('totp')}
>
<div className="flex h-full col-span-3 lg:col-span-12 items-center justify-center">
<Image
src="/TOTP.svg"
alt="TOTP"
width={150}
height={150}
className={`filter-${theme}`}
/>
</div>
<div className="col-span-9 lg:col-span-12">
<div className="text-xl lg:text-2xl font-medium lg:mt-4 text-start lg:text-center text-gray-900 dark:text-white">
Set up TOTP 2FA
</div>
<div className="text-sm lg:text-base text-start lg:text-center mt-2 text-gray-500 dark:text-gray-400">
This option will generate a set of Time dependent
One-Time-Passwords that you can verify by using apps like Google
Authenticator.
</div>
</div>
</button>
),
zk: (
<button
className="w-full max-w-sm bg-white rounded-lg border border-gray-200 shadow-md p-6 lg:p-8 dark:bg-gray-800 dark:border-gray-700 hover:scale-110 transition ease-in-out duration-500 grid grid-cols-12 gap-4"
onClick={() => props.setAuthType('zk')}
>
<div className="flex h-full col-span-3 lg:col-span-12 items-center justify-center">
<Image
src="/ZK.svg"
alt="ZK"
width={150}
height={150}
className={`filter-${theme}`}
/>
</div>
<div className="col-span-9 lg:col-span-12">
<div className="text-xl lg:text-2xl font-medium lg:mt-4 text-start lg:text-center text-gray-900 dark:text-white">
Set up zk-Password 2FA
</div>
<div className="text-sm lg:text-base text-start lg:text-center mt-2 text-gray-500 dark:text-gray-400">
Set up your own password, that will be encripted and stored
on-chain. To verify it a zk-Proof will be generated in the
frontend and verified on-chain.
</div>
</div>
</button>
),
}[props.authType] || <></>
)
}
export default CardChoice

View File

@@ -0,0 +1,96 @@
/* This example requires Tailwind CSS v2.0+ */
import { Fragment } from 'react'
import { Menu, Transition } from '@headlessui/react'
import {
ArrowTopRightOnSquareIcon,
ArrowRightOnRectangleIcon,
} from '@heroicons/react/20/solid'
import { shortenAddress, useEthers, useLookupAddress } from '@usedapp/core'
function classNames(...classes: any) {
return classes.filter(Boolean).join(' ')
}
interface DropdownProps {
account: string
}
const DropdownAccount = (props: DropdownProps) => {
const { deactivate } = useEthers()
const { ens } = useLookupAddress(props.account)
return (
<Menu as="div" className="relative inline-block text-left">
<div>
<Menu.Button
className="font-medium rounded-lg text-sm px-3 py-1.5
text-gray-900 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-200 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700 dark:focus:ring-gray-700"
>
{ens ?? shortenAddress(props.account)}
</Menu.Button>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items
className="absolute right-0 z-10 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md
bg-white dark:bg-gray-800
text-gray-900 dark:text-white
shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
>
<div className="py-1">
<Menu.Item>
{({ active }) => (
<a
href="#"
className={classNames(
active
? 'bg-gray-100 text-gray-900 dark:bg-gray-700 dark:text-gray-100'
: 'text-gray-700 dark:text-gray-300',
'group flex items-center px-4 py-2 text-sm'
)}
>
<ArrowTopRightOnSquareIcon
className="mr-3 h-5 w-5 text-gray-400
dark:text-gray-300 group-hover:text-gray-500 dark:group-hover:text-gray-100"
aria-hidden="true"
/>
See on Etherscan
</a>
)}
</Menu.Item>
{/* <Menu.Item>
{({ active }) => (
<a
className={classNames(
active
? "bg-gray-100 text-gray-900 dark:bg-gray-700 dark:text-gray-100"
: "text-gray-700 dark:text-gray-300",
"group flex items-center px-4 py-2 text-sm"
)}
onClick={() => deactivate()}
>
<ArrowRightOnRectangleIcon
className="mr-3 h-5 w-5 text-gray-400
dark:text-gray-300 group-hover:text-gray-500 dark:group-hover:text-gray-100"
aria-hidden="true"
/>
Logout
</a>
)}
</Menu.Item> */}
</div>
</Menu.Items>
</Transition>
</Menu>
)
}
export default DropdownAccount

View File

@@ -0,0 +1,16 @@
import { Navbar } from './'
import Head from 'next/head'
export default function Layout(props: any) {
return (
<>
<Head>
<title>zkAuth</title>
<meta name="description" content="zero-knowledge Authentification" />
<link rel="icon" href="" />
</Head>
<Navbar />
<main>{props.children}</main>
</>
)
}

View File

@@ -6,12 +6,12 @@ import {
CheckIcon,
} from '@heroicons/react/24/outline'
interface ModalVerifyProps {
interface ModalVerifyTotpProps {
verified: boolean
verifyCode: any
}
const ModalVerify = (props: ModalVerifyProps) => {
const ModalVerifyTotp = (props: ModalVerifyTotpProps) => {
const [open, setOpen] = useState(false)
const onSubmit = (e: React.UIEvent<HTMLButtonElement>) => {
e.preventDefault()
@@ -135,4 +135,4 @@ const ModalVerify = (props: ModalVerifyProps) => {
)
}
export default ModalVerify
export default ModalVerifyTotp

View File

@@ -0,0 +1,123 @@
import { ArrowUturnLeftIcon } from '@heroicons/react/24/outline'
import { useEthers } from '@usedapp/core'
import { useState, useRef, useEffect } from 'react'
import { usePinInput, PinInputActions } from 'react-pin-input-hook'
import { ModalVerifyTotp, QrCodeAuth } from './'
var jsotp = require('jsotp')
var base32 = require('thirty-two')
interface TotpSetupProps {
setAuthType: (arg: string) => void
}
const TotpSetup = (props: TotpSetupProps) => {
const { account, library: provider } = useEthers()
// Secret State Management
const [secret, setSecret] = useState('')
const [blur, setBlur] = useState('opacity-50 blur-sm')
const loadSecret = async (e: React.FormEvent<HTMLButtonElement>) => {
e.preventDefault()
if (provider != undefined) {
const signer = provider.getSigner()
const signature = await signer.signMessage('zkAuth') // TODO: Random Input? ZK?
const secretEncoded = base32
.encode(signature)
.toString()
.replace(/=/g, '')
setSecret(secretEncoded)
setBlur('')
}
}
// PIN state management
const [verified, setVerified] = useState(false)
const [pin, setPin] = useState(['', '', '', '', '', ''])
const [error, setError] = useState(false)
const actionRef = useRef<PinInputActions>(null)
const { fields } = usePinInput({
values: pin,
onChange: setPin,
error,
actionRef,
placeholder: '•',
})
function verifyCode(e: React.FormEvent<HTMLButtonElement>) {
e.preventDefault()
// Check if there is at least one empty field. If there is, the input is considered empty.
if (pin.includes('')) {
// Setting the error.
setError(true)
// We set the focus on the first empty field if `error: true` was passed as a parameter in `options`.
actionRef.current?.focus()
}
const verifier = jsotp.TOTP(secret)
if (verifier.verify(pin.join(''))) {
setVerified(true)
} else {
setVerified(false)
}
}
if (!account) return null
return (
<div className="relative w-full max-w-sm bg-white rounded-lg border border-gray-200 shadow-md p-6 md:p-8 dark:bg-gray-800 dark:border-gray-700">
<div className="absolute top-0 right-0 hidden pt-4 pr-4 sm:block">
<button
type="button"
className="absolute top-3 right-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-800 dark:hover:text-white"
onClick={() => props.setAuthType('')}
>
<span className="sr-only">Back</span>
<ArrowUturnLeftIcon className="h-5 w-5" aria-hidden="true" />
</button>
</div>
<form className="space-y-6" action="#">
<div className="text-2xl font-medium flex justify-center text-gray-900 dark:text-white">
Set up your TOTP
</div>
<div className="relative flex justify-center items-center">
<div className={blur}>
<QrCodeAuth account={account} secret={secret} />
</div>
{secret == '' ? (
<button
className="absolute w-1/2 text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
onClick={(e) => loadSecret(e)}
>
Set up 2FA
</button>
) : (
<></>
)}
</div>
<div>
<label
htmlFor="password"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
>
Authenticator Code
</label>
<div className="pin-input flex flex-row justify-center">
{fields.map((propsField, index) => (
<input
key={index}
className="pin-input__field w-10 p-2.5 mx-1 text-2xl rounded-lg text-center font-mono
bg-gray-50 border border-gray-300 text-gray-900 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"
{...propsField}
/>
))}
</div>
</div>
<ModalVerifyTotp verified={verified} verifyCode={verifyCode} />
</form>
</div>
)
}
export default TotpSetup

View File

@@ -0,0 +1,112 @@
import {
ArrowUturnLeftIcon,
CheckIcon,
XMarkIcon,
} from '@heroicons/react/24/outline'
import { useEthers } from '@usedapp/core'
import { useState } from 'react'
import dynamic from 'next/dynamic'
const PasswordChecklist = dynamic(import('react-password-checklist'), {
ssr: false,
})
interface ZkSetupProps {
setAuthType: (arg: string) => void
}
const ZkPasswordSetup = (props: ZkSetupProps) => {
const { account, library: provider } = useEthers()
// PIN state management
const [password, setPassword] = useState('')
const [passwordAgain, setPasswordAgain] = useState('')
const [isValid, setIsValid] = useState(false)
// Set password to blockchain
const onSubmit = (e: React.FormEvent<HTMLButtonElement>) => {
e.preventDefault()
}
if (!account) return null
return (
<div className="relative w-full max-w-sm bg-white rounded-lg border border-gray-200 shadow-md p-6 md:p-8 dark:bg-gray-800 dark:border-gray-700">
<div className="absolute top-0 right-0 hidden pt-4 pr-4 sm:block">
<button
type="button"
className="absolute top-3 right-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-800 dark:hover:text-white"
onClick={() => props.setAuthType('')}
>
<span className="sr-only">Back</span>
<ArrowUturnLeftIcon className="h-5 w-5" aria-hidden="true" />
</button>
</div>
<form className="space-y-6" action="#">
<h5 className="text-2xl font-medium flex justify-center text-gray-900 dark:text-white">
Set up zk-Password
</h5>
<div className="relative flex justify-center items-center"></div>
<div>
<label
htmlFor="password"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
>
Password
</label>
<input
id="password"
name="password"
type="password"
className="w-full p-2.5 rounded-lg
bg-gray-50 border border-gray-300 text-gray-900 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"
placeholder="••••••••"
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<div>
<label
htmlFor="password"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
>
Repeat Password
</label>
<input
id="password"
name="password"
type="password"
className="w-full p-2.5 rounded-lg
bg-gray-50 border border-gray-300 text-gray-900 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"
placeholder="••••••••"
onChange={(e) => setPasswordAgain(e.target.value)}
/>
</div>
<div>
<PasswordChecklist
rules={['minLength', 'specialChar', 'number', 'capital', 'match']}
minLength={8}
value={password}
valueAgain={passwordAgain}
iconComponents={{
ValidIcon: <CheckIcon className="h-5 w-5 mr-1 text-green-600" />,
InvalidIcon: <XMarkIcon className="h-5 w-5 mr-1 text-red-600" />,
}}
className="flex flex-col text-sm align-middle"
onChange={(isValid) => {
setIsValid(isValid)
}}
/>
</div>
<button
type="button"
className="w-full text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800
disabled:opacity-25"
onClick={(e) => onSubmit(e)}
disabled={!isValid}
>
Set password
</button>
</form>
</div>
)
}
export default ZkPasswordSetup

View File

@@ -3,6 +3,7 @@ import { useEthers } from '@usedapp/core'
import Web3Modal from 'web3modal'
import WalletConnectProvider from '@walletconnect/web3-provider'
import { useTheme } from 'next-themes'
import { ethers } from 'ethers'
const ConnectWalletButton = () => {
const { theme } = useTheme()
@@ -28,6 +29,7 @@ const ConnectWalletButton = () => {
const [loaded, setLoaded] = useState(false)
useEffect(() => setLoaded(true), [])
// Set up Web3modal
const [web3Modal, setWeb3Modal] = useState<Web3Modal | undefined>(undefined)
useEffect(() => {
const providerOptions = {
@@ -47,22 +49,16 @@ const ConnectWalletButton = () => {
},
}
const newWeb3Modal = new Web3Modal({
providerOptions,
cacheProvider: false,
})
setWeb3Modal(newWeb3Modal)
}, [])
if (loaded) {
const newWeb3Modal = new Web3Modal({
providerOptions,
cacheProvider: false,
theme: theme,
})
// const connect = async () => {
// try {
// const provider = await web3Modal?.connect()
// await activate(provider)
// setActivateError("")
// } catch (error: any) {
// setActivateError(error.message)
// }
// }
setWeb3Modal(newWeb3Modal)
}
}, [loaded, theme])
const connect = useCallback(async () => {
try {
@@ -74,12 +70,22 @@ const ConnectWalletButton = () => {
}
}, [web3Modal, activate])
// useEffect(() => {
// if (web3Modal && web3Modal.cachedProvider) {
// console.log(web3Modal.cachedProvider)
// connect()
// }
// }, [connect, web3Modal])
// Set up provider if already connected
useEffect(() => {
const { ethereum } = window
const checkMetaMaskConnected = async () => {
var provider = new ethers.providers.Web3Provider(ethereum)
const accounts = await provider.listAccounts()
const connected = accounts.length > 0
console.log('CONNECTED', connected)
if (connected) {
activate(provider)
}
}
if (ethereum) {
checkMetaMaskConnected()
}
})
if (!loaded) return null

11
dapp/components/index.ts Normal file
View File

@@ -0,0 +1,11 @@
export { default as ConnectWalletButton } from './ConnectWalletButton'
export { default as DropdownAccount } from './DropdownAccount'
export { default as LogInBox } from './LogInBox'
export { default as ModalVerifyTotp } from './ModalVerifyTotp'
export { default as Navbar } from './Navbar'
export { default as QrCodeAuth } from './QrCodeAuth'
export { default as ToggleColorMode } from './ToggleColorMode'
export { default as TotpSetup } from './TotpSetup'
export { default as ZkPasswordSetup } from './ZkPasswordSetup'
export { default as CardChoice } from './CardChoice'
export { default as Layout } from './Layout'

View File

@@ -1,123 +1,72 @@
import { useEthers, shortenAddress, useLookupAddress } from '@usedapp/core'
import { ethers } from 'ethers'
import { useState, useRef, useEffect } from 'react'
import { usePinInput, PinInputActions } from 'react-pin-input-hook'
import ConnectWalletButton from './connectWalletButton'
import ModalVerify from './modalVerify'
import QrCodeAuth from './qrCodeAuth'
import { useEthers } from '@usedapp/core'
import { useState } from 'react'
import { motion } from 'framer-motion'
var jsotp = require('jsotp')
var base32 = require('thirty-two')
import { ConnectWalletButton, TotpSetup, ZkPasswordSetup, CardChoice } from './'
const LogInBox = () => {
const { account, library: provider } = useEthers()
const { ens } = useLookupAddress(account)
// Secret State Management
const [secret, setSecret] = useState('')
const [blur, setBlur] = useState('opacity-50 blur-sm')
const loadSecret = async (e: React.FormEvent<HTMLButtonElement>) => {
e.preventDefault()
if (provider != undefined) {
const signer = provider.getSigner()
const signature = await signer.signMessage('zkAuth') // TODO: Random Input? ZK?
const secretEncoded = base32
.encode(signature)
.toString()
.replace(/=/g, '')
setSecret(secretEncoded)
setBlur('')
}
}
// PIN state management
const [verified, setVerified] = useState(false)
const [pin, setPin] = useState(['', '', '', '', '', ''])
const [error, setError] = useState(false)
const actionRef = useRef<PinInputActions>(null)
const { fields } = usePinInput({
values: pin,
onChange: setPin,
error,
actionRef,
placeholder: '•',
})
function verifyCode(e: React.FormEvent<HTMLButtonElement>) {
e.preventDefault()
// Check if there is at least one empty field. If there is, the input is considered empty.
if (pin.includes('')) {
// Setting the error.
setError(true)
// We set the focus on the first empty field if `error: true` was passed as a parameter in `options`.
actionRef.current?.focus()
}
const verifier = jsotp.TOTP(secret)
if (verifier.verify(pin.join(''))) {
setVerified(true)
} else {
setVerified(false)
}
}
const [authType, setAuthType] = useState('')
return (
<>
{!account ? (
<div className="flex justify-center">
<motion.div
key="connect"
className="flex justify-center"
initial={{ y: 10, opacity: 0, scale: 0.9 }}
animate={{ y: 0, opacity: 1, scale: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 1, ease: 'easeOut' }}
>
<ConnectWalletButton />
</div>
</motion.div>
) : (
<div className="w-full max-w-sm bg-white rounded-lg border border-gray-200 shadow-md sm:p-6 md:p-8 dark:bg-gray-800 dark:border-gray-700">
<form className="space-y-6" action="#">
<h5 className="text-xl font-medium text-gray-900 dark:text-white">
Sign in to our platform
</h5>
<label
htmlFor="account"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
{
totp: (
<motion.div
key="settotp"
initial={{ y: 10, opacity: 0, scale: 0.9 }}
animate={{ y: 0, opacity: 1, scale: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.6, ease: 'easeOut' }}
>
Your account
</label>
<span>{ens ?? shortenAddress(account)}</span>
<div className="relative flex justify-center items-center">
<div className={blur}>
<QrCodeAuth account={account} secret={secret} />
</div>
{secret == '' ? (
<button
className="absolute w-1/2 text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
onClick={(e) => loadSecret(e)}
>
Set up 2FA
</button>
) : (
<></>
)}
</div>
<div>
<label
htmlFor="password"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
>
Authenticator Code
</label>
<div className="pin-input flex flex-row justify-center">
{fields.map((propsField, index) => (
<input
key={index}
className="pin-input__field w-10 p-2.5 mx-1 text-2xl rounded-lg text-center font-mono
bg-gray-50 border border-gray-300 text-gray-900 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"
{...propsField}
/>
))}
</div>
</div>
<ModalVerify verified={verified} verifyCode={verifyCode} />
</form>
</div>
<TotpSetup setAuthType={setAuthType} />
</motion.div>
),
zk: (
<motion.div
key="setzk"
initial={{ y: 10, opacity: 0, scale: 0.9 }}
animate={{ y: 0, opacity: 1, scale: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.6, ease: 'easeOut' }}
>
<ZkPasswordSetup setAuthType={setAuthType} />
</motion.div>
),
}[authType] || (
<div className="flex flex-col space-y-10 lg:space-y-0 lg:flex-row lg:space-x-20">
<motion.div
key="totp"
initial={{ opacity: 0, scale: 0.8 }}
animate={{ y: 0, opacity: 1, scale: 1 }}
exit={{ y: -100, opacity: 0, transition: { duration: 1 } }}
transition={{ duration: 0.6, ease: 'easeOut' }}
>
<CardChoice authType="totp" setAuthType={setAuthType} />
</motion.div>
<motion.div
key="zk"
initial={{ opacity: 0, scale: 0.8 }}
animate={{ y: 0, opacity: 1, scale: 1 }}
exit={{ y: -100, opacity: 0, transition: { duration: 1 } }}
transition={{ duration: 0.6, delay: 0.3, ease: 'easeOut' }}
>
<CardChoice authType="zk" setAuthType={setAuthType} />
</motion.div>
</div>
)
)}
</>
)

View File

@@ -1,18 +1,20 @@
import ToggleColorMode from './toggleColorMode'
import { useEthers } from '@usedapp/core'
import { DropdownAccount, ToggleColorMode } from './'
const Navbar = () => {
const { account } = useEthers()
return (
<nav
className="
dark:bg-gray-900 bg-white
border-gray-200
px-2 sm:px-4 py-2.5 rounded"
>
<nav className="px-4 py-4">
<div className="container flex flex-wrap justify-between items-center mx-auto max-w-6xl">
<span className="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">
zkAuth
</span>
<ToggleColorMode />
<div className="flex flex-row space-x-4 align-middle">
{account ? <DropdownAccount account={account} /> : <></>}
<ToggleColorMode />
</div>
</div>
</nav>
)

View File

@@ -15,8 +15,9 @@ const ToggleColorMode = () => {
<div className="flex flex-0 color-snow hover:scale-110 transition ease-in-out duration-500">
<button
className="
dark:bg-gray-600 bg-gray-300
dark:border-gray-300 border-gray-600
dark:bg-gray-800 bg-white
dark:hover:bg-gray-700 hover:bg-gray-100
dark:border-gray-600 border-gray-300
rounded-full border p-2"
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
>

View File

@@ -15,6 +15,7 @@
"@usedapp/core": "^1.1.5",
"@walletconnect/web3-provider": "^1.8.0",
"ethers": "^5.7.0",
"framer-motion": "^7.3.5",
"fs": "^0.0.1-security",
"jsotp": "^1.0.4",
"next": "12.3.0",
@@ -22,6 +23,7 @@
"next-themes": "^0.2.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-password-checklist": "^1.4.1",
"react-pin-input-hook": "^1.0.8",
"thirty-two": "^1.0.2",
"web3modal": "^1.9.9"

View File

@@ -1,5 +1,6 @@
import '../styles/globals.css'
import type { AppProps } from 'next/app'
import { Layout } from '../components'
import { ThemeProvider } from 'next-themes'
import { DAppProvider } from '@usedapp/core'
@@ -9,9 +10,11 @@ const config = {
function MyApp({ Component, pageProps }: AppProps) {
return (
<ThemeProvider attribute="class">
<ThemeProvider attribute="class" enableSystem={false} defaultTheme="dark">
<DAppProvider config={config}>
<Component {...pageProps} />
<Layout>
<Component {...pageProps} />
</Layout>
</DAppProvider>
</ThemeProvider>
)

View File

@@ -1,20 +1,10 @@
import type { NextPage } from 'next'
import Head from 'next/head'
import LogInBox from '../components/logInBox'
import Navbar from '../components/navbar'
import { LogInBox } from '../components'
const Home: NextPage = () => {
return (
<div>
<Head>
<title>zkAuth</title>
<meta name="description" content="zero-knowledge Authentification" />
<link rel="icon" href="" />
</Head>
<Navbar />
<div className="w-screen h-screen flex flex-col justify-center items-center">
<div className="h-[calc(100vh-100px)] flex justify-center items-center">
<LogInBox />
</div>
</div>

50
dapp/public/TOTP.svg Normal file
View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="svg"
width="338.99203"
height="386.78464"
viewBox="0 0 338.99203 386.78464"
version="1.1"
sodipodi:docname="TOTP.svg"
inkscape:version="1.2 (dc2aeda, 2022-05-15)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2291" />
<sodipodi:namedview
id="namedview2289"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="false"
inkscape:zoom="0.45617322"
inkscape:cx="-288.26769"
inkscape:cy="116.18393"
inkscape:window-width="1355"
inkscape:window-height="702"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="svgg" />
<g
id="svgg"
transform="translate(-34.415837,-10.279271)">
<path
id="path0"
d="M 171.942,93.076 C 65.669,103.963 3.204,216.677 50.355,312.47 c 42.874,87.102 156.101,111.948 232.051,50.92 47.068,-37.821 67.03067,-102.62298 51.02067,-161.19098 -26.88888,0.40353 -0.54179,0.32655 -27.39866,0.27033 2.808,8.211 5.66399,20.03265 6.89799,30.50465 l 0.296,2.518 h -13.03 c -14.578,0 -15.999,0.219 -18.657,2.877 -3.851,3.851 -3.714,10.25 0.299,13.999 2.341,2.186 3.277,2.308 17.708,2.308 h 13.629 l -0.273,2.278 c -7.47,62.362 -52.349,106.975 -114.697,114.018 l -2.518,0.284 -0.011,-11.887 c -0.006,-6.538 -0.202,-12.707 -0.435,-13.71 -1.963,-8.458 -13.841,-10.192 -17.889,-2.613 -1.042,1.949 -1.095,2.596 -1.238,14.868 l -0.148,12.829 h -1.713 c -2.45,0 -10.608,-1.329 -16.253,-2.648 -47.149,-11.018 -84.057,-48.259 -94.881,-95.738 -0.671,-2.942 -2.675,-15.712 -2.681,-17.081 -0.002,-0.465 2.862,-0.6 12.727,-0.6 11.762,0 12.871,-0.072 14.6,-0.955 5.759,-2.938 6.841,-10.933 2.09,-15.446 -2.741,-2.604 -3.849,-2.783 -17.174,-2.783 -10.373,0 -12.245,-0.107 -12.245,-0.695 0,-27.627 20.036,-66.319 45.138,-87.163 19.043,-15.815 50.794,-29.027 70.089,-29.166 0.198,-0.002 0.361,5.663 0.363,12.587 0.003,13.725 0.225,15.321 2.493,17.903 3.811,4.341 9.978,4.54 14.064,0.455 2.821,-2.822 3.104,-4.559 3.104,-19.047 v -12.033 l 3.238,0.318 c 9.195,0.903 19.873,3.332 29.732,6.761 3.423,1.191 6.363,2.166 6.532,2.166 0.168,0 0.307,-6.123 0.307,-13.607 v -13.607 l -5.156,-1.503 c -17.992,-5.247 -41.202,-7.546 -58.394,-5.785 m -35.425,90.339 c -3.546,1.305 -6.061,4.746 -6.061,8.29 0,2.423 -0.145,2.209 17.631,26.196 l 14.865,20.058 -0.465,2.004 c -0.623,2.678 -0.178,10.135 0.756,12.685 l 0.75,2.048 -20.605,30.964 c -20.236,30.408 -20.606,31.007 -20.606,33.347 0,5.157 4.168,8.901 9.423,8.465 3.293,-0.273 0.834,2.284 28.837,-29.99 l 24.241,-27.938 2.202,-0.007 c 15.394,-0.049 27.246,-15.724 23.312,-30.83 -2.95,-11.324 -12.376,-18.541 -24.239,-18.557 l -4.089,-0.006 -18.693,-17.291 c -23.822,-22.036 -22.59,-21.158 -27.259,-19.438 m 57.387,46.568 c 15.586,7.671 10.24,31.141 -7.093,31.141 -16.525,0 -22.626,-21.781 -8.54,-30.486 4.463,-2.759 10.819,-3.025 15.633,-0.655 m -10.606,5.095 c -4.01,1.155 -7.3,6.682 -6.596,11.083 1.979,12.379 20.067,11.242 20.126,-1.266 0.035,-7.296 -6.307,-11.898 -13.53,-9.817"
stroke="none"
fill="#000000"
fill-rule="evenodd"
sodipodi:nodetypes="cccccccsscscccccccccsccssccsscccccscscssccccsccccccscccsccccccscccccc" />
<path
id="path2418"
style="fill:#000000;fill-rule:evenodd;stroke:none;stroke-width:0.960696"
d="m 310.51758,10.279297 c -3.51692,0.0038 -7.07688,0.433269 -10.63867,1.332031 -25.07513,6.326389 -43.45118,30.697975 -43.45118,60.390625 v 4.708985 h 9.60743 9.60742 l 0.2539,-5.427735 c 1.47975,-31.625626 31.26686,-49.981063 53.32813,-32.863281 10.07247,7.816127 15.63666,19.002742 16.21875,32.609375 l 0.24219,5.681641 h 9.60937 9.60742 v -3.257813 c 0,-34.902772 -25.74704,-63.205044 -54.38476,-63.173828 z m 0.13672,76.876953 c -30.60523,0.04317 -61.19445,0.446787 -61.79492,1.167969 -0.96956,1.164418 -1.36177,97.868021 -0.40626,100.013671 0.68465,1.53712 123.73919,1.53712 124.42383,0 0.89592,-2.00983 0.57246,-99.203114 -0.33398,-100.187499 -0.66142,-0.719077 -31.28345,-1.037307 -61.88867,-0.994141 z m 0.4414,20.23047 c 8.41415,7.7e-4 15.2347,6.82218 15.23438,15.23633 -0.0747,5.5366 -3.14733,10.59685 -8.02539,13.21679 l 3.98047,14.65039 c 2.5203,9.27534 4.68988,17.38454 4.82226,18.02149 l 0.24219,1.15625 h -16.44922 c -12.98816,0 -16.40954,-0.13289 -16.25,-0.63086 0.11046,-0.34743 2.30835,-8.44058 4.88477,-17.98438 l 4.13672,-15.32226 c -4.76209,-2.6563 -7.74153,-7.65505 -7.8125,-13.10742 -3.2e-4,-8.41491 6.82141,-15.23665 15.23632,-15.23633 z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

45
dapp/public/ZK.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@@ -1,3 +1,18 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
body {
@apply bg-gradient-to-b from-white to-gray-200 dark:from-gray-900 dark:via-black dark:to-black h-screen;
}
/* ... */
}
.filter-dark {
filter: invert(80%);
}
.filter-light {
filter: invert(20%);
}