Skip to content

Commit d30a6d8

Browse files
committed
feat: Add AuthProvider component for handling user authentication via OAuth tokens
1 parent 6c9ade3 commit d30a6d8

1 file changed

Lines changed: 91 additions & 0 deletions

File tree

src/providers/AuthProvider.tsx

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { GithubAuthProvider, GoogleAuthProvider, signInWithCredential } from 'firebase/auth'
2+
import { useCallback, useEffect } from 'react'
3+
import { useNavigate, useSearchParams } from 'react-router-dom'
4+
import toast from 'react-simple-toasts'
5+
import { useAuth } from 'src/features/auth'
6+
import { firebaseAuth } from 'src/lib/firebase'
7+
8+
export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
9+
const navigate = useNavigate()
10+
const [searchParams] = useSearchParams()
11+
const { closeAuthModal, initState, setAuthError } = useAuth()
12+
13+
const connectTheUser = useCallback((token?: string | null, provider?: string | null) => {
14+
const allowedProviders = ['google', 'github']
15+
if ((provider && !allowedProviders.includes(provider)) || !token) {
16+
return Promise.resolve()
17+
}
18+
19+
const authProvider =
20+
provider === 'google'
21+
? GoogleAuthProvider.credential(null, token)
22+
: GithubAuthProvider.credential(token)
23+
24+
return signInWithCredential(firebaseAuth, authProvider).then((userCredential) => {
25+
const user = userCredential.user
26+
initState({
27+
user: {
28+
name: user.displayName || 'Anonymous',
29+
imageURL: user.photoURL || '',
30+
},
31+
providerId: authProvider.providerId,
32+
})
33+
closeAuthModal()
34+
navigate(window.location.pathname, { replace: true })
35+
})
36+
}, [])
37+
38+
/**
39+
* This effect is used to connect the user when the token is received from the background script
40+
* on Chrome and Firefox extensions
41+
*/
42+
useEffect(() => {
43+
const messageListener = (message: {
44+
action: string
45+
type?: string
46+
access_token?: string
47+
provider?: string
48+
}) => {
49+
if (message.type === 'TOKEN_RECEIVED') {
50+
const { access_token: token, provider } = message
51+
connectTheUser(token, provider).catch((error) => {
52+
if (error && error.code === 'auth/account-exists-with-different-credential') {
53+
setAuthError({
54+
message:
55+
'You have an account with a different provider. Please sign in with that provider to continue.',
56+
})
57+
}
58+
})
59+
}
60+
}
61+
62+
chrome.runtime?.onMessage.addListener(messageListener)
63+
64+
return () => {
65+
chrome.runtime?.onMessage.removeListener(messageListener)
66+
}
67+
}, [])
68+
69+
/**
70+
* This effect is used to connect the user when the user when the token is received from the URL
71+
* on the web
72+
*/
73+
useEffect(() => {
74+
const token = searchParams.get('access_token')
75+
const provider = searchParams.get('provider')
76+
connectTheUser(token, provider).catch((error) => {
77+
if (error && error.code === 'auth/account-exists-with-different-credential') {
78+
toast(
79+
'You have an account with a different provider. Please sign in with that provider to continue.',
80+
{
81+
theme: 'dangerToast',
82+
}
83+
)
84+
} else {
85+
console.log(error)
86+
toast('Error signing in, Please try again', { theme: 'dangerToast' })
87+
}
88+
})
89+
}, [searchParams])
90+
return <>{children}</>
91+
}

0 commit comments

Comments
 (0)