OAuth로 인증코드와 액세스 토큰 받기

mia·2024년 2월 22일
0

Snappi 서비스를 개발하면서 OAuth로 인증코드와 액세스 토큰을 각각 받아오는 작업을 담당했다.

Google 개발자 페이지에서는 OAuth를 통해 사용자인증을 하고 API에 접근하는 과정을 아래와 같이 안내하고 있다.

1. 유저가 소셜 로그인을 하면
2. 구글 서버에서 인증 코드를 제공.
3. 해당 인증코드로 토큰을 구글 서버에 요청해
4. 토큰을 가지고 Google API에 토큰을 전송해주면 된다.

기능별 프로세스

<로그인>

  1. 프론트에서 인증코드를 발급받아 백에 전달
  2. 백에서 해당 코드로 구글에서 액세스 토큰을 발급
  3. 내부 토큰으로 변환하여 프론트로 전달

<유튜브 채널 연동>

  1. 프론트에서 액세스토큰을 발급받아 백에 전달
  2. 백에서 해당 액세스토큰으로 유튜브 채널 API를 받아와서 가공

인증코드 & 액세스토큰 발급 방법

  1. Google API Console에서 사용자 인증 정보 가져오기
    a. Google API Console 이동 > 사용자 인증 정보
    b. 동의 화면 구성(없다면)

    c. 사용자 인증정보 만들기 > OAuth Client Id
    + 애플리케이션 유형 : 데스크톱 앱
    + 승인된 자바스크립트 원본
    - 로컬: http://localhost:3000
    - 운영계: 운영계 주소

여기까지 완료하면 클라이언트 ID와 클라이언트 보안 비밀번호를 얻는다.

  1. Google 승인 서버에서 인증 코드 가져오기
    자바스크립트를 통해 인증 코드, 액세스토큰을 요청하기 위해서는 구글에서 제공하는 엔드포인트로 리디렉션을 해주어야한다.
  • 엔드포인트: https://accounts.google.com/o/oauth2/v2/auth
  • 추가적인 정보는 쿼리스트링으로 제공할 수 있다.
    • client_id: Google API Console에서 발급받은 클라이언트 ID

    • redirect_uri: 사용자가 구글에서 인증을 한 후 결과를 받을 수 있는 주소이다.

      나의 경우 ‘/signin’ 페이지에 구글 로그인 버튼을 만들어주었고 ’/check-signup’ 페이지로 리다이렉트 시켰다. 이 페이지에서는 인증 절차를 거친 후 db에 해당 유저가 있는지 파악해서 정보가 있다면 로그인, 정보가 없다면 회원가입으로 이동시키는 분기처리를 해준다.

      이 redirect_uri는 구글 콘솔 사용자 인증 정보의 승인된 리디렉션 URI와 정확히 일치해야한다.

      난 이때 몇가지 삽질(?)을 했는데

    1. http 이슈

      로컬로 할 땐 문제 없었지만 http 도메인은 보안상 등록되지 않는다. 개발계 주소는 http 로 되어있어 등록할 수 없었고 이 문제가 해결되기까지 테스트를 하지 못했다. (나중에 OCI에 운영계를 올려서 https 도메인으로 테스트 할 수 있었다.)

    2. 후행 슬래시!!
      구글 개발자 페이지에 redirect_uri 부분을 보며 아래와 같이 써있다.
      "http 또는 https 스키마, 대소문자, 후행 슬래시('/')가 모두 일치해야 합니다." 쿼리스트링에도 리디렉션 URI에도 후행 슬래시를 붙이지 않았느데 이때 계속 redirect_uri_mismatch 오류가 발생했다.
      나중에 알고 보니 Next.js에서는 redirect 시 후행 슬래시가 붙어서 이동한다고 한다.
      [참고] Next.js 공식문서 - trailingSlash
      그러니 Next.js를 사용한다면 후행 슬래시를 꼭 붙일 것!
      ex)’https://.../check-signup/google/’
      하지만 액세스 토큰 발급 시 후행 슬래시를 제거해야만 가져와졌다...
      ex)‘https://.../content-upload/get-channel-id’

    • response_type: ‘code’ or ‘token’
    • scope: OAuth 2.0 범위에서 요청할 수 있는 scope들을 확인 할 수 있으며 이 내용을 통해 유저에게 로그인시 필요에 따라 동의를 받을 수 있다.
    • prompt(선택): select_account (유저가 로그인할때마다 계정을 선택하도록 만들고 싶었기 때문에)

예시 (인증 코드)


// getAuthCode.ts

import qs from 'querystring';

export const getGoogleAuthCode = () => {
	const OAUTH_HOST = "https://accounts.google.com/o/oauth2/v2/auth";
	const client_id = // 클라이언트 아이디;
	const redirect_uri = 'https://{domain}/check-signup/google/';
	const response_type = 'code';
	const scope = 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile';
	const prompt = 'select_account';

	const AUTHORIZE_URI = `${OAUTH_HOST}?${qs.stringify({
		client_id,
		redirect_uri,
		response_type,
		scope,
		prompt
	})}`

	window.location.hreft = ATHORIZE_URI;
};

// check-signup.ts

...
const searchParams = useSearchParams();
// 인증 코드는 redirect_uri의 쿼리스트링으로 받을 수 있다.
const authCode = searchParams.get('code');

예시 (액세스 토큰)


// channel-upload.ts

const getGoogleToken = () => {
	const OAUTH_HOST = "https://accounts.google.com/o/oauth2/v2/auth";
	const client_id = // 클라이언트 아이디;
	const redirect_uri = 'https://{도메인}/content-upload/get-channel-id';
	const response_type = 'token';
	const scope = "https://www.googleapis.com/auth/youtube https://www.googleapis.com/auth/youtube.readonly https://www.googleapis.com/auth/youtubepartner";
	const prompt = 'select_account';

	const AUTHORIZE_URI = `${OAUTH_HOST}?${qs.stringify({
		client_id,
		redirect_uri,
		response_type,
		scope,
		propmt 
	})}`;

	window.location.href = AUTHORIZE_URI;
}

export defualt function GetChannelId() {
	const router = useRouter();

// access_token은 redirect_uri의 해시프래그먼트로 받아볼 수 있다.
//https://oauth2.example.com/callback#access_token=4/P7q7W91&token_type=Bearer&expires_in=3600
  
	useEffect(() => {
		window?.localStorage.setItem(
			'googleToken',
			window?.location.href?.split('access_token=')[1]?.split('&')[0]
		);
		router.push('/content-upload/channel-upload');
	}, [router]);

	return <></>
}

이후 나머지 처리를 하면 된다!

profile
노 포기 킾고잉

0개의 댓글