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
658 stars 64 forks source link

Auth context is lost with react router #945

Open slabiakt opened 11 months ago

slabiakt commented 11 months 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 11 months ago

make sure AuthProvider comes before RouterProvider

ssuvorov commented 11 months 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 11 months 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 6 months ago

Did you try using withAuthenticationRequired ? See README