OAuth 팝업 ~ 실용 가이드

에서 우리는 처음부터 GitHub OAuth 흐름을 구현했습니다. 그러나 인증 페이지로의 리디렉션은 하드 리디렉션을 통해 이루어졌으며 특히 SPA를 처리할 때 바람직하지 않은 동작입니다.

야생에서 OAuth가 팝업 접근 방식을 사용하는 것은 놀라운 일이 아닙니다. 해결하기 쉬운 리디렉션은 여전히 ​​발생하지만 용해되기 전에 access_token를 기본 탭으로 다시 전달하는 생성된 팝업에서 발생합니다.


This post is to be read as an enhancement of the previous one, so it has a slightly higher pacing. If in doubt, please refer to the accompanying code or the .



<a id="oauth-github-popup">Authenticate via GitHub (popup)</a>
    <script>
      const CLIENT_ID = "Iv1.395930440f268143";

      const url = new URL("/login/oauth/authorize", "https://github.com");
      url.searchParams.set("client_id", CLIENT_ID);

      const cta = document.getElementById("oauth-github-popup");
      cta.setAttribute("href", url);
      cta.addEventListener("click", (event) => {
        event.preventDefault();

   // ...


접근성을 위해 GitHub OAuth 권한 부여 링크를 조합하고 <a> 태그에 첨부합니다. 그러나 클릭을 수신하여 기본 하드 리디렉션을 방지합니다.

다음으로 Web APIwindow.open를 사용하여 팝업을 생성합니다.width , height 등을 포함하는 문자열을 세 번째 매개변수로 기대합니다.

개인적으로 나는 위에서 언급한 문자열로 변환되는 개체의 명시성을 선호합니다.

top and left properties have the auto value - that's not in the specifications of the API, in fact it is interpreted by the following snippet as an instruction to place in the center of the relative axis. Basically if both are auto the popup will always spawn in the center, even if you change width or height.



// in the 'click' eventListener callback
const features = {
  popup: "yes",
  width: 600,
  height: 700,
  top: "auto",
  left: "auto",
  toolbar: "no",
  menubar: "no",
};

const strWindowsFeatures = Object.entries(features)
  .reduce((str, [key, value]) => {
    if (value == "auto") {
      if (key === "top") {
        const v = Math.round(
          window.innerHeight / 2 - features.height / 2
        );
        str += `top=${v},`;
      } else if (key === "left") {
        const v = Math.round(
          window.innerWidth / 2 - features.width / 2
        );
        str += `left=${v},`;
      }
      return str;
    }

    str += `${key}=${value},`;
    return str;
  }, "")
  .slice(0, -1); // remove last ',' (comma)

window.open(url, "_blank", strWindowsFeatures);


따라서 사용자는 멋진 GitHub 버튼을 클릭하고 팝업을 통해 인증합니다. 그러나 다음 기능을 가진 일부 페이지로 다시 보내도록 서버에 지시하는 것이 중요합니다.
  • 팝업인지 확인합니다
  • .
  • 쿼리 매개변수
  • 에서 추정access_token
  • 상위 창으로 디스패치( window.opener )
  • Close itself

  • 기억하세요: OAuth 콜백

    사용자가 인증되면 GitHub는 OAuth 앱/GitHub 앱 생성에서 지정한 콜백 URL로 리디렉션합니다. 이에 대해서는 previous post 에서 자세히 설명합니다.



    server.get("/oauth/github/login/callback", async (request, reply) => {
      const { code } = request.query;
    
      const exchangeURL = new URL("login/oauth/access_token", "https://github.com");
      exchangeURL.searchParams.set("client_id", process.env.CLIENT_ID);
      exchangeURL.searchParams.set("client_secret", process.env.CLIENT_SECRET);
      exchangeURL.searchParams.set("code", code);
    
      const response = await axios.post(exchangeURL.toString(), null, {
        headers: {
          Accept: "application/json",
        },
      });
    
      const { access_token } = response.data;
    
      const redirectionURL = new URL("popup", "http://localhost:3000");
      redirectionURL.searchParams.set("access_token", access_token);
    
      reply.status(302).header("Location", redirectionURL).send();
    });
    
    server.get("/popup", (request, reply) => {
      return reply.sendFile("popup.html");
    });
    


    클라이언트는 /popup로 리디렉션되고 popup.html가 표시됩니다.

    <script>
        if (window.opener == null) window.location = "/";
    
        const access_token = new URL(window.location).searchParams.get(
        "access_token"
        );
    
        window.opener.postMessage(access_token);
    
        window.close();
    </script>
    

    window.openernull를 통해 페이지가 열리지 않은 경우 window.open입니다. 이렇게 하면 사용자가 /popup 로 직접 라우팅하면 / 로 리디렉션됩니다.

    계산은 최소이며 꽤 빠릅니다. 스피너를 보여주는 것은 당신에게 좋은 일만 할 수 있습니다.



    SPA 및 해당 라우터 솔루션은 경로와 연결할 수 있는 몇 가지 방법beforeEnter을 제공합니다. 더 나은 경험을 제공하기 위해 거기에서 window.opener 값을 확인할 수 있습니다.


    거의 완료되었습니다! 팝업이 access_token를 부모에게 다시 돌진하지만 듣고 있지 않습니다!

    window.addEventListener("message", receiveMessage, false);
    
    function receiveMessage(event) {
      if (event.origin !== window.location.origin) {
        console.warn(`Message received by ${event.origin}; IGNORED.`);
        return;
      }
    
      const access_token = event.data;
    }
    


    예방책으로 다른 출처에서 오는 메시지는 무시하십시오. 코드에서 removeEventListener를 고려하십시오.access_token를 어딘가에 저장합니다. 이 시점에서 흐름은 .

    GitHub 앱 설치 및 권한 변경에도 이 패턴을 사용하는 것을 막을 수는 없습니다.


    관련 게시물

  • cover image

  • 콘택트 렌즈




  • GitHub
  • 좋은 웹페이지 즐겨찾기