Intro
In this article i'll try to port over an app i made in firebase to pocketbase
1 : Authentication
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.
1 - setup your google service account
for this tutorial i used
- http://localhost:3000/redirect as the redirect url and
- http://localhost:3000 for authorised origins
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
2 - add the redirect routein app.tsx
Since we're using react-router-dom , we'll define a client route in App.tsx
import { useState } from 'react'
import { Query, useQuery } from 'react-query';
import { Routes, Route, BrowserRouter } from "react-router-dom";
import './App.css'
import { About } from './components/about/About';
import { Login } from './components/auth/Login';
import { Protected } from './components/auth/Protected';
import { Redirect } from './components/auth/Redirect';
import { UserType } from './components/auth/types';
import { Home } from './components/home/Home';
import { Toolbar } from './components/toolbar/Toolbar';
import { client } from './pb/config';
import { LoadingShimmer } from './components/Shared/LoadingShimmer';
function App() {
const getUser = async()=>{
return await client.authStore.model
}
const userQuery = useQuery(["user"],getUser);
console.log("user query App.tsx==== ", userQuery)
// console.log("client authstore",client.authStore)
const user = userQuery.data
if(userQuery.isFetching || userQuery.isFetching){
return <LoadingShimmer/>
}
return (
<div
className="h-screen w-screen scroll-bar flex-col-center
dark-styles transition duration-500 overflow-x-hidden "
>
<BrowserRouter >
<div className="fixed top-[0px] w-[100%] z-40 p-1">
<Toolbar />
</div>
<div className="w-full h-full mt-12 ">
<Routes>
<Route
path="/"
element={
<Protected user={user}>
<Home />
</Protected>
}
/>
<Route path="/about" element={<About />} />
<Route path="/login" element={<Login user={user}/>} />
<Route path="/redirect" element={<Redirect user=
{user}/>} />
</Routes>
</div>
</BrowserRouter>
</div>
);
}
export default App
and make the redirect and login components
Redirect.tsx
import { User, Admin } from 'pocketbase';
import React, { useEffect } from 'react'
import { useQueryClient } from 'react-query';
import { Navigate, useNavigate, useSearchParams } from 'react-router-dom';
import { client } from '../../pb/config';
import { LoadingShimmer } from '../Shared/loading/LoadingShimmer';
interface RedirectProps {
user?: User | Admin | null
}
export const Redirect: React.FC<RedirectProps> = ({user}) => {
const [loading, setLoading] = React.useState(true)
const queryClient = useQueryClient()
const navigate = useNavigate()
const [searchParams] = useSearchParams();
const code = searchParams.get('code') as string
const local_prov = JSON.parse(localStorage.getItem('provider') as string)
// this hasto match what you orovided in the oauth provider , in tis case google
let redirectUrl = 'http://localhost:3000/redirect'
useEffect(()=>{
if (local_prov.state !== searchParams.get("state")) {
const url = 'http://localhost:3000/login'
if (typeof window !== 'undefined') {
window.location.href = url;
}
}
else {
client.users.authViaOAuth2(
local_prov.name,
code,
local_prov.codeVerifier,
redirectUrl
)
.then((response) => {
// console.log("authentication data === ", response)
// udating te user rofile field in pocket base with custome data from your
// oauth provider in this case the avatarUrl and name
client.records.update('profiles', response.user.profile?.id as string, {
name: response.meta.name,
avatarUrl: response.meta.avatarUrl,
}).then((res) => {
// console.log(" successfully updated profi;e", res)
}).catch((e) => {
console.log("error updating profile == ", e)
})
setLoading(false)
// console.log("client modal after logg == ", client.authStore.model)
queryClient.setQueryData(['user'], client.authStore.model)
navigate('/')
}).catch((e) => {
console.log("error logging in with provider == ", e)
})
}
},[])
if (user) {
return <Navigate to="/" replace />;
}
return (
<div className='w-full h-full '>
{loading ? <LoadingShimmer/>:null}
</div>
);
}
Login.tsx
import React from "react";
import { providers } from "../../pb/config";
import { useNavigate } from 'react-router-dom';
import { Admin, User } from "pocketbase";
interface LoginProps {
user?: User | Admin | null
}
interface ProvType{
name: string
state: string
codeVerifier: string
codeChallenge: string
codeChallengeMethod: string
authUrl: string
}
export const Login: React.FC<
LoginProps
> = ({user}) => {
const provs = providers.authProviders;
const navigate = useNavigate()
// console.log("user in Login.tsx == ",user)
if(user?.email){
navigate('/')
}
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;
}
};
return (
<div className="w-full h-full flex-center-col">
<div className="text-3xl font-bold ">
LOGIN
</div>
{provs &&
provs?.map((item:any) => {
return (
<button
className="p-2 bg-purple-600"
key={item.name}
onClick={() => startLogin(item)}>{item.name}</button>
);
})}
</div>
);
};
Protected.tsx
import { Admin, User } from 'pocketbase';
import React, { ReactNode } from 'react'
import { Navigate } from 'react-router-dom';
interface ProtectedProps {
user?: User | Admin | null
children:ReactNode
}
export const Protected: React.FC<ProtectedProps> = ({user,children}) => {
if(!user?.email){
return <Navigate to={'/login'} />
}
return (
<div className='h-full w-full'>
{children}
</div>
);
}
Top comments (0)