OAuth 팝업 도메인 간 보안 React.js


12

팝업 ( window.open)을 사용하여 React에서 OAuth를 구현하는 방법에 관심이 있습니다.

예를 들어

  1. mysite.com — 팝업을 여는 곳입니다.
  2. passport.mysite.com/oauth/authorize — 팝업.

주요 질문은 window.open(팝업)과 (팝업) 사이의 연결을 만드는 방법입니다. window.opener도메인 간 보안으로 인해 window.opener가 null이므로 더 이상 사용할 수 없습니다.

window.opener는 (보안상의 이유로) 다른 호스트로 이동 할 때마다 제거, 주위 방법이 없습니다. 가능한 경우 프레임에서 결제하는 것이 유일한 옵션입니다. 최상위 문서는 동일한 호스트에 있어야합니다.

계획:

여기에 이미지 설명을 입력하십시오

가능한 해결책:

  1. 여기에setInterval 설명 된대로 열린 창을 확인 하십시오 .
  2. 사용 크로스 저장 (는 이럴하지 가치).

2019 년에 가장 권장되는 방법은 무엇입니까?

반작용에 대한 래퍼 - https://github.com/Ramshackle-Jamathon/react-oauth-popup


2
2019 년에는 localStorage 지원이 훨씬 향상되었습니다. 해결 방법이 많지 않으므로 localStorage 접근 방식 ( stackoverflow.com/questions/18625733/…에 설명되어 있음 )을 사용합니다. 부모 창은 자식 창 상태를 주기적으로 확인할 필요가 없습니다. setIntervallocalStorage의 대체물로 사용될 수 있음
Khanh TO

@KhanhTO, 네,에 대해 완전히 동의 localStorage하지만 같은 도메인에서만 작동하므로 내 상태에서는 작동하지 않습니다
Arthur

2
OAuth를 마치면 자식 창이 다시 도메인으로 리디렉션되고 부모와 같은 도메인에 있습니다
Khanh TO

@KhanhTO, 흠, 이것은 좋은 생각입니다! 알았어 야
Arthur

1
window.opener도메인으로 다시 리디렉션 한 후 브라우저가 복원되는 것이 더 나을 수도 있지만, 그렇지 않습니다
Khanh TO

답변:


6

Khanh TO가 제안합니다 . localStorage를 사용한 OAuth 팝업 를 기반으로 반응-OAuth를-팝업 .

계획:

여기에 이미지 설명을 입력하십시오

암호:

oauth-popup.tsx:

import React, {PureComponent, ReactChild} from 'react'

type Props = {
  width: number,
  height: number,
  url: string,
  title: string,
  onClose: () => any,
  onCode: (params: any) => any,
  children?: ReactChild,
}

export default class OauthPopup extends PureComponent<Props> {

  static defaultProps = {
    onClose: () => {},
    width: 500,
    height: 500,
    url: "",
    title: ""
  };

  externalWindow: any;
  codeCheck: any;

  componentWillUnmount() {
    if (this.externalWindow) {
      this.externalWindow.close();
    }
  }

  createPopup = () => {
    const {url, title, width, height, onCode} = this.props;
    const left = window.screenX + (window.outerWidth - width) / 2;
    const top = window.screenY + (window.outerHeight - height) / 2.5;

    const windowFeatures = `toolbar=0,scrollbars=1,status=1,resizable=0,location=1,menuBar=0,width=${width},height=${height},top=${top},left=${left}`;

    this.externalWindow = window.open(
        url,
        title,
        windowFeatures
    );

    const storageListener = () => {
      try {
        if (localStorage.getItem('code')) {
          onCode(localStorage.getItem('code'));
          this.externalWindow.close();
          window.removeEventListener('storage', storageListener);
        }
      } catch (e) {
        window.removeEventListener('storage', storageListener);
      }
    }

    window.addEventListener('storage', storageListener);

    this.externalWindow.addEventListener('beforeunload', () => {
      this.props.onClose()
    }, false);
  };

  render() {
    return (
      <div onClick={this.createPopup)}>
        {this.props.children}
      </div>
    );
  }
}

app.tsx

import React, {FC} from 'react'

const onCode = async (): Promise<undefined> => {
  try {
    const res = await <your_fetch>
  } catch (e) {
    console.error(e);
  } finally {
    window.localStorage.removeItem('code'); //remove code from localStorage
  }
}

const App: FC = () => (
  <OAuthPopup
    url={<your_url>}
    onCode={onCode}
    onClose={() => console.log('closed')}
    title="<your_title>">
    <button type="button">Enter</button>
  </OAuthPopup>
);

export default App;

3

MS-Edge의 window.open/window.opener 버그로 oauth 로그인 흐름에 문제가 발생했습니다.

이 문제 이전의 흐름은

  • 로그인 버튼을 클릭하면 팝업 열기
  • 로그인에 성공하면 oauth 앱이 내 도메인 페이지로 리디렉션됩니다
  • 그런 다음 oauth 응답의 데이터와 부모 창에서 팝업 (window.opener.fn)에서 부모 창의 기능을 호출 한 다음 자식 팝업 창을 닫습니다.

이 문제 이후의 흐름은

  • 로그인 버튼을 클릭하면 팝업 열기
  • 경우에 setinterval을 작성하십시오 (window.opener가 정의되지 않음)
  • 로그인에 성공하면 oauth 앱이 내 도메인 페이지로 리디렉션됩니다
  • window.opener가 사용 가능한지 확인한 다음 위의 흐름에서 3 번을 수행하고 clearInterval
  • window.opener를 사용할 수없는 경우 내 도메인 페이지에 있으므로 localstorage를 설정하고 부모 창의 setInterval 함수 내부에서 localstorage를 읽으려고 시도하고 localstorage 및 setInterval을 지우고 진행하십시오.
  • (이전 버전과의 호환성을 위해) 로컬 저장소도 사용할 수없는 경우 짧은 만료 (5-10 초) 시간으로 데이터를 사용하여 클라이언트 쪽 쿠키를 설정하고 부모 창의 setInterval 함수 내에서 쿠키 (document.cookie)를 읽으십시오. 발하다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.