Domänenübergreifende OAuth-Popup-Sicherheit React.js

12

Ich bin daran interessiert, wie OAuth in React mit popup ( window.open) implementiert wird .

Zum Beispiel habe ich:

  1. mysite.com - Hier öffne ich das Popup.
  2. passport.mysite.com/oauth/authorize - Pop-up.

Die Hauptfrage ist, wie eine Verbindung zwischen window.open(Popup) und window.opener(wie bekannt, ist window.opener aufgrund der domänenübergreifenden Sicherheit null, daher können wir es nicht mehr verwenden) hergestellt wird.

window.openerwird entfernt, wenn Sie zu einem anderen Host navigieren (aus Sicherheitsgründen). Daran führt kein Weg vorbei. Die einzige Möglichkeit sollte darin bestehen, die Zahlung in einem Rahmen durchzuführen, wenn dies möglich ist. Das oberste Dokument muss sich auf demselben Host befinden.

Planen:

Geben Sie hier die Bildbeschreibung ein

Mögliche Lösungen:

  1. Überprüfen Sie ein geöffnetes Fenster mit setIntervalder hier beschriebenen .
  2. Mit Cross-Lagerung (nicht wert IMHO).

Was ist der am besten empfohlene Ansatz für 2019?

Wrapper for React - https://github.com/Ramshackle-Jamathon/react-oauth-popup

Arthur
quelle
2
Im Jahr 2019 ist die Unterstützung von localStorage viel besser. Ich würde mich für den localStorage-Ansatz entscheiden (beschrieben in stackoverflow.com/questions/18625733/… ), da dies keine wirkliche Problemumgehung zu sein scheint. Das übergeordnete Fenster muss nicht regelmäßig nach dem Status des untergeordneten Fensters suchen. setIntervalkönnte als Fallback für localStorage
Khanh TO
@ KhanhTO, ja, ich stimme Ihnen vollkommen zu localStorage, aber es funktioniert nur für dieselbe Domain, so dass es in meinem Zustand nicht funktioniert
Arthur
2
Nachdem Sie mit OAuth fertig sind, wird das untergeordnete Fenster zurück zu Ihrer Domain umgeleitet. Sie befinden sich jetzt mit dem übergeordneten
Element
@ KhanhTO, hm, das ist eine großartige Idee! Ich hätte es
Arthur
1
Es wäre sogar noch besser, wenn der Browser window.openernach der Umleitung zurück zu unserer Domain wiederhergestellt würde , aber dies ist nicht der Fall
Khanh

Antworten:

6

Vorgeschlagen von Khanh TO . OAuth-Popup mit localStorage. Basierend auf React-Oauth-Popup .

Planen:

Geben Sie hier die Bildbeschreibung ein

Code:

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;
Arthur
quelle
3

Ich habe einmal ein Problem mit meinem oauth-Anmeldefluss mit dem Fehler window.open/window.opener auf ms-edge festgestellt

Mein Fluss vor diesem Problem war

  • Klicken Sie beim Anmelden auf ein Popup
  • Nach erfolgreicher Anmeldung leitet die oauth-App zur Seite meiner Domain weiter
  • Dann rufe ich eine Funktion des übergeordneten Fensters von mit im Popup (window.opener.fn) mit Daten von oauth response auf und das übergeordnete Fenster schließe dann das untergeordnete Popup-Fenster

Mein Fluss nach diesem Problem war

  • Klicken Sie beim Anmelden auf ein Popup
  • Erstellen Sie ein setinterval für den Fall (window.opener ist undefiniert)
  • Nach erfolgreicher Anmeldung leitet die oauth-App zur Seite meiner Domain weiter
  • Überprüfen Sie, ob window.opener verfügbar ist, und führen Sie dann # 3 aus dem obigen Ablauf und clearInterval aus
  • Wenn window.opener nicht verfügbar ist, versuche ich, da ich mich auf meiner Domainseite befinde, localstorage festzulegen und den localstorage aus der Funktion setInterval im übergeordneten Fenster heraus zu lesen. Löschen Sie dann localstorage und setInterval und fahren Sie fort.
  • (aus Gründen der Abwärtskompatibilität) Wenn localstorage ebenfalls nicht verfügbar ist, setzen Sie ein clientseitiges Cookie mit den Daten mit einer kurzen Ablaufzeit (5-10 Sekunden) und versuchen Sie, das Cookie (document.cookie) in der Funktion setInterval im übergeordneten Fenster und zu lesen Vorgehen.
Shah92
quelle