UseAuth : AWSは、Auth +反応フックを簡単にします


これは、サンプルの実装を提供する短いポストですAWS Amplify authentication management フックで反応アプリで.

TLドクター

  • Auth.signIn() 成功すると、Auth.currentSession() . これはよく文書化されていないようですが、ブラウザのリフレッシュ時に認証状態を保持する能力を解除します.
  • source code and tests .
  • 叫ぶuseHooks.com インスピレーションを求めてuseAuth フックのソースコード.
  • 叫ぶKent C. Dodds 反応フックテスト戦略と実施に関するインスピレーションのために.
  • 問題


    この記事によって対処される望ましい結果は、Auth Management戦略です.
  • それがすべてのコンポーネントに容易に手に入るように、中心的にAuth状態を管理します.
  • このフックは、フックの構文を使用します.
  • 認証サービスはAWS増幅(フードの下のAWS認知)です.
  • をテストする.
  • 私がAWAVEアンプで私の初期の時間で見つけた1つのものは、ブラウザーリフレッシュで、私のアプリが現在の認証状態を失うだろうということです.一言で言えば、ログインしたユーザーがブラウザのリフレッシュにログインします.そしてそれは迷惑です.
    その上、私はこの問題についてあまり書かれていませんでした.AWSドキュメントで重要なラインを逃したのは全く可能ですがAuth.currentSession() アクセスされたセッションクッキーは、ブラウザで保持された主要なエピファニーだった.

    フック


    // use-auth.js
    
    import React, {
      useState, useEffect, useContext, createContext,
    } from 'react';
    import { Auth } from '@aws-amplify/auth';
    
    // Implement your particular AWS Amplify configuration
    const amplifyConfigurationOptions = {
      userPoolRegion: "REGION",
      userPoolId: "POOL_ID",
      userPoolWebClientId: "CLIENT_ID",
    };
    
    Auth.configure(amplifyConfigurationOptions);
    
    const AuthContext = createContext();
    
    // Wrap your app with <ProvideAuth />
    export function ProvideAuth({ children }) {
      const auth = useProvideAuth();
      return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>;
    }
    
    // Access auth values and functions with custom useAuth hook
    export const useAuth = () => useContext(AuthContext);
    
    function useProvideAuth() {
      const [user, setUser] = useState(null);
      const [isSignedIn, setIsSignedIn] = useState(false);
    
      useEffect(() => {
        // NOTE: check for user or risk an infinite loop
        if (!user) {
          // On component mount
          // If a session cookie exists
          // Then use it to reset auth state
          Auth.currentSession()
            .then((session) => {
              const {
                idToken,
                accessToken,
              } = session;
    
              // Define your user schema per your needs
              const user = {
                email: idToken.payload.email,
                username: idToken.payload.preferred_username,
                userId: idToken.payload.sub,
                accessToken: accessToken.jwtToken,
              };
    
              setIsSignedIn(true);
              setUser(user);
            })
            .catch((err) => {
              // handle it
            });
        }
      }, [user]);
    
      const signIn = ({ email, password }) => Auth.signIn(email, password)
        .then((cognitoUser) => {
          // Set user data and access token to memory
          const {
            attributes,
            signInUserSession: {
              accessToken,
            },
          } = cognitoUser;
    
          const user = {
            email: attributes.email,
            username: attributes.preferred_username,
            userId: attributes.sub,
            accessToken: accessToken.jwtToken,
          };
    
          setIsSignedIn(true);
          setUser(user);
    
          return user;
        });
    
      const signOut = () => Auth.signOut()
        .then(() => {
          setIsSignedIn(false);
          setUser(null);
        });
    
      return {
        user,
        isSignedIn,
        signIn,
        signOut,
      };
    }
    
    
    それが来るとき、私は認められたneophyteですuseEffect , それで、このコールバックの中でauth状態を回復するためのより良い実現があるかもしれません.特に、私は最初に無限ループにsetUser() だってuser はコールバックの依存関係の一つです.この1つのアドバイスを聞いて満足.

    使い方


    多くの擬似コードが、あなたはアイデアを得る.
    // AppRoot.jsx
    import React from 'react';
    
    import App from './app'; // uses <MyComponent />
    import { ProvideAuth } from './use-auth';
    
    return (
      <ProvideAuth>
        <App />
      </ProvideAuth>
    );
    
    // MyComponent.jsx
    import React from 'react';
    
    import { useAuth } from './use-auth';
    
    function MyComponent() {
      const { isSignedIn, user, signIn, signOut } = useAuth();
    
      return (
        <div>
          <div>{`IsSignedIn: ${isSignedIn}`}</div>
          <div>{`Username: ${user?.username}`}</div>
          {isSignedIn ? (
            <button onClick={signOut} type="button">Sign Out</button>
          ) : (
            <button onClick={signIn} type="button">Sign In</button>
          )}
        </div>
      )
    };
    

    テスト


    それは抽象的なフックをテストするのに完全に可能です、しかし、ケントC .ドッズはそれが自然の生息地でフックをテストするのがより良いと私に確信させました.コンポーネント.
    基本的に、フックを使用する例のコンポーネントを設定し、フックによってのみ実現できるコンポーネントの状態を期待します.
    // Example Component
    
    import React from 'react';
    import { ProvideAuth, useAuth } from '../src/use-auth';
    
    function TestComponent() {
      const {
        user,
        isSignedIn,
        signIn,
        signOut,
      } = useAuth();
    
      const handleSignIn = () => {
        const mockCreds = {
          email: '[email protected]',
          password: 'pw',
        }
    
        signIn(mockCreds);
      }
    
      const handleSignOut = () => signOut()
    
      return (
        <div>
          <div>{`IsSignedIn: ${isSignedIn}`}</div>
          <div>{`Username: ${user?.username}`}</div>
          <div>{`AccessToken: ${user?.accessToken}`}</div>
          <button onClick={handleSignIn} type="button">SignInButton</button>
          <button onClick={handleSignOut} type="button">SignOutButton</button>
        </div>
      );
    }
    
    function UseAuthExample() {
      return (
        <ProvideAuth>
          <TestComponent />
        </ProvideAuth>
      );
    }
    
    export { UseAuthExample };
    
    // use-auth.test.jsx
    
    import React from 'react';
    import {
      render, screen, fireEvent, act,
    } from '@testing-library/react';
    import { Auth } from '@aws-amplify/auth';
    
    import { UseAuthExample } from './UseAuthExample';
    
    describe('useAuth', () => {
      beforeEach(() => {
        jest.clearAllMocks();
      });
    
      it('should provide default values on load when user is not authenticated', () => {
        const currentSessionMock = jest.fn().mockRejectedValue('No user found.');
        Auth.currentSession = currentSessionMock;
    
        render(<UseAuthExample />);
    
        const isSignedIn = screen.getByText(/issignedin/i);
        const username = screen.getByText(/username/i);
        const accessToken = screen.getByText(/accesstoken/i);
    
        expect(isSignedIn).toHaveTextContent('IsSignedIn: false');
        expect(username).toHaveTextContent('Username:');
        expect(accessToken).toHaveTextContent('AccessToken:');
      });
    
      it('should provide current user on load when current session is found', async () => {
        const currentSessionMock = jest.fn().mockResolvedValue({
          idToken: {
            payload: {
              email: '[email protected]',
              preferred_username: 'myuser',
              sub: '1234-abcd',
            },
          },
          accessToken: {
            jwtToken: 'fake-token',
          },
        });
        Auth.currentSession = currentSessionMock;
    
        await act(async () => {
          render(<UseAuthExample />);
        });
    
        const isSignedIn = screen.getByText(/issignedin/i);
        const username = screen.getByText(/username/i);
        const accessToken = screen.getByText(/accesstoken/i);
    
        expect(isSignedIn).toHaveTextContent('IsSignedIn: true');
        expect(username).toHaveTextContent('Username: myuser');
        expect(accessToken).toHaveTextContent('AccessToken: fake-token');
      });
    
      it('should login the user and update ui', async () => {
        const currentSessionMock = jest.fn().mockRejectedValue('No user found.');
        const signInMock = jest.fn().mockResolvedValue({
          attributes: {
            email: '[email protected]',
            preferred_username: 'myuser',
            sub: '1234-abcd',
          },
          signInUserSession: {
            accessToken: {
              jwtToken: 'fake-token',
            },
          },
        });
        Auth.currentSession = currentSessionMock;
        Auth.signIn = signInMock;
    
        render(<UseAuthExample />);
    
        const isSignedIn = screen.getByText(/issignedin/i);
        const username = screen.getByText(/username/i);
        const accessToken = screen.getByText(/accesstoken/i);
    
        expect(isSignedIn).toHaveTextContent('IsSignedIn: false');
        expect(username).toHaveTextContent('Username:');
        expect(accessToken).toHaveTextContent('AccessToken:');
    
        const signInButton = screen.getByText(/signinbutton/i);
    
        await act(async () => {
          fireEvent.click(signInButton);
        });
    
        expect(isSignedIn).toHaveTextContent('IsSignedIn: true');
        expect(username).toHaveTextContent('Username: myuser');
        expect(accessToken).toHaveTextContent('AccessToken: fake-token');
      });
    });