authts / react-oidc-context

Lightweight auth library based on oidc-client-ts for React single page applications (SPA). Support for hooks and higher-order components (HOC).
MIT License
709 stars 66 forks source link

Auth context is lost with react router #945

Closed slabiakt closed 1 month ago

slabiakt commented 1 year ago

Hi, I use react v18, router v6 and this library. Without router everything works as expected however with router i have this problem that when i try to enter protected route /admin directly from browser then auth.isAuthenticated is false, but when I enter this path via <Naviage to="/admin"> then auth.isAuthenticated is still set to true. When I don't use RouteAuthGuard and enter directly to /admin then auth context is resolved from local storage but with RouteAuthGuard it seems that it can't resolve auth from local storage and auth is set to false.

function App() {
  const router = createBrowserRouter([
    {
      path: "/",
      element: <Home />,
    },
    {
      path: "/admin",
      element: <RouteAuthGuard component={<Admin />}></RouteAuthGuard>,
    },
    {
      path: "/callback",
      element: <AuthControl />,
    },
  ]);

  <RouterProvider router={router} />;

  return <RouterProvider router={router} />;
}
export const RouteAuthGuard: React.FC<Props> = ({ component }) => {
  const auth = useAuth();
  const location = useLocation();
  const navigate = useNavigate();
  const context = React.useContext(AuthContext);

  useEffect(() => {
    if (!auth.isAuthenticated) {
      // localStorage.setItem(PATH_LOCAL_STORAGE_KEY, location.pathname);
      navigate("/home");
    }
  }, [auth.isAuthenticated]);

  if (auth.isLoading) {
    return <p>loading!</p>;
  }

  if (auth.error) {
    throw new Error("unauthorized");
  }

  return <>{component}</>;
};
const AuthControl: React.FC = () => {
  const navigate = useNavigate();
  const auth = useAuth();

  useEffect(() => {
    if (auth.isAuthenticated) {
      console.log(auth);
      navigate("/");
    }
  }, [auth.isAuthenticated]);

  if (!auth.isAuthenticated) {
    return <div>Loading...</div>;
  }

  if (auth.error) {
    return <div>Oops... {auth.error.message}</div>;
  }
};

export default AuthControl;
export const oidcConfig = {
  redirect_uri: `${window.location.origin}/callback`,
  post_logout_redirect_uri: window.location.origin,
  authority: "https://euc1.auth.ac/auth/realms/xxxxx",
  client_id: "react-app",
  response_type: "code",
  automaticSilentRenew: true,
  loadUserInfo: true,
  triggerAuthFlow: true,
  userStore: new WebStorageStateStore({ store: window.localStorage }),
};

any idea why?

pamapa commented 1 year ago

make sure AuthProvider comes before RouterProvider

ssuvorov commented 1 year ago

@pamapa I used metadata key in the config to avoid using CORS and also have a redirection problem

export const oidcConfig = {
  authority: GAS_BASE_URL,
  client_id: process.env.REACT_APP_GAS_CLIENT_ID,
  redirect_uri: process.env.REACT_APP_GAS_CALLBACK_URL,
  scope: 'openid profile group_type email',
  onSigninCallback: (user) => {
    console.log('onsignin', user);
    window.history.replaceState({}, document.title, '/');
    window.location.reload();
  },
  userStore: new WebStorageStateStore({ store: window.localStorage }),
  metadata: {
    issuer: GAS_BASE_URL,
    authorization_endpoint: GAS_BASE_URL + '/as/authorization.oauth2',
    userinfo_endpoint: GAS_BASE_URL + '/idp/userinfo.openid',
    end_session_endpoint: GAS_BASE_URL + 'idp/startSLO.ping'
  }
};

log "onsignin" never fires. And in the console there is nothing but a warning: "No routes matched location "/callback?code=F8...78ca17e..asd".

The provider comes before Router:

root.render(
  <AuthProvider {...oidcConfig}>
    <BrowserRouter>
      <Routes>
       ...

any ideas why redirection doesn't happen? Without metadata and with "CORS Unblock" extension everything works fine

UPDATED: Enabled logs and getting

Logger.ts:89 [OidcClient] readSigninResponseState: Error: No matching state found in storage

maybe a configuration is wrong, weird

UPDATED: I added token_endpoint: GAS_BASE_URL + '/as/token.oauth2' parameter, now it's just getting stuck on a callback page with a CORS error to this /as/token.oauth2 URL.

And I'm wondering, is it a bug in this lib or on an SSO authority side? Because in the docs oidc-client-ts it says: just add metadata to solve CORS issue. I added, didn't help :)

pamapa commented 1 year ago

Logger.ts:89 [OidcClient] readSigninResponseState: Error: No matching state found in storage

That is the redirect callback call and it can somehow not match the data with the previously stored sigin request data, you will need to find out why...

wa1id commented 8 months ago

Did you try using withAuthenticationRequired ? See README

pamapa commented 1 month ago

This issue (question) staled some time ago, closing it for now.