pocketbase + google-oauth + react-router + react-query로 반응 인증

소개



이 기사에서는 firebase에서 만든 앱을 pocketbase로 포팅하려고 합니다.

1 : 인증



2022년에는 사용자와 개발자가 쉽게 사용할 수 있도록 공급자 로그인이 기본 인증 옵션이 되어야 합니다.
사용자는 한 번의 클릭으로 로그인할 수 있으며 개발자는 사용자 암호 확인, 저장 및 관리에 대해 걱정할 필요가 없습니다.
Firebase는 이를 구현하는 간단한 방법을 제공하지만(특히 Google에서는 firebase가 기본적으로 구성된 클라이언트 토큰인 클라이언트 암호로 서비스 계정을 생성하기 때문에) pocketbase에서 약간의 작업을 수행할 수도 있습니다.

1 - Google 서비스 계정 설정



for this tutorial i used





official docs

2 - app.tsx에 리디렉션 경로 추가



react-router-dom 을 사용하고 있으므로 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



리디렉션 및 로그인 구성 요소를 만듭니다.
리디렉션.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>
);
}



로그인.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>
  );
};



보호됨.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>
);
}



full code

좋은 웹페이지 즐겨찾기