DEV Community

Cover image for Social app with React and Pocketbase : Oauth Authentication
Dennis kinuthia
Dennis kinuthia

Posted on • Edited on

Social app with React and Pocketbase : Oauth Authentication

Authentication

In this first part we'll implement user authentication with the pocketbase social OAUTH providers
I'll use google and GitHub but they support a dozen more.

  1. Obtaining client id and client secret from the providers setting up GitHub OAUTH

setting up google OAUTH

then enable the respective providers in the pocketbase admin dashboard

enabling GitHub OAUTH
enabling Google OAUTH

  1. frontend integration using the pocketbase client
const authData = await pb.collection('devs').authWithOAuth2( 'google', 'CODE', 'VERIFIER', 'REDIRECT_URL', // optional data that will be used for the new account on OAuth2 sign-up { 'name': 'test', }, ); 
Enter fullscreen mode Exit fullscreen mode

to get the required arguments we need to fetch the enabled providers

the start function

first we get some icons for the respective providers

import { TheIcon } from '@denniskinuthia/tiny-pkgs'; import { FaGithub,FaGoogle } from 'react-icons/fa' const providerIcons={ github:FaGithub, google:FaGoogle } 
Enter fullscreen mode Exit fullscreen mode

then

const providers = await client.collection("devs").listAuthMethods() 
Enter fullscreen mode Exit fullscreen mode

initiate login function using:

const startLogin = (prov:ProvType) => { localStorage.setItem("provider",JSON.stringify(prov)); const redirectUrl = "http://localhost:3000/redirect"; const url = prov.authUrl + redirectUrl; // console.log("prov in button === ", prov) // console.log("combined url ==== >>>>>> ",url) if (typeof window !== "undefined") { window.location.href = url; } }; 
Enter fullscreen mode Exit fullscreen mode

note: the redirect URL should match what you provided in the setup process: once you've hosted your website you can use your actual domain instead of localhost

then we'll map over them and render out a button for each provider

 <div className="w-full h-fit md:h-full flex flex-wrap items-center justify-center gap-2 "> {provs && provs?.map((item:any) => { return ( <div key={item.name} onClick={() => startLogin(item)} className="p-2 w-[50%] md:w-[30%] cursor-pointer bg-slate-600 rounded-lg hover:bg-slate-800 capitalize text-xl font-bold flex items-center justify-center gap-2" > <TheIcon iconstyle="" Icon={providerIcons[item.name as keyof typeof providerIcons]} size={'30'} />  {item.name} </div>  ); })} </div> 
Enter fullscreen mode Exit fullscreen mode

finally the redirect component

remember to define a route for it in your react router config

Click to expand Redirect.tsx
Redirect.tsx import React, { useEffect } from 'react' import { useNavigate } from 'react-router-dom'; import { PBUser } from '../../utils/types/types'; import { useQueryClient } from '@tanstack/react-query'; import { client } from './../../utils/pb/config'; import { LoadingRipples } from '@denniskinuthia/tiny-pkgs'; import { redirect_url } from '../../utils/env'; import { login_url } from './../../utils/env'; interface RedirectProps { user?:PBUser } export const Redirect: React.FC<RedirectProps> = ({user}) => { const queryClient = useQueryClient() const navigate = useNavigate() const local_prov = JSON.parse(localStorage.getItem('provider') as string) const url = new URL(window.location.href); const code = url.searchParams.get('code') as string const state = url.searchParams.get('state') as string // this hasto match what you orovided in the oauth provider , in tis case google let redirectUrl = redirect_url useEffect(()=>{ const pbOauthLogin=async()=>{ client.autoCancellation(false) const oauthRes = await client.collection('devs') .authWithOAuth2(local_prov.name, code, local_prov.codeVerifier, redirectUrl) await client.collection('devs').update(oauthRes?.record.id as string, { avatar: oauthRes.meta?.avatarUrl, accessToken: oauthRes.meta?.accessToken }) queryClient.setQueryData(['user'], client.authStore.model) navigate('/') } if (local_prov.state !== state) { const url = login_url if (typeof window !== 'undefined') { window.location.href = url; } } else { pbOauthLogin().catch((e) => { console.log("error logging in with provider == ", e) }) } },[]) return ( <div className='w-full h-full flex items-center justify-center'> <LoadingRipples/> </div> ); } 
Enter fullscreen mode Exit fullscreen mode

Note: I used client.autoCancellation(false) to avoid the OAUTH request getting auto cancelled in dev mode because of react strict mode

finally we can put in place route AUTH guards , I prefer to do it at the root layout level inside which every other route is nested

Click to expand RootLayout.tsx

RootLayout.tsx import React from 'react' import { Outlet, useNavigate } from 'react-router-dom'; import { Toolbar } from '../../components/toolbar/Toolbar'; import { PBUser } from '../../utils/types/types'; interface RootLayoutProps { user : PBUser test_mode:boolean } export const RootLayout: React.FC = ({user,test_mode}) =&gt; { const navigate = useNavigate() React.useEffect(() =&gt; { if (!user?.email&amp;&amp;!test_mode) { navigate('/auth') } }, [user?.email]) return ( ); } 
Enter fullscreen mode Exit fullscreen mode

complete code
AUTH guarding

Top comments (0)