Back to blogs
Blog
Writing in public
Longer posts — published here first, then cross-posted to Dev.to with a canonical URL back to this site.
Blog
Longer posts — published here first, then cross-posted to Dev.to with a canonical URL back to this site.
Back to blogs
Back to blogs
Postreact, pocketbase, googlecloud, oauth
Intro In this article i'll try to port over an app i made in firebase to pocketbase ...
Date published

Why You Might Still Pick Next.js Over TanStack Start From a TanStack Start...

A complete theme management system for React applications with SSR support! ✨ 🌟...

You can select the scopes you want to request from GitHub/your oauthprovider in the login page, and...
In this article i'll try to port over an app i made in firebase to pocketbase
In 2022 provider sign-ins should be the main auth option given the ease of use for the user and developer . The users get to login with one click while the dev doesn't have to worry about verifying , storing and managing the user passwords. Firebase gives us a simple way to implement this (especially in google since firebase creates you a service account with the client secret an client token configured by default ) but it can also be done in pocketbase woth a little amnual work.
for this tutorial i used

official docs video demonstration up to 1:55
then we'll enable the google as an auth provider and provde the client id and client secret in the pocket base admin panel

Since we're using react-router-dom , we'll define a client route in App.tsx
1import { useState } from 'react'2import { Query, useQuery } from 'react-query';3import { Routes, Route, BrowserRouter } from "react-router-dom";4import './App.css'5import { About } from './components/about/About';6import { Login } from './components/auth/Login';7import { Protected } from './components/auth/Protected';8import { Redirect } from './components/auth/Redirect';9import { UserType } from './components/auth/types';10import { Home } from './components/home/Home';11import { Toolbar } from './components/toolbar/Toolbar';12import { client } from './pb/config';13import { LoadingShimmer } from './components/Shared/LoadingShimmer';141516function App() {171819 const getUser = async()=>{20 return await client.authStore.model21 }2223const userQuery = useQuery(["user"],getUser);24 console.log("user query App.tsx==== ", userQuery)2526 // console.log("client authstore",client.authStore)27const user = userQuery.data2829if(userQuery.isFetching || userQuery.isFetching){30 return <LoadingShimmer/>31}3233return (34 <div35 className="h-screen w-screen scroll-bar flex-col-center36 dark-styles transition duration-500 overflow-x-hidden "37 >38 <BrowserRouter >3940 <div className="fixed top-[0px] w-[100%] z-40 p-1">41 <Toolbar />42 </div>434445 <div className="w-full h-full mt-12 ">46 <Routes>47 <Route48 path="/"49 element={50 <Protected user={user}>51 <Home />52 </Protected>53 }54 />5556 <Route path="/about" element={<About />} />57 <Route path="/login" element={<Login user={user}/>} />58 <Route path="/redirect" element={<Redirect user=59 {user}/>} />60 </Routes>61 </div>6263 </BrowserRouter>64 </div>65 );66}6768export default App69
and make the redirect and login components Redirect.tsx
1import { User, Admin } from 'pocketbase';2import React, { useEffect } from 'react'3import { useQueryClient } from 'react-query';4import { Navigate, useNavigate, useSearchParams } from 'react-router-dom';5import { client } from '../../pb/config';6import { LoadingShimmer } from '../Shared/loading/LoadingShimmer';789interface RedirectProps {10user?: User | Admin | null11}1213export const Redirect: React.FC<RedirectProps> = ({user}) => {14const [loading, setLoading] = React.useState(true)15const queryClient = useQueryClient()16const navigate = useNavigate()17const [searchParams] = useSearchParams();18const code = searchParams.get('code') as string19const local_prov = JSON.parse(localStorage.getItem('provider') as string)20// this hasto match what you orovided in the oauth provider , in tis case google21let redirectUrl = 'http://localhost:3000/redirect'22useEffect(()=>{23 if (local_prov.state !== searchParams.get("state")) {24 const url = 'http://localhost:3000/login'25 if (typeof window !== 'undefined') {26 window.location.href = url;27 }28 }29 else {3031 client.users.authViaOAuth2(32 local_prov.name,33 code,34 local_prov.codeVerifier,35 redirectUrl36 )37 .then((response) => {38 // console.log("authentication data === ", response)39 // udating te user rofile field in pocket base with custome data from your40 // oauth provider in this case the avatarUrl and name41 client.records.update('profiles', response.user.profile?.id as string, {42 name: response.meta.name,43 avatarUrl: response.meta.avatarUrl,4445 }).then((res) => {46 // console.log(" successfully updated profi;e", res)4748 }).catch((e) => {49 console.log("error updating profile == ", e)50 })51 setLoading(false)52 // console.log("client modal after logg == ", client.authStore.model)53 queryClient.setQueryData(['user'], client.authStore.model)54 navigate('/')5556 }).catch((e) => {57 console.log("error logging in with provider == ", e)58 })59 }6061},[])62if (user) {63 return <Navigate to="/" replace />;64}65return (66 <div className='w-full h-full '>67 {loading ? <LoadingShimmer/>:null}68 </div>69);70}71
Login.tsx
1import React from "react";2import { providers } from "../../pb/config";3import { useNavigate } from 'react-router-dom';4import { Admin, User } from "pocketbase";56789interface LoginProps {10user?: User | Admin | null11}12interface ProvType{1314 name: string15 state: string16 codeVerifier: string17 codeChallenge: string18 codeChallengeMethod: string19 authUrl: string2021}2223export const Login: React.FC<24 LoginProps25> = ({user}) => {26const provs = providers.authProviders;27const navigate = useNavigate()28// console.log("user in Login.tsx == ",user)29if(user?.email){30 navigate('/')31}32const startLogin = (prov:ProvType) => { localStorage.setItem("provider",JSON.stringify(prov));33 const redirectUrl = "http://localhost:3000/redirect";34 const url = prov.authUrl + redirectUrl;35 // console.log("prov in button === ", prov)36 // console.log("combined url ==== >>>>>> ",url)3738 if (typeof window !== "undefined") {39 window.location.href = url;40 }41 };4243 return (44 <div className="w-full h-full flex-center-col">45 <div className="text-3xl font-bold ">46 LOGIN47 </div>48 {provs &&49 provs?.map((item:any) => {50 return (51 <button52 className="p-2 bg-purple-600"53 key={item.name}54 onClick={() => startLogin(item)}>{item.name}</button>55 );56 })}57 </div>58 );59};60
Protected.tsx
1import { Admin, User } from 'pocketbase';2import React, { ReactNode } from 'react'3import { Navigate } from 'react-router-dom';45interface ProtectedProps {6 user?: User | Admin | null7children:ReactNode8}910export const Protected: React.FC<ProtectedProps> = ({user,children}) => {11if(!user?.email){12 return <Navigate to={'/login'} />13}14return (15 <div className='h-full w-full'>16 {children}17 </div>18);19}20