React.js di sicurezza tra domini popup OAuth


12

Sono interessato a come implementare OAuth in React usando popup ( window.open).

Ad esempio ho:

  1. mysite.com - è qui che apro il popup.
  2. passport.mysite.com/oauth/authorize - apparire.

La domanda principale è come creare una connessione tra window.open(popup) e window.opener(come è noto, window.opener è nullo a causa della sicurezza tra domini, quindi non possiamo più usarlo).

window.openerviene rimosso ogni volta che si passa a un host diverso (per motivi di sicurezza), non è possibile aggirarlo. L'unica opzione dovrebbe essere quella di effettuare il pagamento in un frame, se possibile. Il documento principale deve rimanere sullo stesso host.

Schema:

inserisci qui la descrizione dell'immagine

Possibili soluzioni:

  1. Controllare una finestra aperta usando setIntervaldescritto qui .
  2. Utilizzo del cross-storage (non ne vale la pena imho).

Quindi qual è l'approccio migliore raccomandato nel 2019?

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


2
Nel 2019, il supporto di localStorage è molto meglio. Andrei con l'approccio localStorage (descritto in stackoverflow.com/questions/18625733/… ) in quanto non sembra una soluzione alternativa. La finestra padre non deve controllare periodicamente lo stato della finestra figlio. setIntervalpotrebbe essere utilizzato come fallback per localStorage
Khanh TO

@KhanhTO, sì, sono completamente d'accordo con te localStorage, ma funziona solo per lo stesso dominio, quindi non funziona nelle mie condizioni
Arthur

2
Dopo aver finito con OAuth, la finestra figlio viene reindirizzata al tuo dominio, ora sei nello stesso dominio con il genitore
Khanh TO

@KhanhTO, hm, questa è un'ottima idea! Avrei dovuto saperlo ...
Arthur,

1
Sarebbe ancora meglio se il browser ripristinasse window.openerdopo il reindirizzamento al nostro dominio, ma non è così
Khanh AL

Risposte:


6

Suggerito da Khanh TO . Popup OAuth con localStorage. Basato su reazioni-oauth-popup .

Schema:

inserisci qui la descrizione dell'immagine

Codice:

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

Una volta ho riscontrato un problema sul mio flusso di accesso oauth con bug window.open/window.opener su ms-edge

Il mio flusso prima di questo problema era

  • Al pulsante di accesso fai clic su Apri un popup
  • Dopo aver effettuato l'accesso, l'app oauth reindirizza alla pagina del mio dominio
  • Quindi chiamo una funzione della finestra padre da con nel popup (window.opener.fn) con i dati dalla risposta automatica e la finestra padre quindi chiudo la finestra popup figlio

Il mio flusso dopo questo problema è stato

  • Al pulsante di accesso fai clic su Apri un popup
  • Crea un setinterval nel caso (window.opener non è definito)
  • Dopo aver effettuato l'accesso, l'app oauth reindirizza alla pagina del mio dominio
  • Controlla se window.opener è disponibile, quindi esegui # 3 dal flusso sopra e cancella Intervallo
  • Se window.opener non è disponibile, dato che sono nella pagina dei miei domini, provo a impostare localstorage e provo a leggere il localstorage dall'interno della funzione setInterval nella finestra padre, quindi azzerare il localstorage e setInterval e procedere.
  • (per compatibilità con le versioni precedenti) Se anche localstorage non è disponibile, imposta un cookie lato client con i dati con una scadenza breve (5-10 sec) e prova a leggere il cookie (document.cookie) all'interno della funzione setInterval nella finestra principale e procedere.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.