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.
- Obtaining client id and client secret from the providers setting up GitHub OAUTH
then enable the respective providers in the pocketbase admin dashboard
- frontend integration using the pocketbase client ```ts
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',
},
);
to get the required arguments we need to fetch the enabled providers
the start function
first we get some icons for the respective providers
```ts
import { TheIcon } from '@denniskinuthia/tiny-pkgs';
import { FaGithub,FaGoogle } from 'react-icons/fa'
const providerIcons={
github:FaGithub,
google:FaGoogle
}
then
const providers = await client.collection("devs").listAuthMethods()
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;
}
};
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>
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>
);
}
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}) => {
const navigate = useNavigate()
React.useEffect(() => {
if (!user?.email&&!test_mode) {
navigate('/auth')
}
}, [user?.email])
return (
);
}
Top comments (0)