OAuth спливаюча міждоменна безпека React.js


12

Мене цікавить, як реалізувати OAuth в React за допомогою popup ( window.open).

Наприклад, у мене є:

  1. mysite.com - саме тут я відкриваю спливаюче вікно.
  2. passport.mysite.com/oauth/authorize - вискочити.

Головне питання - як створити з'єднання між window.open(спливаючим) та window.opener(як відомо, window.opener є нульовим через захист міждоменів, тому ми більше не можемо ним користуватися).

window.openerвидаляється кожного разу, коли ви переходите до іншого хоста (з міркувань безпеки), його немає. Єдиний варіант повинен здійснювати оплату в рамках, якщо це можливо. Верхній документ повинен залишатися на тому ж хості.

Схема:

введіть тут опис зображення

Можливі рішення:

  1. Перевірте відкрите вікно, використовуючи setIntervalописане тут .
  2. Використання перехресного зберігання (не варто того, що імхо).

То який найкращий рекомендований підхід у 2019 році?

Обгортка для React - https://github.com/Ramshackle-Jamathon/react-oauth-popup


2
У 2019 році підтримка локального сховища значно краща. Я б підходив до підходу localStorage (описаного в stackoverflow.com/questions/18625733/… ), оскільки це не виглядає як багато вирішення. Батьківському вікну не потрібно періодично перевіряти стан дочірнього вікна. setIntervalможе бути використаний як резервний
Хан

@KhanhTO, так, я повністю з вами згоден localStorage, але він працює лише для одного домену, тому він не працює в моєму стані
Артур

2
Після закінчення роботи з OAuth дочірнє вікно буде переспрямоване назад до вашого домену, ви перебуваєте в тому самому домені зараз з батьком
Khanh ДО

@KhanhTO, гм, це чудова ідея! Я повинен був знати ..
Артур

1
Ще краще було б, якби браузер відновився window.openerпісля перенаправлення назад до нашого домену, але це не так
Хань ДО

Відповіді:


6

Запропонував Khanh TO . Спливаюче вікно 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

Я одного разу зіткнувся з проблемою в потоці входу в oauth з помилкою window.open/window.opener на ms-edge

Моя течія до цього випуску була

  • На кнопці входу натисніть кнопку відкрити спливаюче вікно
  • Після успішного входу в програму oauth перенаправлення на сторінку мого домену
  • Тоді я викликаю функцію батьківського вікна з з у спливаючому вікні (window.opener.fn) з даними від відповіді oauth та батьківським вікном, а потім закриваю дочірнє спливаюче вікно

Мій потік після цього випуску був

  • На кнопці входу натисніть кнопку відкрити спливаюче вікно
  • Створіть інтервал задання у випадку, коли (window.opener не визначено)
  • Після успішного входу в програму oauth перенаправлення на сторінку мого домену
  • Перевірте, чи доступний window.opener, тоді зробіть №3 з вищевказаного потоку та очистіть інтервал
  • Якщо window.opener недоступний, то, оскільки я перебуваю на сторінці моїх доменів, я намагаюся встановити localstorage і намагаюся прочитати localstorage зсередини функції setInterval у батьківському вікні, тоді очистіть localstorage та setInterval і продовжуйте.
  • (для зворотної сумісності) Якщо локальне зберігання також недоступне, встановіть cookie на стороні клієнта з даними з коротким терміном (5-10 сек) та спробуйте прочитати файл cookie (document.cookie) всередині функції setInterval у батьківському вікні та продовжувати.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.