oidc-client icon indicating copy to clipboard operation
oidc-client copied to clipboard

Questions about migrating from `@axa-fr/react-oidc-redux` v3 to `@axa-fr/react-oidc` v5

Open ulrik59 opened this issue 2 years ago • 8 comments

Issue and Steps to Reproduce

I'm currently migrating a project which was using @axa-fr/react-oidc-redux v3 to @axa-fr/react-oidc v5 and I have troubles understanding the migration guide.

My project was using the notAuthorized and UserStore props on the provider component but they do not exist anymore on last version. By which new props should I replace them? Same in the configuration prop of the provider, the fields automaticSilentReview, loadUserInfo, post_logout_redirect_url and response_type were used but do not exist anymore. Which fields should I now used instead?

Thanks in advance for your responses.

ulrik59 avatar Jun 07 '22 15:06 ulrik59

Hi @ulrik59 , do you have a sample of your old redux code?

You have to compose your redux mapping with the new hook.

import { useOidcUser, UserStatus } from '@axa-fr/react-oidc';

const DisplayUserInfo = () => {
  const{ oidcUser, oidcUserLoadingState } = useOidcUser();

  switch (oidcUserLoadingState){
    case UserStatus.Loading:
      return <p>User Information are loading</p>;
    case UserStatus.Unauthenticated:
      return <p>you are not authenticated</p>;
    case UserStatus.LoadingError:
      return <p>Fail to load user information</p>;
    default:
      return (
              <div className="card text-white bg-success mb-3">
                <div className="card-body">
                  <h5 className="card-title">User information</h5>
                  <p className="card-text">{JSON.stringify(oidcUser)}</p>
                </div>
              </div>
      );
  }
};

response_type does not exist anymore only code with pcke is supported automaticSilentReview does not exist any more. If you do not set silent_redirect_uri, you won't have silent auto renew.

post_logout_redirect_url has been replaced by a property inside logout function

guillaume-chervet avatar Jun 07 '22 15:06 guillaume-chervet

Hello @guillaume-chervet , thanks for your answer. I can then safely remove response_type and automaticSilentReview. I assume I can also safely remove the loadUserInfo field.

On my app, there is really few code used:

// src\layout\App\App.tsx
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { InMemoryWebStorage, Oidc, OidcSecure } from '@axa-fr/react-oidc-redux';
import store from '../../store';
import NotAuthorized from '../NotAuthorized';
import Footer from '../Footer';
import Header from '../Header';
import Routes from '../Routes';

interface AppAuthConfig {
    automaticSilentRenew: true;
    loadUserInfo: true;
    authority: string;
    client_id: string;
    post_logout_redirect_uri: string;
    redirect_uri: string;
    response_type: 'code';
    silent_redirect_uri: string;
    scope: string;
}

const App: React.VFC<{ configuration: AppAuthConfig }> = ({ configuration }) => (
  <Oidc store={store} configuration={configuration} UserStore={InMemoryWebStorage} notAuthorized={NotAuthorized}>
    <BrowserRouter>
      <OidcSecure>
        <div className="app">
          <div className="app__sub-container">
            <Header />
            <Routes />
          </div>
          <Footer />
        </div>
      </OidcSecure>
    </BrowserRouter>
  </Oidc>
);
// src\store\rootReducer.ts
import { reducer as oidc } from '@axa-fr/react-oidc-redux';
import { combineReducers } from 'redux';

const rootReducer = combineReducers({ oidc });
// src\store\oidc\selectors.ts
import { RootState } from '../rootReducer';

export const selectOidcUser = (state: RootState): User | undefined => state?.oidc?.user;
// src\store\oidc\selectors.ts
import { RootState } from '../rootReducer';

export const selectOidcUser = (state: RootState): User | undefined => state?.oidc?.user;
// src\pages\AccountEdit\AccountEditForm.tsx
import { withAuthentication } from '@axa-fr/react-oidc-redux-fetch';
import React from 'react';
import { useDispatch } from 'react-redux';
import { useParams } from 'react-router-dom';
import {
    preloadAccountToEdit,
} from '../../store/accountSearch/reducer/result/accountsReducer';

type AccountEditFormProps = {
    fetch: typeof window.fetch;
};

const AccountEditForm: React.FC<AccountEditFormProps> = ({ fetch }) => {
    const { accountId } = useParams<{ accountId: string }>();
    const dispatch = useDispatch();

    useEffect(() => {
        dispatch(preloadAccountToEdit({ fetch, accountId }));
    }, [accountId]);

    return (
        <form aria-label="form">
            {/* ... */}
        </form>
    );
};

export default withAuthentication(fetch)(AccountEditForm);

As you can see there is absolutely no logout. I did a bit of retro engineering and it seems that the field post_logout_redirect_uri was only used on logout. So can I assume I can totally remove it? About the provider, I assume I can safely remove the store and UserStore props but I did some investigation about the notAuthorized one and find out only one use case. As you can see, we are using the withAuthentication HOC which is wrapping the fetch function to add the tokens in the headers. But it is also redirecting to a route where this NotAuthorized component will be used when response status is 403. There is also a tentative of silent authentication and retry when response status is 401. See it here: https://github.com/AxaGuilDEv/react-oidc/blob/v3.1.7/packages/redux-fetch/src/withAuthentication.ts The problem is these mecanisms are missing in the last version: https://github.com/AxaGuilDEv/react-oidc/blob/v5.10.1/packages/react/src/oidc/FetchToken.tsx I tried to reimplement it myself:

// src\hooks\useFetchWithAuthentication.ts
import { useOidcFetch, useOidc } from '@axa-fr/react-oidc';
import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';

const useFetchWithAuthentication = (prevFetch?: typeof window.fetch) => {
    const navigate = useNavigate();
    const { login } = useOidc();
    const { fetch } = useOidcFetch(prevFetch);

    const fetchWithAuthentication = useCallback(
        async (url: RequestInfo, options?: RequestInit, isRetry = true) => {
            let response = await fetch(url, options);

            if (response.status === 403) {
                navigate('/not-authorized');
            }

            if (isRetry) {
                if (response.status === 401) {
                    await login();
                    response = await fetchWithAuthentication(url, options, false);
                }
            }

            return response;
        },
        [fetch, navigate, login],
    );

    return fetchWithAuthentication;
};

export default useFetchWithAuthentication;

But I have only access to a login function from useOidc hook and and I don't have access to any functions to silently login unless I use some "hacks" where I get directly the Oidc singleton from the dist/ files. How could I solve that? Thx in advance

ulrik59 avatar Jun 08 '22 14:06 ulrik59

Hi @ulrik59 ,

sorry for the delay.

I think you are in the good direction. I have added comment to the code bellow :

// src\hooks\useFetchWithAuthentication.ts
import { useOidcFetch, useOidc } from '@axa-fr/react-oidc';
import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';

const useFetchWithAuthentication = (prevFetch?: typeof window.fetch) => {
    const navigate = useNavigate();
    const { login } = useOidc();
    const { fetch } = useOidcFetch(prevFetch);

    const fetchWithAuthentication = useCallback(
        async (url: RequestInfo, options?: RequestInit, isRetry = true) => {
            let response = await fetch(url, options);

            if (response.status === 403) {
                navigate('/not-authorized');
            }
            // you also do not need retry, the new version assume the token is always up to date
            // silent sign is done in background if require
            /*if (isRetry) {
                if (response.status === 401) {
                    // await login(); you do not need that
                    response = await fetchWithAuthentication(url, options, false);
                }
            }*/

            return response;
        },
        [fetch, navigate],
    );

    return fetchWithAuthentication;
};

export default useFetchWithAuthentication;

guillaume-chervet avatar Jun 10 '22 13:06 guillaume-chervet

Hi @ulrik59 did you succed your migration?

guillaume-chervet avatar Jun 14 '22 16:06 guillaume-chervet

Hey @guillaume-chervet , yes I did. My PR is in code review for now and we will test it as soon as possible on our staging environment. Removing the silent retry part solved a lot of issues. I will let you know if tests are successful Thanks for the help!

ulrik59 avatar Jun 15 '22 09:06 ulrik59

Hi @ulrik59 may you describe your problem with silentsign. It is interresting. I suppose you use Wac an Maam at AXA France.

guillaume-chervet avatar Jun 17 '22 05:06 guillaume-chervet

Hi @ulrik59 , was you tests successfull?

guillaume-chervet avatar Jul 28 '22 15:07 guillaume-chervet

Hi @ulrik59 , do you have some feedback to help other?

guillaume-chervet avatar Aug 07 '22 06:08 guillaume-chervet

I close the issue @ulrik59 , feel free to reopen it if you need.

guillaume-chervet avatar Aug 16 '22 09:08 guillaume-chervet