Skip to content

Chrome Extension에서 OAuth 연동

qkrrlgh519 edited this page Dec 19, 2020 · 4 revisions

1. 일반적인 웹 애플리케이션의 OAuth 흐름

  1. 애플리케이션 구현 ( + Callback URL에 해당되는 페이지 구현)
  2. Resource Server에 Hompage URL(Service URL)과 Callback URL(Redirect URL) 등록 후 Client ID, Client Secret 정보 획득
  3. Resource Owner가 'OAuth 인증 버튼' 클릭 (Client ID와 Callback URL을 조합한 주소로 이동)
  4. 로그인 여부에 따라 로그인 화면이 나오고, 로그인 성공하면 Resource Server는 요청 URL의 Client ID와 Callback URL을 검사 진행
  5. 요청 URL 검사 통과시 Resource Owner에게 scope에 해당하는 권한을 Client에게 부여할 것인지 팝업을 띄운다. (예를 들면, 이메일, 성별, 이름 정보 제공 동의)
  6. Resource Owner가 권한 허용을 하면 Rersource Server에 scope 정보가 저장되고 code값을 포함한 Callback URL로 리다이렉트 진행
  7. Client 쪽에서 미리 구현해놓은 Callback URL에서 code를 받고 Client의 Server로 보내서 인증 처리 진행
    • Server 쪽에서는 Code를 받고, Client ID와 Client Secret를 조합해 Resource Server에 Access Token을 요청한다.
    • Resource Server에서 Code, Client ID, Client Secret 값을 검증 후 통과하면 Access Token을 발급한다.
    • Server 쪽에서는 Access Token을 받고, Resource Server에 Resource를 요청하고, 응답받는다.

2. Chrome Extension 환경에서의 문제점

  • (0번) Callback URL이 없으며, 해당 페이지를 구현할 수 없음
  • (1번) Extension 환경이라 공식적인 Homepage URL을 가질 수 없어서, Homepage URL과 Callback URL을 명시할 수가 없음
  • 위의 문제로 인해서 로그인 화면을 띄웠을 때 입력 화면이 아닌 오류 화면 출력 (올바르지 않은 URL)

3. Chrome Extension 환경에서의 해결 방법

  • Identity API의 launchWebAuthFlow (details: WebAuthFlowDetails, callback: function)
    • 첫번째 매개변수로 들어간 객체의 URL을 이용해서 인증 흐름을 진행합니다.
    • 이 함수는 Google이 아닌 다른 서비스의 인증 흐름을 가능하게 해줍니다.
    • https://.chromiumapp.org와 일치하는 URL로 redirect하면 창이 닫히고, 최종 redirect URL이 callback 함수로 전달된다.
    • callback 함수는 redirect URL을 매개변수로 받습니다.
    (responseURL: string) => {...}
  • IdentityAPI의 launchWebAuthFlow를 활용하면 URL문제와 상관없이 OAuth를 구현할 수 있습니다.

4. 전체 소스 코드

   chrome.identity.launchWebAuthFlow(
      {
        url: `https://nid.naver.com/oauth2.0/authorize
						?response_type=code
						&client_id=${CLIENT_ID}
						&redirect_uri=${REDIRECT_URL}
						&state=${STATE}`,
        interactive: true,
      },

      function (redirect_url) {
        const code = redirect_url.split('code=')[SECOND_ELEM_INDEX].split('&')[FIRST_ELEM_INDEX];
        const body = { code: code };
        fetch('${서버쪽 인증 관련 API URL}', {
          method: 'POST',
          body: JSON.stringify(body),
          headers: {
            'Content-Type': 'application/json',
          },
        })
          .then((res) => res.json())
          .then((data) => {
            // 서버쪽에서 인증 후 받은 값 처리하는 로직
          })
          .catch((error) => {
            sendResponse({ error: '로그인 에러' });
          });
      }
    );

💒 Home

Home

📆 Planning

📋 요구 사항

📑 프로젝트 설계

📓 Api 명세서

📖 제품 백로그

📺 화면 기획서

📽️ Project

📖 도움말

📷 실행 화면

⚒️ 기술 스택

⚙️ 기술 특장점

✔️ Team Rule

그라운드 룰

☑️ 깃허브 사용 규칙

코딩 컨벤션 규칙

📝 Progress

🌿 1주차 Progress
☘️ 2주차 Progress
🍀 3주차 Progress
🍁 4주차 Progress
🌲 5주차 Progress

📚 학습 정리 공유

🛠️ 기술 관련 공유

Clone this wiki locally