React Azure AD returned blank page - reactjs

I have a React application which wished to add an Azure login authentication.
The original code calling the App.js in index.js without Azure AD is as below:
import React from 'react';
import ReactDOM from 'react-dom';
import 'index.css';
import App from 'App';
import * as serviceWorker from 'serviceWorker';
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import reducer from 'store/reducer'
//, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
{/* <React.StrictMode> */}
<App />
{/* </React.StrictMode> */}
</Provider>,
document.getElementById('root')
);
serviceWorker.unregister();
After adding Azure AD:
import React from 'react';
import ReactDOM from 'react-dom';
import 'index.css';
import App from 'App';
import * as serviceWorker from 'serviceWorker';
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import reducer from 'store/reducer'
import { AzureAD } from 'react-aad-msal';
import { authProvider } from './authProvider';
ReactDOM.render(
<AzureAD provider={authProvider} forceLogin={true}>
<App />
</AzureAD>,
document.getElementById('root'),
);
serviceWorker.unregister();
The above code can pass my organization Azure login. However, it would show a blank page after the login had been passed, even I tried to change <App /> to <h1>Hello World</h1> would still be blanked.
Do you have any insights on this? Many thanks!
Reference:
https://medium.com/#pavelray/connect-your-react-app-with-azure-ad-using-3ddd39223d27
https://www.npmjs.com/package/react-aad-msal

I've followed the doc your provided and tested in my side, it worked indeed, but I made some changes which I think is wrong. In my opinion, it seems to have something wrong with the redirect url.
This is my config, pls note I used 'http://localhost:3000' rather than 'https' which is mentioned in the doc. And of course, I set the same redirect url in azure portal. Then it worked, I can redirect to the home page after login.
import { MsalAuthProvider, LoginType } from 'react-aad-msal';
// Msal Configurations
const config = {
auth: {
authority: 'https://login.microsoftonline.com/common',
clientId: '<client-id>',
redirectUri: 'http://localhost:3000'
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true
}
};
// Authentication Parameters
const authenticationParameters = {
scopes: [
// '<property (i.e. user.read)>',
// 'https://<your-tenant-name>.onmicrosoft.com/<your-application-name>/<scope (i.e. demo.read)>'
'user.read'
]
}
// Options
const options = {
loginType: LoginType.Popup,
tokenRefreshUri: window.location.origin + '/auth.html'
}
export const authProvider = new MsalAuthProvider(config, authenticationParameters, options)
Finally, I found a doc which said Microsoft is currently in process of building an official #azure/msal-react library which will replace react-aad-msal, so if you prepare to change your mind, I think the samples in this page could help you. It contains the one uses #azure/msal-react.

Related

Redux-firestore with React

So I have been creating an application where a user needs to log into firebase using google authentication. I am using redux, react-redux, react-redux-firebase, redux-firestore, and redux-thunk. I am able to successfully log the user into firebase with the google authentication. I now want to use firestore in order to have a collection of all the users. I have looked at the documentation for redux-firestore and the method of getting/manipulating is a little different. I have tried using the documentation, but I cannot get the functions to work with redux-firestore.
Here is the action
export const signIn = () => (
dispatch,
getState,
{getFirebase, getFirestore}) => {
const firebase = getFirebase();
const firestore = getFirestore();
firebase.auth().signInWithPopup(provider).then(function(result) {
if(result.credential) {
firestore.get({collection: 'users', doc: result.user.uid}).then(function(doc) {
if(!doc.exists){
console.log("new!")
firestore.add(
{collection: 'users', doc: result.user.uid},
{name: firebase.auth.currentUser.displayName});
} else{
console.log("old!")
}
})
}
}).catch((err) => {
})
};
And here is my setup in index.js for the src folder
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {BrowserRouter} from 'react-router-dom';
import {createStore, applyMiddleware, compose} from 'redux';
import {Provider} from 'react-redux';
import allReducers from './reducers';
import thunk from 'redux-thunk';
import firebase from './Firebase';
import {firebaseConfig} from './Firebase'
import {createFirestoreInstance, getFirestore, reduxFirestore} from 'redux-firestore';
import {ReactReduxFirebaseProvider, getFirebase} from 'react-redux-firebase';
const store = createStore(
allReducers,
compose(
applyMiddleware(thunk.withExtraArgument({getFirebase, getFirestore})),
reduxFirestore(firebaseConfig)
));
const rrfConfig = {
userProfile: 'users',
useFirestoreForProfile: true
};
const rrfProps = {
firebase,
config: rrfConfig,
dispatch: store.dispatch,
}
ReactDOM.render(
<Provider store={store}>
<ReactReduxFirebaseProvider {...rrfProps}>
<BrowserRouter>
<App />
</BrowserRouter>
</ReactReduxFirebaseProvider>
</Provider>,
document.getElementById('root')
);
serviceWorker.unregister();
I know that I have not used createFirestoreInstance in this code, but I was playing around with it.
If anyone could tell me how to get this working, I would appreciate it.
Thanks!
Quick update:
I have figured out how to at least write to firestore using this code
const userRef = firebase.firestore().collection('users').doc(result.user.uid);
userRef.get().then(function(doc) {
if(!doc.exists){
userRef.set({name: result.user.displayName});
}
})
This is not the best (or maybe the right solution), but it does work. It is not using redux-firestore, but is there a better way?
If you're using React, use react-redux-firebase. There's no need for these many complication and the code looks much neater and simpler. Authentication, firestore and all other firebase features works out of the box with just small amount of code. They also comes with React hooks like useFirebase() and useFirestore() instead of you needing to write them on your own.
react-redux-firebase is built on top of redux-firebase and provides all the things you would need in React.
If your app only uses firebase, I would even recommend you use just plain Redux without Redux Thunk or Redux Saga.

How to save state when re-rendering a component with JWT Token

I"m having issues fully implementing authentication with a JWT token. I finally have my app keeping me signed in on a page refresh...HOWEVER, I loose the rest of the state of my user. So my user is still signed in, but has no username, or any kind of info.
Here is how I'm using the token to keep a user logged in.
const token = localStorage.getItem('token');
if (token) {
store.dispatch({ type: 'AUTH_USER'})
}
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
Then my currentUser reducer has this case...
case 'AUTH_USER':
return { ...state, loggedIn:true }
The problem is on a page re-render state gets set back to an empty object. I suppose I could send a request back to the API to fetch the user data again...BUT, there has to be a way to keep that user's data stored???
I am using redux-persist to make the state persistent. Recommended. I had, however, some difficulties making it work.
Here is my code if you want to use it.
I am using redux-persist version 4 (version 5 had some major changes, didn't migrate to it yet), so please use this version if you want to use my code.
I am also using Redux DevTools, react-router and redux-thunk, therefore the code includes them.
These are the relevant parts from my index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { compose, createStore, applyMiddleware } from 'redux';
import { persistStore, autoRehydrate } from 'redux-persist';
import ReduxThunk from 'redux-thunk';
const composeEnhancers =
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
}) : compose;
const enhancers = composeEnhancers(
applyMiddleware(ReduxThunk),
autoRehydrate()
);
const initialState = {};
const store = createStore(
reducers,
initialState,
compose(enhancers)
);
persistStore(store);
ReactDOM.render(
<Provider store={store}>
<div>
<BrowserRouter>
<Switch>
<Route exact path="/xxx" component={xxx} />
<Route path="/yyy" component={yyy} />
</Switch>
</BrowserRouter>
</div>
</Provider>
, document.querySelector('#root'));
Good luck!

Redirect React Router from function

I am creating a React application using create-react-app, react-redux, react-thunk, react-router.
I am setting a localstorage variable "current_user" after authenticating so that I can setup my Restricted Routers.
I am using Axios to make the ajax requests, and In my initial index.js file I have setup a global Axios Interceptor so that if any ajax call returns 401, it automatically clears the local storage "current_user". I would like it to also redirect to /Login.
I'm able to do this the old fashioned way (ie window.location) however, is there a way to set this global function to redirect from anywhere in the application to the login page?
The global interceptor:
index.js
import "materialize-css/dist/css/materialize.min.css";
import "jquery/dist/jquery.min.js";
import "materialize-css/dist/js/materialize.min.js";
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import reduxThunk from 'redux-thunk';
import axios from "axios";
import App from "./components/App";
import reducers from './reducers';
const store = createStore(reducers, {}, applyMiddleware(reduxThunk));
axios.interceptors.response.use(undefined, function (error) {
if(error.response.status === 401) { //Setup Global Config
localStorage.setItem('current_user', null);
//Would like to redirect to /Login here
}
});
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.querySelector("#root")
);
You can try something like this :
import createHistory from 'history/createBrowserHistory';
const history = createHistory({
window.location.pathname,
});
// ...
if(error.response.status === 401) {
// ...
history.push('/login');
}
Here you can find another approach to redirect your user if you don't have the token in the localStorage : https://github.com/strapi/strapi-examples/blob/master/login-react/react-login-front-end-app/app/containers/WithAuth/index.js
Just Use : return <Redirect to="/login" />. That should do.

Dynamic imports in React

I am trying to import dynamic of a file by means of process.env.NODE_ENV to import a style sheet or another one in production or in development. I have made a condition to load it but it gives me an error Error in ./src/index.js
Syntax error: 'import' and 'export' may only appear at the top level (13: 4) I guess this is not correct but ... how can I do it? I use create-react-app
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './components/App';
import routes from './routes';
import configureStore from './store/configureStore';
import initialState from './reducers/initialState';
if (process.env.NODE_ENV === 'production') {
import './styles/index.css';
}else {
import './styles/index.scss';
}
const store = configureStore(initialState);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
Thanks!!!
As Laoujin mentions in the comments, you'll want to use require in this scenario.
For example, here's how I configure access to my Redux store, based on NODE_ENV, which could be adjusted to suit your needs:
const INITIAL_STATE = {};
function getStore () {
const configureStore = process.env.NODE_ENV === 'production'
? require('./configure-store.prod').default
: require('./configure-store.dev').default;
return configureStore(INITIAL_STATE);
}
export default getStore();

How to organize state with redux and react-router?

Redux advocates the use of a single store with a single state. However, with react-router you get multiple pages each having their own top-level root component.
How should one go about wiring up redux with a react-router app that has multiple pages? React-router 1.0 no longer lets you pass props to the routes so making the router the top level component that contains the state for all the pages is no longer possible nor is it feasible.
If you are using redux + react-router I would highly recommend using redux-router as well - this will allow you to keep route information in your store - I usually have the following setup.
redux-router: ^1.0.0-beta3 /
react-router": ^1.0.0-rc1 /
redux: "^3.0.2 /
react: "^0.14.0
//index.js [entry point]
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route } from 'react-router';
import { Provider } from 'react-redux';
import createStore from './utils/create-store';
import routes from './bootstrap/routes';
import { ReduxRouter } from 'redux-router';
const store = createStore(routes);
ReactDOM.render(
<Provider store={store}>
<ReduxRouter>
{routes}
</ReduxRouter>
</Provider>,
document.getElementById('root')
);
// create-store.js
import { applyMiddleware, createStore, combineReducers, compose } from 'redux';
import * as reducers from '../reducers/index';
import promiseMiddleware from './promise-middleware';
import { routerStateReducer, reduxReactRouter } from 'redux-router';
import history from './history'
/**
* Sets up the redux store. Responsible for loading up the reducers and middleware.
*
* #param routes
*/
export default function create(routes) {
const composedReducers = combineReducers({
router: routerStateReducer,
...reducers
});
const finalCreateStore = compose(applyMiddleware(promiseMiddleware),
reduxReactRouter({
routes,
history
}))(createStore);
let store = finalCreateStore(composedReducers);
return store;
}
// routes.js
import React from 'react';
import { Route } from 'react-router';
import App from './app';
module.exports = (
<Route component={App}>
...all your routes go here
</Route>
);
// app.js
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
export default class App extends React.Component {
render() {
const { props: { children } } = this;
return (
<div className="app-container">
{children}
</div>
);
}
}
So as you can see there is one higher order component that wraps the rest of your routes
Redux-router is no longer compatible with react-router v4, and the above solution will not work.
As such, I suggest utilzing redux-little-router. Unlike redux-router it is not dependent on react-router, but instead a replacement for it.
With it you can do things like:
Push to new routes from your async actions:
export function navigateAbout() {
return (dispatch) => {
dispatch(push('/about'))
}
}
See your router details (current path, querystring and params) inside redux-dev-tools, and access those details from you store like you would for any other prop:
function mapStateToProps(state) {
return {
string: state.router.query.string
}
}
PS. You can refer to or use this react boilerplate with redux-little-redux already implemented

Resources