OAuth弹出式跨网域安全React.js


12

我对如何使用popup(window.open)在React中实现OAuth感兴趣。

例如,我有:

  1. mysite.com -这是我打开弹出窗口的地方。
  2. passport.mysite.com/oauth/authorize - 弹出。

主要问题是如何在window.open(popup)和(popup)之间建立连接(window.opener众所周知,由于跨域安全性,window.opener为null,因此我们不能再使用它了)。

window.opener每当您导航到不同的主机(出于安全原因)被删除,有没有办法解决它。如果可能的话,唯一的选择应该是在框架内付款。最上面的文档需要保留在同一主机上。

方案:

在此处输入图片说明

可能的解决方案:

  1. 使用此处setInterval所述检查打开的窗口。
  2. 使用交叉存储(不值得imho)。

那么,2019年推荐的最佳方法是什么?

React包装器-https: //github.com/Ramshackle-Jamathon/react-oauth-popup


2
在2019年,localStorage支持要好得多。我会使用localStorage方法(在stackoverflow.com/questions/18625733/…中进行了描述),因为它似乎不是一个解决方法。父窗口不需要定期检查子窗口的状态。setInterval可以用作localStorage的后备时间
庆至

@KhanhTO,是的,我完全同意您的意见localStorage,但是它仅适用于同一域,因此在我的情况下不起作用
Arthur

2
完成OAuth之后,子窗口将重定向回您的域,您现在与父窗口位于同一个域中
Khanh TO

@KhanhTO,嗯,这是个好主意!我应该知道..
亚瑟

1
如果浏览器window.opener重定向回到我们的域后还原,那会更好,但是事实并非如此
Khanh TO

Answers:


6

TO Khanh建议。带有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应用程序将重定向到我的域页面
  • 然后,我用来自oauth响应的数据从弹出窗口(window.opener.fn)中调用父窗口的函数,并从父窗口中关闭子弹出窗口

在此问题之后,我的流程是

  • 在登录按钮上,单击打开一个弹出窗口
  • 创建一个setinterval以防万一(window.opener未定义)
  • 成功登录后,oauth应用程序将重定向到我的域页面
  • 检查window.opener是否可用,然后从上述流程中执行#3并执行clearInterval
  • 如果window.opener不可用,则由于我在我的域页面上,因此我尝试设置localstorage并尝试从父窗口的setInterval函数内部读取localstorage,然后清除localstorage和setInterval并继续。
  • (为了向后兼容)如果本地存储也不可用,则使用短到期时间(5-10秒)的数据设置客户端cookie,并尝试在父窗口的setInterval函数内读取cookie(document.cookie),并继续。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.