byyoungjin / myblog-next

https://log.byyoung.me
0 stars 1 forks source link

Next.js authentication with AWS amplify #9

Closed byyoungjin closed 3 years ago

byyoungjin commented 3 years ago

https://dev.to/dabit3/the-complete-guide-to-next-js-authentication-2aco

byyoungjin commented 3 years ago

issue: signin 후에 blank page

https://github.com/aws-amplify/amplify-js/issues/5458 <-- not work

해결: 단순 typo --> sign in 후에는 component 가 사라진다.

byyoungjin commented 3 years ago

issue2 : federated signin typescript error

fix: https://github.com/dabit3/amplify-auth-demo/issues/7

byyoungjin commented 3 years ago
byyoungjin commented 3 years ago

issue: Auth.federatedSignin -> cred 반환하지 않음

https://github.com/aws-amplify/amplify-js/issues/5723

cognito 가 뒤에서 모든걸 handle 하기 때문에 id 를반환하지 않는다고함 https://github.com/aws-amplify/amplify-js/issues/5723#issuecomment-627395742

조금더 step 을 나눠서 아래방법으로 시도해봄 https://docs.amplify.aws/lib/auth/advanced/q/platform/js#google-sign-in-react

byyoungjin commented 3 years ago

issue: pop up closed by user

조금더 step 을 나눠서 아래방법으로 시도해봄 (user pool 을 이용하지않고, identity pool 을 이용함) https://docs.amplify.aws/lib/auth/advanced/q/platform/js#google-sign-in-react

위 방법으로 진행하면 client 단에서 요청이가서 유저가 cookie를 모두 허용해야만 작동한다. https://stackoverflow.com/questions/63631849/google-sign-in-not-working-in-incognito-mode/64011425#64011425

--> 적합하지 않음.

쿠키를 허용하지 않으면 제대로 작동하지 않음 https://github.com/google/google-api-javascript-client/issues/507

쿠키 허용하면 제대로 동작하는데, aws.configure 자체를 userPool 사용할때와는 다르게 해줘야하는것 같음 요런 에러가 뜬다. image

서버 안만들기로 생각하고 있으니까, 그냥 일단은 userPool 을 사용하자.

byyoungjin commented 3 years ago

OpenId connect Identiti Provideer 이용하는 법

https://aws.amazon.com/blogs/mobile/building-an-application-with-aws-amplify-amazon-cognito-and-an-openid-connect-identity-provider/

byyoungjin commented 3 years ago

user data 는 로그인후에 Profile 에서 넣을 수있도록하고,

데이터베이스 관리는 auth 와 연관해서 작업해보자.

byyoungjin commented 3 years ago

auth 관리 정리

auth, user 정도는 global context 로 관리할 필요가 있는것같다. auth 가 되어있다고 하더라도, 내가 관리하는 db 에 user 가 register 되어있을 필요가 있고, register 되어있지 않다면 register 하고, 이미 register 되어있다면 context 에 저장하는 로직. user 정보가 profile 페이지에서만 필요할 수도 있지만, 유저에 따라서 어떤 페이지가 보여지게 할지 customizing 할 수도 있으므로, user 정보는 global 하게 관리하는게 맞는 것 같다.

aws-amplify Hub 이용해서

  1. auth 바뀌는것 check,
  2. customOAuthState 이벤트 이용해서 fedrated signin 이후 로직 진행(추가 유저정보 입력, db 에 user 저장 )
  3. context api 이용해서 auth, user 정보 저장.
  4. 앱 최상단에 useEffect 이용해서 auth , user check, -> context 에 state hydrate

next.js 에서는 _app.ts 파일에 다음과같이 적용하면 될듯하다.

import React, { useEffect, useContext, createContext, useReducer } from 'react';
import { AppProps } from 'next/app';
import { useRouter } from 'next/router';
import produce from 'immer';

import Head from 'next/head';
import Amplify, { API, Storage, Auth, Hub, withSSRContext } from 'aws-amplify';

import { userByProviderKey } from '@/graphql/queries';

import config from '../aws-exports';
import './globals.scss';

Amplify.configure({
  ...config,
  ssr: true,
});

const INITIAL_AUTH_STATE = {
  user: null,
  auth: null,
  isAuthenticated: false,
  isSignIned: false,
};

const authReduceer = produce((draft, action) => {
  switch (action.type) {
    case 'AUTHENTICATE':
      draft.auth = action.payload;
      draft.isAuthenticated = true;
      break;
    case 'USER_SIGN_IN':
      draft.user = action.payload;
      draft.isSignIned = true;
      break;
    case 'SIGN_OUT':
      return INITIAL_AUTH_STATE;

    default:
      // throw Error('No matching type');
      return;
  }
}, INITIAL_AUTH_STATE);

export const AuthContext = createContext({});

function MyApp({ Component, pageProps }: AppProps) {
  const router = useRouter();
  const [authState, dispatch] = useReducer(authReduceer, INITIAL_AUTH_STATE);

  const listenAuthHandler = async ({ payload: { event, data } }) => {
    switch (event) {
      case 'signIn':
        dispatch({ type: 'AUTHENTICATE', payload: data });
        break;
      case 'signOut':
        dispatch({ type: 'SIGN_OUT' });
        break;
      case 'customOAuthState':
        try {
          const cognitoUser = await Auth.currentAuthenticatedUser();
          await userSignIn(cognitoUser);
        } catch (e) {
          console.log(`e`, e);
        }
        break;
    }
  };

  const userSignIn = async (cognitoUser) => {
    const providerKey = cognitoUser.username;
    const userOfProviderKey = await API.graphql({
      query: userByProviderKey,
      variables: { providerKey },
    });

    const dbUser = userOfProviderKey.data.userByProviderKey.items[0] ?? null;

    if (dbUser) {
      dispatch({ type: 'USER_SIGN_IN', payload: dbUser });
      console.log(`dbUser`, dbUser);
    } else {
      router.push('/user/register');
    }
  };

  useEffect(() => {
    Hub.listen('auth', listenAuthHandler);

    Auth.currentAuthenticatedUser()
      .then((cognitoUser) => {
        dispatch({ type: 'AUTHENTICATE', payload: cognitoUser });
        return cognitoUser;
      })
      .then((cognitoUser) => {
        userSignIn(cognitoUser);
      })
      .catch((e) => dispatch({ type: 'SIGN_OUT' }));

    return () => {
      Hub.remove('auth', listenAuthHandler);
    };
  }, []);
  return (
    <>
      <AuthContext.Provider value={{ authState, dispatch }}>
        <Head>
          <title>LOG</title>
          <link rel="icon" href="/favicon.ico" />
          <link
            rel="preload"
            href="/fonts/Pacifico-Regular.ttf"
            as="font"
            crossOrigin=""
          />
        </Head>
        <Component {...pageProps} />
      </AuthContext.Provider>
    </>
  );
}

export default MyApp;