如何在使用哈希路由器的React应用中实现Microsoft oauth

时间:2019-06-21 22:40:41

标签: javascript reactjs react-router microsoft-graph msal

我正在尝试在响应中执行Microsoft oauth按钮,该重定向/弹出允许用户登录其Microsoft帐户,然后使用该信息获取访问令牌,然后发送至图形api以获取用户信息进行登录。我正在使用此软件包https://www.npmjs.com/package/msal,并尝试为应用重定向注册一个URL。

问题是React应用正在使用哈希路由器来延迟加载页面,并且为了在Azure AAD中注册重定向URL,它不能是片段。因此,我尝试切换到浏览器路由器,并在本地主机中对其进行测试后,至少从成功的重定向中获得了结果。

然后在生产版本中尝试无法使其成功重定向。每次它重定向,刷新或什至在URL中写入其他路径时,都找不到该页面。我从React-router urls don't work when refreshing or writing manually处了解到此问题。但是现在不确定如何解决此问题。我对编码还很陌生,因此希望获得任何形式的建议。

import React, { Component } from 'react';
import { BrowserRouter,browserHistory,Router, Route, Switch } from 'react-router-dom';
import { ProtectedRoute } from './auth/ProtectedRoute'


const loading = () => <div className="animated fadeIn pt-3 text-center">Loading...</div>;

// Containers
const DefaultLayout = React.lazy(() => import('./containers/DefaultLayout'));

// Pages
const Login = React.lazy(() => import('./views/Pages/Login'));
const Register = React.lazy(() => import('./views/Pages/Register'));
const Page404 = React.lazy(() => import('./views/Pages/Page404'));
const Page500 = React.lazy(() => import('./views/Pages/Page500'));
//const EditInfo = React.lazy(() => import('./views/Pages/EditInfo'));

export class App extends Component {

  render() {
    return (
      <BrowserRouter>
          <React.Suspense fallback={loading()}>
            <Switch>
              <Route exact path="/login" name="Login Page" render={props => <Login {...props}/>} />
              <Route exact path="/register" name="Register Page" render={props => <Register {...props}/>} />
              <Route exact path="/404" name="Page 404" render={props => <Page404 {...props}/>} />
              <Route exact path="/500" name="Page 500" render={props => <Page500 {...props}/>} />
              <ProtectedRoute path="/" name="Home" component={DefaultLayout} />
              <ProtectedRoute path="/dashboard" name="Home" component={DefaultLayout} />
            </Switch>
          </React.Suspense>
      </BrowserRouter>
    );
  }
}

我处理配置,重定向,请求的功能。我想重定向到登录页面。但是,在使用浏览器路由器的生产版本中,重定向到服务器上找不到的页面。

import * as Msal from 'msal';
import axios from 'axios'

export function loginOauth () {
  var msalConfig = {
    auth: {
      clientId: 'my client id',
      redirectUri: 'http://mysite.io/login'
    },
    cache: {
      cacheLocation: "sessionStorage",
      storeAuthStateInCookie: true
    }
  };

  let loginRequest = {
    scopes: ["user.read"],
    prompt: 'select_account'
  }

  let accessTokenRequest = {
    scopes: ["user.read","profile"]
  }

  var graphConfig = {
    graphMeEndpoint: "https://graph.microsoft.com/v1.0/me"
  };


  var msalInstance = new Msal.UserAgentApplication(msalConfig);



  let authRedirectCallBack = (errorDesc, token, error, tokenType) => {
    if (token) {
      console.log(token);
    }
    else {
      console.log(error + ":" + errorDesc);
    }
  };

  msalInstance.handleRedirectCallback(authRedirectCallBack);



  let signIn = () => {
    msalInstance.loginRedirect(loginRequest).then(async function (loginResponse) {
      return msalInstance.acquireTokenSilent(accessTokenRequest);
    }).then(function (accessTokenResponse) {
      const token = accessTokenResponse.accessToken;
      console.log(token);
    }).catch(function (error) {
      //handle error
    });
  }


  let acquireTokenRedirectAndCallMSGraph = () => {
    //Always start with acquireTokenSilent to obtain a token in the signed in user from cache
    msalInstance.acquireTokenSilent(accessTokenRequest).then(function (tokenResponse) {
      callMSGraph(graphConfig.graphMeEndpoint, tokenResponse.accessToken, graphAPICallback);
    }).catch(function (error) {
      console.log(error);
      // Upon acquireTokenSilent failure (due to consent or interaction or login required ONLY)
      // Call acquireTokenRedirect
      if (requiresInteraction(error.errorCode)) {
        msalInstance.acquireTokenRedirect(accessTokenRequest);
      }
    });
  }

  let callMSGraph = (theUrl, accessToken, callback) => {
    console.log(accessToken);
    var xmlHttp = new XMLHttpRequest();
    xmlHttp.onreadystatechange = function () {
      if (this.readyState === 4 && this.status === 200)
        callback(JSON.parse(this.responseText));
    }
    xmlHttp.open("GET", theUrl, true); // true for asynchronous
    xmlHttp.setRequestHeader('Authorization', 'Bearer ' + accessToken);
    xmlHttp.send();
  }

  let graphAPICallback = (data) => {
    console.log(data);
  }


  let requiresInteraction = (errorCode)=> {
    if (!errorCode || !errorCode.length) {
      return false;
    }
    return errorCode === "consent_required" ||
      errorCode === "interaction_required" ||
      errorCode === "login_required";
  }

  signIn();


}

在重定向和加载页面时,我正在使用componentmount来从会话中获取数据。

export default class Login extends Component{
  constructor(props) {
    super(props);
    this.state = {
      modal: false,
    };

    this.toggle = this.toggle.bind(this);
  }


  toggle() {
    this.setState(prevState => ({
      modal: !prevState.modal,
    }));
  }

  handleLogout(){
    auth.logout(() => {console.log("logged out")})
    this.toggle()
  }

  componentWillMount() {

    var msalConfig = {
      auth: {
        clientId: 'my_clientid',
        redirectUri: 'http://mysite.io/login'
      },
      cache: {
        cacheLocation: "sessionStorage",
        storeAuthStateInCookie: true
      }
    };

    var msalInstance = new Msal.UserAgentApplication(msalConfig);



    if (msalInstance.getAccount()) {
      var tokenRequest = {
        scopes: ["user.read"]
      };
      msalInstance.acquireTokenSilent(tokenRequest)
        .then(response => {

          callMSGraph(response.accessToken, (data)=>console.log(data))

          // get access token from response
          // response.accessToken
        })
        .catch(err => {
          // could also check if err instance of InteractionRequiredAuthError if you can import the class.
          if (err.name === "InteractionRequiredAuthError") {
            return msalInstance.acquireTokenPopup(tokenRequest)
              .then(response => {
                // get access token from response
                // response.accessToken
              })
              .catch(err => {
                // handle error
              });
          }
        });
    } else {
      // user is not logged in, you will need to log them in to acquire a token
    }

    let token = sessionStorage.getItem('msal.idtoken');
    if(token !== null) {
      var decoded = jwt_decode(token);
      console.log(decoded);
    } else {
      console.log("NO token yet");
    }
  }


  render() {
    let open;
      if(auth.isAuthenticated() === "true"){
         open = true
      }else{ open = false}

    return (<div><button onClick={ () => {loginOauth()}} >Login with Microsoft</button> </div>);
}

1 个答案:

答案 0 :(得分:1)

您应该只注册要用户重定向到的基本URL。要允许状态数据(即片段)穿越服务边界,请使用state参数。

您通过state参数传递给OAuth的任何信息都将与您的令牌一起返回。然后,您将在返回用户时解析state