Next.js-KACAログイン実施


今回のプロジェクトは登録部分を担当します.
Kakaoログインと電子メールログインを実現するには、バックエンド開発者が作成したapiがすでにあるので、簡単にできるはずです...
途中で私の知っている方法でやらなかったので,新しい方法を探してやったので,少し苦労した.😂
その過程で学んだことを整理したい.

Kakaoログイン


まず、KACAログインを実施した内容を確認する前に、KACAログインの手順を確認します:
この絵(出ている:https://data-jj.tistory.com/53)に合っています.
フロントで話した部分は1번, 2번, 3번, 8번で、reactを使用したプロジェクトで以前やったようにしようとしましたが、計画はこうです.
まず,kakaoのapiで承認コードを取得する.
第二に、apiを介して受信した承認コードをサーバに渡す.
第3に、apiは、戻り値をJWTに渡し、復号してユーザ情報として使用し、登録情報を保持するためにlocalStorageに格納する.
3つ目の過程で問題が発生しました👿

👊 問題の原因

nextjsを用いてレンダリングを行うためには、サーバ側からデータを取得し、この場合はユーザのデータが必要となり、JWTを復号してインポートする必要がある.ただし、サーバ側はlocalStorageにアクセスできないため、エラーが発生しました.

🤔 ソリューション


問題を解決するために、Googleを使用する場合、通常はJWTcookieに格納して管理します.このプロセスでは、localStorageに格納されているセキュリティの問題も理解しました.もちろん、現在のプロジェクトでは、Access TokenRefresh Tokenは、いずれもkakao自身が管理・更新しているので関係ありませんが、今後はAccess TokenRefresh Tokenを直接管理できるので、勉強したほうがいいです.
ローカルストレージ
localStorageに保存すると、XSS攻撃でJWTを奪われる可能性があります!
https://academind.com/tutorials/localstorage-vs-cookies-xss
クッキーストレージ
クッキーの格納方式もXSS、CSRF攻撃から解放されないが、クッキーのSameSite, httpOnly, Secureなどの属性はある程度セキュリティ問題を解決している.さらに、JWTが他のドメインで使用されず、apiサーバも同じドメインを使用する場合、Access TokenおよびRefresh Tokenは、cookieに含めて管理することができる.この詳細については、投稿2投稿を参照してください.
クライアントコードにcookieを設定するプロセスを非表示にし、ログイン情報をプレゼンテーションページに適用するために、nextjsのapi-route機能が使用される.

👍 トラブルシューティング


まず,kakaoのapiで承認コードを取得する.
2番目に受信した承認コードは、/api/loginKakaoパスのapiを呼び出し、nextサーバに転送する.
第三に、nextサーバはapiを呼び出し、ライセンスコードをサーバに渡します.
4番目に返されるJWTは、set-headerを介してcookieに格納される.errorが受信されると、errorメッセージがクライアントに送信される.

クライアントコード


  // 1.kakao에서 인증코드 받아오기
  const login = () => {
    window.Kakao.Auth.login({
      throughTalk: false,
      success: function (authObj) {
        onSuccess(authObj);
      },
      fail: function (err) {
        console.log('카카오에서 인증코드 받아오는 과정에서 오류발생', err);
        alert('카카오에서 인증코드 받아오는 과정에서 오류발생');
      },
    });
  };

  
  const onSuccess = async (res) => {
    // 2.인증코드를 받아오는데 성공하면 next서버로 인증코드를 보냄
    await axios
      .post('/api/loginKakao', {
        token_type: res.token_type,
        access_token: res.access_token,
        expires_in: res.expires_in,
        refresh_token: res.refresh_token,
        refresh_token_expires_in: res.refresh_token_expires_in,
      })
      .catch((err) => {
        console.log(err);
        alert('서버에서 오류발생');
      });
    
	//로그인 성공 후 이전 홈으로 이동
    router.push('/');
  };

apiサーバコード

import axios from 'axios';
import { LOGIN_WITH_KAKAO } from '/gql/_mutation';

export default async function loginKakao(req, res) {
  const {
    body: {
      token_type,
      access_token,
      expires_in,
      refresh_token,
      refresh_token_expires_in,
    },
  } = req;
	
  //우리 서버로 토큰을 넘기는 과정
  const { data } = await axios
    .post(process.env.NEXT_PUBLIC_API_SERVER, {
      query: LOGIN_WITH_KAKAO,
      variables: {
        access_token: access_token,
        expires_in: expires_in,
        refresh_token: refresh_token,
        refresh_token_expires_in: refresh_token_expires_in,
        token_type: token_type,
      },
    })
    .catch((err) => {
      res.status(500);
    });
  
  //쿠키에 저장
  res.setHeader(
    'Set-Cookie',
    `celebstock=${data.data.loginKakao.jwt}; path=/; domain=.celebstock.kr; Max-Age=${refresh_token_expires_in}`,
  );
  res.status(200).send();
}

補足すべき点

cookieにのみ保存され、cookieに属性が正しく指定されていません.httpOnlysamesiteのような属性は、後でさらに確認し、修正する必要がある場合があります.

References

  • Yaytomatoの声
  • araisのねじ