Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Migrate Auth Process to Farcaster #15

Merged
merged 1 commit into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading