OAuth междоменная безопасность React.js

12

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

Например у меня есть:

  1. mysite.com - здесь я открываю всплывающее окно.
  2. passport.mysite.com/oauth/authorize - неожиданно возникнуть.

Основной вопрос - как создать соединение между window.open(popup) и 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/… ), так как он не выглядит как обходной путь. Родительскому окну не нужно периодически проверять состояние дочернего окна. setIntervalможет быть использован как запасной вариант для localStorage
Khanh TO
@KhanhTO, да, я полностью с тобой согласен localStorage, но он работает только для того же домена, поэтому он не работает в моем состоянии
Артур
2
После того как вы завершите работу с OAuth, дочернее окно будет перенаправлено обратно на ваш домен, теперь вы находитесь в одном домене с родительским
Khanh TO
@ KhanhTO, хм, это отличная идея! Я должен был знать ..
Артур
1
Было бы еще лучше, если бы браузер восстанавливал window.openerпосле перенаправления обратно на наш домен, но это не так
Khanh TO

Ответы:

6

Предложил Ханг TO . OAuth всплывающее окно с localStorage. Основано на реакции-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

Однажды я столкнулся с проблемой в моем потоке входа в систему oauth с ошибкой window.open/window.opener на ms-edge

Мой поток до этой проблемы был

  • При нажатии кнопки входа откройте всплывающее окно
  • После успешного входа в систему приложение oauth перенаправляет на страницу моего домена
  • Затем я вызываю функцию родительского окна из с во всплывающем окне (window.opener.fn) с данными из ответа oauth и родительского окна, затем закрываю дочернее всплывающее окно

Мой поток после этой проблемы был

  • При нажатии кнопки входа откройте всплывающее окно
  • Создайте setinterval в случае (window.opener не определен)
  • После успешного входа в систему приложение oauth перенаправляет на страницу моего домена
  • Проверьте, доступен ли window.opener, затем выполните # 3 из вышеуказанного потока и clearInterval
  • Если window.opener недоступен, так как я нахожусь на своей странице доменов, я пытаюсь установить localstorage и прочитать локальное хранилище внутри функции setInterval в родительском окне, затем очистить localstorage и setInterval и продолжить.
  • (для обратной совместимости) Если localstorage также недоступен, установите файл cookie на стороне клиента с данными с коротким сроком действия (5-10 секунд) и попробуйте прочитать файл cookie (document.cookie) внутри функции setInterval в родительском окне и продолжить.
Shah92
источник