次の認証.ファイアーベース


私の次に.JSプロジェクト、私はいくつかの認証を追加したい.私のユーザー管理とデータストアのFireBaseを使用することを決めた.
必要なもの
  • さえずりを使ったOAuth
  • クライアント側認証
  • 保護ページ
  • サーバ側認証
  • Assumptions: You already have a base Next.js project setup and you created a project in Firebase.



    セットアップfirebase
    firebaseパッケージのインストール
    npm i --save firebase firebase-admin
    
    クリエイトアenv.local ファイルを必要なすべてのfirebaseキーを追加する
    NEXT_PUBLIC_FIREBASE_API_KEY=********************
    NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=***********
    NEXT_PUBLIC_FIREBASE_PROJECT_ID=*********
    
    FIREBASE_PRIVATE_KEY=*********************
    FIREBASE_CLIENT_EMAIL=*************
    FIREBASE_DATABASE_URL=*************
    
    ここでFireBaseに接続するファイルを作成する必要があります.lib/firebase.ts - OAuthの処理と認証の維持
    import * as firebase from 'firebase/app';
    import 'firebase/auth';
    import 'firebase/functions';
    import 'firebase/firestore';
    
    if (!firebase.apps.length) {
      firebase.initializeApp({
        apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
        authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
        projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID
      });
    }
    
    export default firebase;
    
    lib/firebase-admin.ts - トークンサーバー側の確認.
    import admin from 'firebase-admin';
    
    if (!admin.apps.length) {
      admin.initializeApp({
        credential: admin.credential.cert({
          projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
          privateKey: process.env.FIREBASE_PRIVATE_KEY,
          clientEmail: process.env.FIREBASE_CLIENT_EMAIL
        }),
        databaseURL: process.env.FIREBASE_DATABASE_URL
      });
    }
    
    const db = admin.firestore();
    const auth = admin.auth();
    
    export { db, auth };
    
    lib/db.ts - データベースクエリ
    import firebase from '../lib/firebase';
    
    const firestore = firebase.firestore();
    
    export function updateUser(uid: string, data: any) {
      return firestore.collection('users').doc(uid).update(data);
    }
    
    export function createUser(uid: string, data: any) {
      return firestore
        .collection('users')
        .doc(uid)
        .set({ uid, ...data }, { merge: true });
    }
    
    今、我々は簡単に我々のユーザーのセッションとauth状態を維持するためのフックを構築するためにこれらのlibファイルを使用することができます.

    Authフックのビルド
    私は、auth状態を扱うために文脈APIを使用することに決めました.この方法で簡単に任意のアプリケーションの全体の変数にアクセスできます.
    まず、私はlib/auth.tsx .
    それからフックのコンテキスト部分を設定します
    interface AuthContext {
      auth: Auth | null;
      loading: boolean;
      signInWithTwitter: () => Promise<void>;
      signOut: () => Promise<void>;
    }
    
    // Create context with a default state.
    const authContext: Context<AuthContext> = createContext<AuthContext>({
      auth: null,
      loading: true,
      signInWithTwitter: async () => {},
      signOut: async () => {}
    });
    
    export function AuthProvider({ children }) {
      const auth = useProvideAuth();
      return <authContext.Provider value={auth}>{children}</authContext.Provider>;
    }
    
    // Helper to easily get auth context within components
    export const useAuth = () => useContext(authContext);
    
    より複雑な部分のための時間useProvideAuth() .
    function useProvideAuth() {
      const [auth, setAuth] = useState<Auth | null>(null);
      const [loading, setLoading] = useState<boolean>(true);
    
      /**
       * Callback function used for firebase.auth.onAuthStateChanged().
       * Takes the user object returned and formats it for my state.
       * We fetch the idToken and append it to my auth state and store it.
       */
      const authStateChanged = async (authState: firebase.User | null) => {
        // Formats response into my required state.
        const formattedAuth = formatAuth(authState);
        // Fetch firebase auth ID Token.
        formattedAuth.token = await authState.getIdToken();
        // Stores auth into state.
        setAuth(formattedAuth);
        // Sets loading state to false.
        setLoading(false);
      };
    
      /**
       * Callback function used for response from firebase OAuth.
       * Store user object returned in firestore.
       * @param firebase User Credential
       */
      const signedIn = async (resp: firebase.auth.UserCredential) => {
        // Format user into my required state.
        const storeUser = formatAuth(resp.user);
        // firestore database function
        createUser(storeUser.uid, storeUser);
      };
    
      /**
       * Callback for when firebase signOut.
       * Sets auth state to null and loading to true.
       */
      const clear = () => {
        setAuth(null);
        setLoading(true);
      };
    
      /**
       * Triggers firebase Oauth for twitter and calls signIn when successful.
       * sets loading to true.
       */
      const signInWithTwitter = () => {
        setLoading(true);
        return firebase.auth().signInWithPopup(new firebase.auth.TwitterAuthProvider()).then(signedIn);
      };
    
      /**
       * Calls firebase signOut and with clear callback to reset state.
       */
      const signOut = () => {
        return firebase.auth().signOut().then(clear);
      };
    
      /**
       * Watches for state change for firebase auth and calls the handleUser callback
       * on every change.
       */
      useEffect(() => {
        const unsubscribe = firebase.auth().onAuthStateChanged(authStateChanged);
        return () => unsubscribe();
      }, []);
    
      // returns state values and callbacks for signIn and signOut.
      return {
        auth,
        loading,
        signInWithTwitter,
        signOut
      };
    }
    

    Authフックの使用
    追加AuthProvider 私にpages/_app.tsx .
    import { AppProps } from 'next/app';
    import { AuthProvider } from '../lib/auth';
    
    import '../styles/globals.css';
    
    export default function MyApp({ Component, pageProps }: AppProps) {
      return (
        <AuthProvider>
          <Component {...pageProps} />
        </AuthProvider>
      );
    }
    
    今、我々はAuthContext 我々のページで.
    私たちはボタンにサインを追加することができますpages/index.tsx . 我々が認証されるならば、我々はリンクとサインアウトボタンを表示することができます.
    import { useAuth } from '../lib/auth';
    import Link from 'next/link';
    import { useEffect } from 'react';
    
    export default function Home() {
      const { auth, signOut, signInWithTwitter } = useAuth();
    
      return (
        <div>
          {auth ? (
            <div>
              <Link href='/dashboard'>
                <a>Dashboard</a>
              </Link>
              <button onClick={() => signOut()}>Sign Out</button>
            </div>
          ) : (
            <button onClick={() => signInWithTwitter()}>Sign In</button>
          )}
        </div>
      );
    }
    
    私のダッシュボードルートをAuthで保護したい.ユーザーが認証されていない場合、インデックスページにリダイレクトされます.
    import { useRouter } from 'next/router';
    import { useEffect } from 'react';
    import { useAuth } from '../lib/auth';
    
    export default function Dashboard() {
      const { auth, loading, signOut } = useAuth();
    
      const router = useRouter();
    
      useEffect(() => {
        // If auth is null and we are no longer loading
        if (!auth && !loading) {
          // redirect to index
          router.push('/');
        }
      }, [auth, loading]);
    
      return (
        <div>
          <p>Dashboard: Hello World</p>
          {auth && (
            <div>
              <button onClick={() => signOut()}>Sign Out</button>
            </div>
          )}
        </div>
      );
    }
    

    サーバ側認証
    サーバー側認証はクライアントからAPIへのIDトークンを渡すことによって扱われます.その後、APIはすべてのリクエストでトークンを確認します.
    最初にトークンを渡すフェッチUtilを作成しましょう.util/fetcher.ts .
    const fetcher = async (url: string, token: string) => {
      const res = await fetch(url, {
        method: 'GET',
        headers: new Headers({ 'Content-Type': 'application/json', token }),
        credentials: 'same-origin'
      });
    
      return res.json();
    };
    
    export default fetcher;
    
    その後、APIルート上のトークンを確認することができますfirebase-admin .
    APIルート:pages/api/user.ts
    import { NextApiRequest, NextApiResponse } from 'next';
    import { auth } from '../../lib/firebase-admin';
    
    export default async (req: NextApiRequest, res: NextApiResponse) => {
      try {
        const { uid } = await auth.verifyIdToken(req.headers.token);
    
        res.status(200).json({ uid });
      } catch (error) {
        res.status(401).json({ error });
      }
    };
    
    これで、ダッシュボードページ内のユーザーデータを取得するAPI呼び出しを行うことができます.私はuseSWR API呼び出しを扱うフック.pages/dashboard.tsx
    import { useRouter } from 'next/router';
    import { useEffect } from 'react';
    import useSWR from 'swr';
    import { useAuth } from '../lib/auth';
    import fetcher from '../util/fetcher';
    
    export default function Dashboard() {
      const { auth, loading, signOut } = useAuth();
    
      const router = useRouter();
    
      useEffect(() => {
        if (!auth && !loading) {
          router.push('/');
        }
      }, [auth, loading]);
    
      const { data } = useSWR(auth ? ['/api/user', auth.token] : null, fetcher);
    
      return (
        <div>
          <p>Dashboard: Hello World</p>
          {auth && (
            <div>
              <button onClick={() => signOut()}>Sign Out</button>
            </div>
          )}
          {data && <div>{data}</div>}
        </div>
      );
    }
    

    結論
    私は今、FireBaseを使用して自分のWebアプリケーションの認証を行っている.
  • ユーザーはTwitterのOAuthを使用してログインできます.
  • これは、ユーザーを作成し、FireBaseに格納します.
  • 私は、ユーザーが認証されないなら、リダイレクトを持った保護されたルートを持っています.
  • 私は、これまでのリクエストでユーザーのトークンを検証する保護されたエンドポイントを持っています.
  • ここではRepository 記事の作業コードを使用します.
    それは最高の解決策ではないかもしれないが、仕事を得る.
  • ネスト.jsDocs
  • 火の粉Docs
  • SWRDocs
  • 技術とプログラミングについてのランダムなポストのために私に続いてください.私も私の旅の学習デザインのドキュメントです.