maxmantz / redux-oidc

A package for managing OpenID Connect authentication in ReactJS / Redux apps
MIT License
400 stars 111 forks source link

A non-serializable value was detected in the state, in the path: `auth.user` #169

Open bastiW opened 4 years ago

bastiW commented 4 years ago

Challenge

When we integrate the redux-oidc state into our project, we get the following error on all store calls:

A non-serializable value was detected in the state, in the path: `auth.user`. Value:
t
expires_in: (...)
expired: (...)
scopes: (...)
id_token: "1234.1234"
session_state: undefined
access_token: "ey"
refresh_token: undefined
token_type: "bearer"
scope: "openid"
profile: {sub: "123456-123456", email_verified: true, jtt: "id_token", lee: Array(1), acr: "https://example.com", …}
expires_at: 1585328012
state: undefined

The problem seems to be that the user object is a class

Our fix

We could fix that, by rewriting the following method inside src/OidcProvider.js:

  onUserLoaded = (user) => {
    this.props.store.dispatch(userFound(JSON.parse(user.toStorageString())));
  };

Parts of our code

This is how we make use of the reducer:

import { combineReducers } from '@reduxjs/toolkit';
import { connectRouter } from 'connected-react-router';

import { reducer as oidcReducer } from 'redux-oidc';

import myProfileReducer from 'components/my-profile/MyProfileSlice';

import { createBrowserHistory } from 'history';

export const history = createBrowserHistory();

const rootReducer = combineReducers({
    auth: oidcReducer,
    router: connectRouter(history),
    myProfile: myProfileReducer,
});

export type RootState = ReturnType<typeof rootReducer>;

export default rootReducer;

And this is the callback component

import React from 'react';
import { CallbackComponent as CallbackComponentOidc } from 'redux-oidc';
import userManager from '../userManager';
import { useHistory } from 'react-router-dom';

export const CallbackComponent: React.FC<{}> = () => {
    const history = useHistory();
    console.log('callback component is called');
    return (
        <>
            <br />
            <br />
            <h1>Callback Componengt</h1>
            <CallbackComponentOidc
                userManager={userManager}
                successCallback={() => {
                    console.log('Success');
                    history.push('/dashboard');
                }}
                errorCallback={error => {
                    history.push('/');
                    console.error(error);
                }}
            >
                <div>Redirecting...</div>
            </CallbackComponentOidc>
        </>
    );
};
bastiW commented 4 years ago

also we needed to change this on all other places where you make user of the user:

I have created a fork here: https://github.com/bastiW/redux-oidc

mitoihs commented 4 years ago

I'd also like to get rid of non-serializable warning. We're using Redux Toolkit which includes serializable checks for state as a default dev-only middleware (https://redux-toolkit.js.org/api/other-exports#createserializablestateinvariantmiddleware) and I'll be happier without warnings which I should always ignore.

japalo commented 4 years ago

Any updates on this? I am also using the redux toolkit and the console is getting quite cluttered over here. I did manage to temporarily disable the serializable check, but this is not a valid fix for anything. This just gives me back my console.

import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'
...
const store = configureStore(
    {
        reducer: YOUR_REDUCER(S)_HERE,
        middleware: [
            ...getDefaultMiddleware({
                serializableCheck: false
            }),
            ...otherMiddlewares
        ],
        devTools: process.env.NODE_ENV !== 'production' ? { CONFIG_GOES_HERE } : false
    }
);

This disables the check for serializable data in you store, so i wont recommend this as a fix to the problem.

zach-rodriguez commented 4 years ago

Adding onto @japalo solution, you can disable the serializationCheck specifically just for the oidc reducer by providing the options below:

import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'
import { reducer as oidcReducer } from 'redux-oidc'
...
const store = configureStore(
    {
        reducer: {
             YOUR_REDUCER(S)_HERE,
             oidc: oidcReducer
        }
        middleware: [
            ...getDefaultMiddleware({
                serializableCheck: {
                    ignoredActions: ['redux-oidc/USER_FOUND'],
                    ignoredPaths: ['oidc.user']
                }
            }),
            ...otherMiddlewares
        ],
        devTools: process.env.NODE_ENV !== 'production' ? { CONFIG_GOES_HERE } : false
    }
);

Documentation on these options for redux-toolkit can be found on the redux-toolkit docs.