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年に推奨される最善のアプローチは何でしょうか?

Reactのラッパー-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、うーん、これは素晴らしいアイデアです!私は知っているべき
アーサー

1
ブラウザーwindow.openerがドメインにリダイレクトされた後に復元した方がよいでしょうが、そうではありません
Khanh TO

回答:


6

カーンTOによって提案されました。localStorageを使用したOAuthポップアップ。react-oauth-popupに基づいています。

スキーム:

ここに画像の説明を入力してください

コード:

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アプリは私のドメインのページにリダイレクトします
  • 次に、親ウィンドウの関数をポップアップ(window.opener.fn)でoauth応答からのデータで呼び出し、親ウィンドウで子ポップアップウィンドウを閉じます

この問題の後の私の流れは

  • ログインボタンをクリックしてポップアップを開く
  • 場合に応じてsetintervalを作成します(window.openerは未定義です)
  • ログインに成功した後、oauthアプリは私のドメインのページにリダイレクトします
  • window.openerが使用可能かどうかを確認し、上記のフローとclearIntervalから#3を実行します。
  • window.openerが使用できない場合、ドメインページにいるので、localstorageを設定し、親ウィンドウのsetInterval関数内からlocalstorageを読み取ってから、localstorageとsetIntervalをクリアして続行します。
  • (下位互換性のため)localstorageも使用できない場合は、クライアント側のCookieに有効期限が短い(5〜10秒)時間のデータを設定し、親ウィンドウのsetInterval関数内のCookie(document.cookie)を読み取ってください。続行します。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.