[Next.js] Github Login

Eunhye Kim·2024년 1월 22일
0

Next.js

목록 보기
10/14

Nex.js에서 어떻게 Gihub Login을 할 수 있는지 작성해보았다.

API 명세서

Github 로그인 과정

  1. github으로 로그인해서 특정 사용자에 대한 토큰에 액세스한다.
  2. github에서 준 code라는 것을 가져와 프록시 서버에서 AccessToken을 얻는다.
  3. AccessToken을 클라이언트로 다시 보낸다.
  4. AccessToken과 Github API를 통해서 User data를 얻는다.

OAuth application 만들기

프로필 -> settings -> Developer Settings -> OAuth Apps

Authorization callback URL은 github에서 로그인 후 어디로 돌아가는지 지정해주는 곳이다.

다 만들면 Client IDClient secrets을 얻을 수 있다. 얻은 다음 이 2가지를 .env 파일에 추가해주면 된다.

NEXT_PUBLIC_GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=

1. github login (code 얻기)

const Login = () => {
  // github으로 로그인 code 받기
  // 예) code=4bf42ada452ddda0e66d
  const loginWithGithub = () => {
    window.location.assign(
      `https://github.com/login/oauth/authorize?client_id=${process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID}`
    );
  };

return (	
  <button onClick={loginWithGithub}>login</button>
 )
}

export default Login;

로그인 링크를 활용해서 로그인에 액세스 할 수 있다.
그러면 URL에 코드 매개변수가 생긴다.

http://localhost:3000/login?code=a70b2a78e6e9144241e8

이렇게 하면 코드 매개변수를 가져올 수 있다.

  useEffect(() => {
    const queryString = window.location.search;
    const urlParams = new URLSearchParams(queryString);
    const codeParam = urlParams.get("code");
    console.log(codeParam);

  }, []);

이제 이 code를 사용해서 github api와 통신해서 AccessToken을 얻을 수 있게된다.

2. AccessToken을 얻기

api 폴더 안에 getAccessToken폴더를 생성한다.

url에서 code를 가져와 https://github.com/login/oauth/access_token를 통해서 AccessToken를 호출한다.
그리고 가져온 AccessToken를 NextResponse로 클라이언트에게 보내준다.

import { NextRequest, NextResponse } from 'next/server';

export const GET = async (request: NextRequest, response: NextResponse) => {
  try {
    const requestUrl = new URL(request.nextUrl);
    const code = requestUrl.searchParams.get('code');

    const baseUrl = 'https://github.com/login/oauth/access_token';
    const config = {
      client_id: process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID || '',
      client_secret: process.env.GITHUB_CLIENT_SECRET || '',
      code: code || '',
    };
    const params = new URLSearchParams(config).toString();
    const finalUrl = `${baseUrl}?${params}`;

    const data = await fetch(finalUrl, {
      method: 'POST',
      headers: {
        Accept: 'application/json',
      },
    });

    const json = await data.json();

    return new NextResponse(JSON.stringify(json), {
      status: 200,
      headers: {
        'Access-Control-Allow-Origin': '*',
      },
    });
  } catch (error) {
    console.log(error);
    return new NextResponse('Internal Server Error', { status: 500 });
  }
};

3. User data 얻기

api 폴더 안에 getUserData 폴더를 생성한다.
https://api.github.com/usergithub api를 통해서 유저 데이터를 가져올 수 있다.

import { NextResponse, NextRequest } from 'next/server';

export const GET = async (request: NextRequest, response: NextResponse) => {
  try {
    // Authorization 헤더를 가져오기
    const authorizationHeader = request.headers.get('Authorization');
    console.log(authorizationHeader);
    if (!authorizationHeader) {
      return new NextResponse('Authorization header is missing', {
        status: 401,
      });
    }

    const data = await fetch('https://api.github.com/user', {
      method: 'GET',
      headers: {
        Authorization: authorizationHeader,
      },
    });

    const json = await data.json();

    return new NextResponse(JSON.stringify(json), {
      status: 200,
      headers: {
        'Access-Control-Allow-Origin': '*',
      },
    });
  } catch (error) {
    console.log(error);
    return new NextResponse('Internal Server Error', { status: 500 });
  }
};

프론트 코드

"use client";

import axios from "axios";
import React, { useEffect, useState } from "react";

interface UserDataProps {
  id: number;
  login: string;
}

const Login = () => {
  const [isRerender, setIsRerender] = useState(false);
  const [userDate, setUserData] = useState<UserDataProps>({ id: 0, login: '' });

  const token = typeof window !== 'undefined' && localStorage.getItem("accessToken");

  // github으로 로그인 code 받기
  // 예) code=4bf42ada452ddda0e66d
  const loginWithGithub = () => {
    window.location.assign(
      `https://github.com/login/oauth/authorize?client_id=${process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID}`
    );
  };

  const getUserData = async () => {
    const config = {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    };

    const userResponse = await axios.get(
      `/api/getUserData`,
      config
    );
    
    const user = userResponse.data;
    setUserData(user);
  };

  useEffect(() => {
    // url에 있는 code 가져오기
    const queryString = window.location.search;
    const urlParams = new URLSearchParams(queryString);
    const codeParam = urlParams.get("code");
    console.log(codeParam);
    if (codeParam && token === null) {

      const getAccessToken = async () => {
        await fetch(
          `/api/getAccessToken?code=${codeParam}`,
          { method: "GET" }
        )
          .then((response) => {
            return response.json();
          })
          .then((data) => {
            console.log(data);
            if (data.access_token) {
              localStorage.setItem("accessToken", data.access_token);
              setIsRerender(!isRerender);
            }
          });
      };
      getAccessToken();
    }
  }, []);


  return (

    <div>
      {typeof window !== "undefined" && localStorage.getItem("accessToken") ? (
        <div>
          <p>accessToken이 있습니다</p>
          <button
            onClick={() => {
              typeof window !== "undefined" &&
                localStorage.removeItem("accessToken");
              setIsRerender(!isRerender);
            }}
          >
            Log out
          </button>
          <div>
            <p>Github User Data</p>
            <button onClick={getUserData}>Get Data</button>
            <div>
              <p>ID: {userDate.login}</p>
            </div>
          </div>
        </div>
      ) : (
        <div>
          <p>로그인 해주세요</p>
          <button onClick={loginWithGithub}>login</button>
        </div>
      )}

    </div>
  );
};

export default Login;
profile
개발에 몰두하며 성장하는 도중에 얻은 인사이트에 희열을 느낍니다.

0개의 댓글