Skip to content

Commit

Permalink
feat: migrate auth process to farcaster
Browse files Browse the repository at this point in the history
  • Loading branch information
juliopavila committed Jul 5, 2024
1 parent dd2bd5b commit e5292ff
Show file tree
Hide file tree
Showing 17 changed files with 1,329 additions and 227 deletions.
4 changes: 4 additions & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
"dependencies": {
"@emotion/babel-plugin": "^11.11.0",
"@emotion/react": "^11.11.4",
"@farcaster/auth-client": "^0.1.1",
"@farcaster/auth-kit": "^0.3.1",
"@phosphor-icons/react": "^2.1.4",
"@svgr/rollup": "^8.1.0",
"axios": "^1.6.8",
Expand All @@ -28,6 +30,8 @@
"react-markdown": "^9.0.1",
"react-router-dom": "^6.22.3",
"react-syntax-highlighter": "^15.5.0",
"viem": "^2.17.0",
"vite-plugin-node-polyfills": "^0.22.0",
"vite-plugin-top-level-await": "^1.4.1",
"vite-tsconfig-paths": "^4.3.2"
},
Expand Down
69 changes: 69 additions & 0 deletions packages/client/src/components/FarcasterModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { useVoteManagementContext } from '@/context/voteManagement'
import useLocalStorage from '@/hooks/generic/useLocalStorage'
import { AuthClientError, QRCode, StatusAPIResponse } from '@farcaster/auth-kit'
import { DeviceMobileCamera } from '@phosphor-icons/react'
import React, { useEffect } from 'react'
import { Link } from 'react-router-dom'

interface FarcasterModalProps {
url?: string
data: StatusAPIResponse | undefined
error: AuthClientError | undefined
onClose: () => void
}

const FarcasterModal: React.FC<FarcasterModalProps> = ({ url, data, error, onClose }) => {
const { setUser } = useVoteManagementContext()
const [farcasterAuth, setFarcasterUser] = useLocalStorage<StatusAPIResponse | null>('farcasterAuth', null)

useEffect(() => {
if (data && data.state === 'completed' && !farcasterAuth) {
setUser(data)
setFarcasterUser(data)
onClose()
}
}, [data, setFarcasterUser])

return (
<div className='mt-4 space-y-10'>
<div className='flex flex-col items-center justify-center'>
<div className='flex flex-col space-y-2'>
<h2 className='text-xl font-bold text-slate-600'>Verify your account with farcaster</h2>
{!error && (
<>
<p className='text-sm'>Scan with your phone's camera to continue.</p>
<Link to='https://warpcast.com/~/signup' target='_blank'>
<p className='text-sm text-lime-600 underline'>Need to create an account?</p>
</Link>
</>
)}
</div>
{!error && (
<>
{url && (
<div className='my-8'>
<QRCode uri={url} size={260} logoSize={40} />
</div>
)}
{url && (
<div className='flex items-center space-x-2'>
<Link to={url} target='_blank' className='flex items-center space-x-2'>
<DeviceMobileCamera size={24} className='text-lime-600' />
<p className='text-base text-lime-600 underline'>I'm using my phone</p>
</Link>
</div>
)}
</>
)}
{error && (
<p className='mt-4 text-center'>
Your polling request has timed out after 5 minutes. This may occur if you haven't scanned the QR code using Farcaster, or if
there was an issue during the process. Please ensure you have completed the QR scan and try again.
</p>
)}
</div>
</div>
)
}

export default FarcasterModal
7 changes: 5 additions & 2 deletions packages/client/src/components/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ interface ModalProps {
show: boolean
onClose: () => void
children: React.ReactNode
className?: string | undefined
}

const Modal: FC<ModalProps> = ({ show, onClose, children }) => {
const Modal: FC<ModalProps> = ({ show, onClose, children, className }) => {
const modalRef = useRef<HTMLDivElement>(null)

const closeModal = (e: React.MouseEvent<HTMLDivElement>) => {
Expand All @@ -34,7 +35,9 @@ const Modal: FC<ModalProps> = ({ show, onClose, children }) => {

return (
<div className='fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-20 p-4' onClick={closeModal} ref={modalRef}>
<div className='relative max-h-[672px] w-full max-w-screen-md overflow-auto rounded-[24px] border-2 border-slate-600/20 bg-white p-6 shadow-2xl md:p-12'>
<div
className={`relative max-h-[672px] max-w-screen-md overflow-auto rounded-[24px] border-2 border-slate-600/20 bg-white p-6 shadow-2xl md:p-12 ${className ? className : 'w-full'}`}
>
{children}
<button className='absolute right-4 top-4 md:right-8 md:top-8' onClick={onClose}>
<div className='close-icon' />
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/components/NavMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ const NavMenu: React.FC<NavMenuProps> = () => {
onClick={toggleMenu}
className='flex items-center justify-between space-x-1 rounded-lg border-2 bg-white/60 px-2 py-1 duration-300 ease-in-out hover:bg-white'
>
<img src={user.avatar} className='h-[20px] w-[20px] rounded-full' />
<img src={user.pfpUrl} className='h-[20px] w-[20px] rounded-full' />
<p className='text-xs font-bold'>@{user.username}</p>
<CaretRight className={isOpen ? '-rotate-90 transition-transform duration-200' : ''} />
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@ import { createGenericContext } from '@/utils/create-generic-context'
import { VoteManagementContextType, VoteManagementProviderProps } from '@/context/voteManagement'
import { useWebAssemblyHook } from '@/hooks/wasm/useWebAssembly'
import { useEffect, useState } from 'react'
import { SocialAuth } from '@/model/twitter.model'
import useLocalStorage from '@/hooks/generic/useLocalStorage'
import { VoteStateLite, VotingRound } from '@/model/vote.model'
import { useEnclaveServer } from '@/hooks/enclave/useEnclaveServer'
import { convertPollData, convertTimestampToDate } from '@/utils/methods'
import { Poll, PollResult } from '@/model/poll.model'
import { generatePoll } from '@/utils/generate-random-poll'
import { handleGenericError } from '@/utils/handle-generic-error'
import { StatusAPIResponse } from '@farcaster/auth-client'

const [useVoteManagementContext, VoteManagementContextProvider] = createGenericContext<VoteManagementContextType>()

const VoteManagementProvider = ({ children }: VoteManagementProviderProps) => {
/**
* Voting Management States
**/
const [socialAuth, setSocialAuth] = useLocalStorage<SocialAuth | null>('socialAuth', null)
const [user, setUser] = useState<SocialAuth | null>(socialAuth)
const [farcasterAuth, setFarcasterUser] = useLocalStorage<StatusAPIResponse | null>('farcasterAuth', null)
const [user, setUser] = useState<StatusAPIResponse | null>(farcasterAuth)
const [roundState, setRoundState] = useState<VoteStateLite | null>(null)
const [votingRound, setVotingRound] = useState<VotingRound | null>(null)
const [roundEndDate, setRoundEndDate] = useState<Date | null>(null)
Expand Down Expand Up @@ -59,7 +59,7 @@ const VoteManagementProvider = ({ children }: VoteManagementProviderProps) => {

const logout = () => {
setUser(null)
setSocialAuth(null)
setFarcasterUser(null)
}

const getRoundStateLite = async (roundCount: number) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { ReactNode } from 'react'
import * as WasmInstance from 'libs/wasm/pkg/crisp_web'
import { Auth, SocialAuth } from '@/model/twitter.model'

import { BroadcastVoteRequest, BroadcastVoteResponse, VoteStateLite, VotingRound } from '@/model/vote.model'
import { Poll, PollRequestResult, PollResult } from '@/model/poll.model'
import { StatusAPIResponse } from '@farcaster/auth-client'
import { Auth } from '@/model/auth.model'

export type VoteManagementContextType = {
isLoading: boolean
wasmInstance: WasmInstance.InitOutput | null
encryptInstance: WasmInstance.Encrypt | null
user: SocialAuth | null
user: StatusAPIResponse | null
votingRound: VotingRound | null
roundEndDate: Date | null
pollOptions: Poll[]
Expand All @@ -25,7 +27,7 @@ export type VoteManagementContextType = {
existNewRound: () => Promise<void>
getPastPolls: () => Promise<void>
setVotingRound: React.Dispatch<React.SetStateAction<VotingRound | null>>
setUser: (value: SocialAuth | null) => void
setUser: (value: StatusAPIResponse | null) => void
initWebAssembly: () => Promise<void>
encryptVote: (voteId: bigint, publicKey: Uint8Array) => Promise<Uint8Array | undefined>
broadcastVote: (vote: BroadcastVoteRequest) => Promise<BroadcastVoteResponse | undefined>
Expand Down
4 changes: 4 additions & 0 deletions packages/client/src/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ footer {
}
}

._1n3pr306 svg g {
fill: #65a30d !important;
}

/* Custom Scrollbar */
/* Firefox */
/* \* {
Expand Down
3 changes: 2 additions & 1 deletion packages/client/src/hooks/enclave/useEnclaveServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { BroadcastVoteRequest, BroadcastVoteResponse, RoundCount, VoteStateLite
import { useApi } from '../generic/useFetchApi'
import { PollRequestResult } from '@/model/poll.model'
import { fixPollResult, fixResult } from '@/utils/methods'
import { Auth } from '@/model/twitter.model'
import { Auth } from '@/model/auth.model'


const ENCLAVE_API = import.meta.env.VITE_ENCLAVE_API

Expand Down
70 changes: 0 additions & 70 deletions packages/client/src/hooks/twitter/useTwitter.ts

This file was deleted.

20 changes: 15 additions & 5 deletions packages/client/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,25 @@ import './globals.css'
import { HashRouter } from 'react-router-dom'
import { VoteManagementProvider } from '@/context/voteManagement/index.ts'
import { NotificationAlertProvider } from './context/NotificationAlert/NotificationAlert.context.tsx'
import '@farcaster/auth-kit/styles.css'
import { AuthKitProvider } from '@farcaster/auth-kit'

const config = {
relay: 'https://relay.farcaster.xyz',
domain: window.location.host,
siweUri: window.location.href,
}

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.Fragment>
<HashRouter>
<NotificationAlertProvider>
<VoteManagementProvider>
<App />
</VoteManagementProvider>
</NotificationAlertProvider>
<AuthKitProvider config={config}>
<NotificationAlertProvider>
<VoteManagementProvider>
<App />
</VoteManagementProvider>
</NotificationAlertProvider>
</AuthKitProvider>
</HashRouter>
</React.Fragment>,
)
4 changes: 4 additions & 0 deletions packages/client/src/model/auth.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface Auth {
jwt_token: string
response: 'Already Authorized' | 'No Authorization'
}
32 changes: 0 additions & 32 deletions packages/client/src/model/twitter.model.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/client/src/pages/DailyPoll/DailyPoll.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const DailyPoll: React.FC = () => {
return broadcastVote({
round_id: votingRound.round_id,
enc_vote_bytes: Array.from(voteEncrypted),
postId: user.token,
postId: user.fid?.toString() ?? '',
})
},
[broadcastVote, user, votingRound],
Expand Down
Loading

0 comments on commit e5292ff

Please sign in to comment.