AxaFrance / oidc-client

Light, Secure, Pure Javascript OIDC (Open ID Connect) Client. We provide also a REACT wrapper (compatible NextJS, etc.).
MIT License
601 stars 161 forks source link

TypeError: Failed to fetch Object.startKeepAliveServiceWorker #1392

Open HSULGIT opened 5 months ago

HSULGIT commented 5 months ago

Issue and Steps to Reproduce

~~OidcServiceWorker.js:19 Failed to normalize url: undefined normalizeUrl @ OidcServiceWorker.js:19 getCurrentDatabasesTokenEndpoint @ OidcServiceWorker.js:291 handleFetch @ OidcServiceWorker.js:367~~

When user gets redirected to keycloak login and put in credentials and hit login, the redirect to the next page (react-oidc secured component) causes the error seen above. Seems that i tries to inintialize the auth but the OidcTrustedDomains config is not loaded in time for the OidcServiceWorker. Only happens after login on auth provider. When reloading website no error occurs.

Also i got a non Error in console on that:

TypeError: Failed to fetch
    at ke (:3000/static/js/bundle.js:136546:34)
    at r (:3000/static/js/bundle.js:136623:41)
    at Object.startKeepAliveServiceWorker (:3000/static/js/bundle.js:136658:42)
    at :3000/static/js/bundle.js:137325:178
    at async n (:3000/static/js/bundle.js:137803:19)
    at async ks (:3000/static/js/bundle.js:137633:9)

After that also the fetching of "/.well-known/openid-configuration" from auth provider which is present and available, fails. When following the url there is the openid-configuration present.

Also the fetch on "https://myDomain.de/OidcKeepAliveServiceWorker.json?minSleepSeconds=150" fails, like mentioned here (no OidcKeepAliveServiceWorker.json is present in public folder): https://github.com/AxaFrance/oidc-client/pull/1355

What could lead to this problems?

Versions

7.13.6

Additional Details

  "dependencies": {
    "@axa-fr/react-oidc": "^7.13.6",
    "@date-io/date-fns": "^1.3.13",
    "@emotion/react": "^11.11.0",
    "@emotion/styled": "^11.11.0",
    "@fontsource/roboto": "^5.0.2",
    "@mui/icons-material": "^5.11.16",
    "@mui/material": "^5.13.3",
    "@mui/styled-engine-sc": "^5.12.0",
    "@mui/system": "^5.14.0",
    "@mui/x-date-pickers": "^7.3.2",
    "@tag0/use-text-width": "^1.2.0",
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^1.6.7",
    "dayjs": "^1.11.11",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-dropzone": "^14.2.3",
    "react-measure": "^2.5.2",
    "react-papaparse": "^4.1.0",
    "react-router-dom": "^6.4.5",
    "react-scripts": "^5.0.1",
    "react-virtualized": "^9.22.5",
    "react-virtualized-auto-sizer": "^1.0.24",
    "react-window": "^1.8.10",
    "react-window-infinite-loader": "^1.0.9",
    "styled-components": "^5.3.11",
    "typescript": "^4.9.5",
    "uuid": "^9.0.0",
    "web-vitals": "^2.1.4"
  },
    "devDependencies": {
    "@types/react-measure": "^2.0.12",
    "@types/react-virtualized": "^9.21.30",
    "@types/react-window": "^1.8.8",
    "@types/styled-components": "^5.1.34",
    "@types/uuid": "^9.0.2",
    "css-loader": "^6.8.1",
    "env-cmd": "^10.1.0",
    "husky": "^7.0.4",
    "openapi-typescript-codegen": "^0.25.0",
    "style-loader": "^3.3.3"
  }
guillaume-chervet commented 5 months ago

Hi @HSULGIT thank you for your issue.

Do you have a sample of your configuration and also you trusted domain.js?

HSULGIT commented 4 months ago

Hi @guillaume-chervet,

the "Failed to normalize url" is gone, but the failed fetches on

remaining.

This happens while development and with the deployed build on the server.

Here is my trusted domain file and the configuration.

OidcTrustedDomains.js (urls replaced with placeholders here (except 127.0.0.1))

const trustedDomains = {
    default: [
        "http://127.0.0.1",
        "https://dev-frontend.com",
        "https://dev-api.com",
        "https://auth.service.com/realms/test",
        "https://test-frontend.com",
        "https://test-api.com",
      ],
      config_classic: ["https://auth.service.com/realms/test"],
      config_without_silent_login: [
        "https://auth.service.com/realms/test",
      ],
      config_without_refresh_token: [
        "https://auth.service.com/realms/test",
      ],
      config_without_refresh_token_silent_login: [
        "https://auth.service.com/realms/test",
      ],
      config_with_hash: ["https://auth.service.com/realms/test"],  
};

trustedDomains.config_show_access_token = { 
    domains: [
      "http://127.0.0.1",
      "https://dev-frontend.com",
      "https://dev-api.com",
      "https://auth.service.com/realms/test",
      "https://test-frontend.com",
      "https://test-api.com",
      ],
    showAccessToken: true,
    // convertAllRequestsToCorsExceptNavigate: false,
    // setAccessTokenToNavigateRequests: true,
};

trustedDomains.config_separate_oidc_access_token_domains = {
    oidcDomains: [
      "http://127.0.0.1",
      "https://dev-frontend.com",
      "https://dev-api.com",
      "https://auth.service.com/realms/test",
      "https://test-frontend.com",
      "https://test-api.com",
      ],
    accessTokenDomains: ['https://dev-api.com', 'https://test-api.com'],
};

And my configuration (Authentication.tsx):

import React, { useEffect, useState } from "react";
import { OidcProvider } from "@axa-fr/react-oidc";
import OutputAuthState from "./OutputAuthState";

const envState = process.env.NODE_ENV;
const envVarAuthClient = process.env.NODE_ENV === "development" ? process.env.REACT_APP_CLIENT_ID : window.__RUNTIME_CONFIG__.REACT_APP_CLIENT_ID;
const envVarAuthProvider = process.env.NODE_ENV === "development" ? process.env.REACT_APP_AUTHORITY : window.__RUNTIME_CONFIG__.REACT_APP_AUTHORITY;

if (!envVarAuthClient) {
  console.error("Auth client is not set or is empty. Please provide a valid client ID.");
}

if (!envVarAuthProvider) {
  console.error("Authority provider is not set or is empty. Please provide a valid authority URL.");
}

const configuration = {
  client_id: envVarAuthClient ?? "",
  redirect_uri: window.location.origin + "/authentication/callback",
  silent_redirect_uri:
    window.location.origin + "/authentication/silent-callback", // Optional: Used for silent sign-in.
  scope: "openid profile email", // The scopes requested during authentication.
  refresh_time_before_tokens_expiration_in_second: 200, // Time in seconds before token expiration to refresh.
  authority: envVarAuthProvider ?? "", // The OIDC authority URL.
  service_worker_relative_url: "/OidcServiceWorker.js", // URL for the service worker.
  service_worker_only: false, // Whether to use the service worker only.
};

type AuthenticationProps = {
  children: JSX.Element;
};

const authStateSwitchDisplay = (state: string, authAvailable: boolean) => (
  <OutputAuthState state={state} authProviderPresent={authAvailable} />
);

const Authentication = (props: AuthenticationProps): JSX.Element => {
  const [authProviderPresent, setAuthProviderPresent] =
    useState<boolean>(false);
  const [isMounting, setIsMounting] = useState<boolean>(true);

  useEffect(() => {
    if (isMounting) {
      setIsMounting(false);
    } else {
      const urlToCheck =
        envVarAuthProvider + "/.well-known/openid-configuration";
      fetch(urlToCheck)
        .then((response) => {
          if (response.status === 200) {
            setAuthProviderPresent(true);
          } else {

            setAuthProviderPresent(false);
          }
        })
        .catch((error) => {
          setAuthProviderPresent(false);
        });
    }
  }, [isMounting]);

  return (
    <OidcProvider
      configuration={configuration}
      authenticatingComponent={() =>
        authStateSwitchDisplay("authenticating", authProviderPresent)
      }
      loadingComponent={() =>
        authStateSwitchDisplay("loading", authProviderPresent)
      }
      sessionLostComponent={() =>
        authStateSwitchDisplay("sessionLost", authProviderPresent)
      }
      authenticatingErrorComponent={() =>
        authStateSwitchDisplay("authenticatingError", authProviderPresent)
      }
      callbackSuccessComponent={() =>
        authStateSwitchDisplay("callbackSuccess", authProviderPresent)
      }
      serviceWorkerNotSupportedComponent={() =>
        authStateSwitchDisplay(
          "serviceWorkerNotSupported",
          authProviderPresent
        )
      }
    >
      {props.children}
    </OidcProvider>
  );
};

export default Authentication;

AppBar.tsx:

import React from "react";
import css from "./cssModules/AppBar.module.css";
import CustomAppBar from "./CustomAppBar";
import { useOidc, useOidcIdToken } from "@axa-fr/react-oidc";
import { Menu as MenuIcon, Logout as LogoutIcon } from "@mui/icons-material";
import { Box, Toolbar, Typography, IconButton } from "@mui/material";

type AppBarProps = {
  open: boolean;
  drawerWidth: number;
  onDrawerOpen: () => void;
};

const AppBar = ({
  open,
  drawerWidth,
  onDrawerOpen,
}: AppBarProps): JSX.Element => {
  const { logout } = useOidc();
  const { idTokenPayload } = useOidcIdToken();

  const handleLogout = (): void => {
    logout("/").catch((error) => {
      console.error(error);
    });
  };

  const liftDrawerOpen = (): void => {
    onDrawerOpen?.();
  };

  const envState = process.env.NODE_ENV;

  return (
    <Box className={css.outerWrapperBox}>
      <Box className={css.innerWrapperBox}>
        <CustomAppBar position="fixed" open={open} sidebarWidth={drawerWidth}>
          <Toolbar className={css.toolbar}>
            <IconButton
              className={css.iconButton}
              aria-label="open drawer"
              onClick={liftDrawerOpen}
              edge="start"
              sx={{ ...(open && { display: "none" }) }}
            >
              <MenuIcon className={css.menuIcon} />
            </IconButton>
            <Box className={css.box}>
              {idTokenPayload.name && (
                <Typography className={css.boxTypography}>
                  {envState === "development" && (
                    <span className={css.boldText}>---LOCAL DEVELOPMENT MODE---&emsp;</span>
                  )}
                  {`Logged in as ${idTokenPayload.name
                    .split(" ")
                    .map((part: string) => part.charAt(0).toUpperCase())
                    .join(". ")}`}
                  .
                </Typography>
              )}
              <IconButton
                className={css.boxIconButton}
                aria-label="logout"
                onClick={handleLogout}
              >
                <LogoutIcon className={css.logoutIcon} />
              </IconButton>
            </Box>
          </Toolbar>
        </CustomAppBar>
      </Box>
    </Box>
  );
};

export default AppBar;

Hope that helps to track down the issue on Object.startKeepAliveServiceWorker.

Thank you helping!

HSULGIT commented 2 weeks ago

Till now i sadly get this error. All others are solved.

TypeError: Failed to fetch at we (:3000/static/js/bundle.js:137911:34) at r (:3000/static/js/bundle.js:137999:41) at Object.startKeepAliveServiceWorker (:3000/static/js/bundle.js:138034:42) at :3000/static/js/bundle.js:139207:178 at async s (:3000/static/js/bundle.js:139532:19) at async Sn `(:3000/static/js/bundle.js:139404:9)

It seems to happen when using logout("/", { id_token_hint: idToken }) used from const { logout, isAuthenticated } = useOidc(); const { idTokenPayload, idToken } = useOidcIdToken(); imported via import { useOidc, useOidcIdToken } from "@axa-fr/react-oidc";

@guillaume-chervet: Is there a solution to the Problem or are you still investigating on it?

Btw thanks for your great work with the package. The minSleepSeconds problem disappeared after updating to the newest version.