DEV Community

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

Posted on • Updated 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)