skip to Main Content

I’m using Keycloak in React to authenticate users, when I use <React.StrictMode> I get the following error:

Failed to initialize adapter: Error: A ‘Keycloak’ instance can only be initialized once.

First, I defined the Keycloak instance in component and at the highest level and outside of useEffect.
Then I created the Keycloak config in a separate file as below and inserted it into component, still the problem persists.

Keycloak config file

import Keycloak from 'keycloak-js';

const keycloakConfig = {
    url: 'http://192.168.12.231:8080/auth/',
    realm: 'MyRelm',
    clientId: 'client',
};
const keycloak = new Keycloak(keycloakConfig);

export default keycloak;

component

import React, { useState, useEffect } from "react";
import { NavLink, Outlet } from "react-router-dom";
import keycloak from "../keycloakConfig";
import { jwtDecode } from "jwt-decode";

function Management() {
    const [authenticated, setAuthenticated] = useState(false);
    const [userRoles, setUserRoles] = useState([]);
    const [fullName, setFullName] = useState('');

    useEffect(() => {
        const initKeycloak = async () => {
            try {
                const authenticated = await keycloak.init({ onLoad: 'check-sso', initOptions: { pkceMethod: 'S256', checkLoginIframe: false } });
                console.log(`User is ${authenticated ? 'authenticated' : 'not authenticated'}`);
                setAuthenticated(authenticated);

                if (authenticated) {
                    const token = keycloak.token;
                    const decodedToken = jwtDecode(token);
                    const roles = decodedToken.realm_access.roles;
                    const name = decodedToken.name
                    setUserRoles(roles);
                    setFullName(name);
                }
            } catch (error) {
                console.error('Failed to initialize adapter:', error);
            }
        };

        initKeycloak();
    }, []);

    const handleLogout = () => {
        keycloak.logout();
    };

    const handleLogin = () => {
        keycloak.login();
    };

    return (
        <div className="panel app">
            <div className="sidebar">
                <NavLink to={"/"}> Welcome ! </NavLink>
                <NavLink to={"/managment"}> Project Information </NavLink>
                <NavLink to={"managment/user"}> User </NavLink>
                <NavLink to={"managment/groups"}> Groups </NavLink>
            </div>
            <div className="content">
                {authenticated ? (
                    <Outlet />
                ) : (
                    <p>You do not have permission to access this content!</p>
                )}
            </div>
            <div className="user-info">
                {authenticated ? (
                    <>
                        <h3>{fullName}</h3>
                        <h5>User Roles:</h5>
                        <ul>
                            {userRoles.map((role, index) => (
                                <li key={index}>{role}</li>
                            ))}
                        </ul>
                        <button className="logOut" onClick={handleLogout}>Log Out</button>
                    </>
                ) : (
                    <button className="logOut" onClick={handleLogin}>Log In</button>
                )}

            </div>
        </div>
    );
}

export default Management;

I’m using Keycloak in React to authenticate users.

2

Answers


  1. Chosen as BEST ANSWER

    I think I found the answer to my question in the following post. https://stackoverflow.com/a/72585970/9897918

    React 18 now has Strict.Mode which can mount, unmount, and remount components which causes the abortController to issue an error on the first unmount. Remember, this only happens in development mode when Strict.Mode is applied in your index.js. We can check for that behaviour while in development-mode. https://reactjs.org/docs/strict-mode.html#ensuring-reusable-state


  2. In Strict.Mode, the problem was solved in the following way

    I created a useRef variable and set its value to false

    const didInit = useRef(false);
    

    I checked the defined variable before initialization Keycloak

     if (!didInit.current)
    

    If the value of the variable is false, I True it and do init

    didInit.current = true;
    

    The above code was edited as follows

     const didInit = useRef(false);
    
    
    
    useEffect(() => {
        const initKeycloak = async () => {
          try {
            if (!didInit.current) {
              didInit.current = true;
              const auth = await keycloak.init({ onLoad: 'check-sso', initOptions: { pkceMethod: 'S256', checkLoginIframe: false } });
              console.log(`User is ${auth ? 'authenticated' : 'not authenticated'}`);
              setAuthenticated(auth);
    
              if (auth) {
                const token = keycloak.token;
                const decodedToken = jwtDecode(token);
                const roles = decodedToken.realm_access.roles;
                const name = decodedToken.name
                setUserRoles(roles);
                setFullName(name);
              }
            }
    
          } catch (error) {
            console.error('Failed to initialize adapter:', error);
          }
        };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search