I figured out this error in my case, and just want to document it.
Similar problems
- What does "Session doesn't have required client" mean?: not the bug encountering in this case, I believe
- Why invalid_grant error with "Session doesn’t have required client"?: revoke refresh token is disabled in this case
- https://github.com/keycloak/keycloak/issues/27384: bug revolving CORS issues, I believe
Environment
- React Vite: template React + TS v5.3.1
- Keycloak (KC): running on Docker, v25.0.1
Problem
I need to implement OAuth 2, specifically the Authorization Code Flow and the Refreshing an Access Token Flow. First, I created a React public client, and ran a default KC instance, acting as my authorization server. The AuthZ Code Flow worked fine, and I received all the necessary data, including an access token and a refresh token.
To perform the next flow, I sent a POST request to KC's endpoint /token with the urlencoded body as grant_type=refresh_token;client_id=clientIdHere;refresh_token=refreshTokenHere, as instructed in here. For some reason, KC refused and returned an error like this:
{
"error": "invalid_grant",
"error_description": "Session doesn't have required client"
}
KC Image setup
Code
# running the default KC instance
docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak start-dev
# useEffect code
const navigate = useNavigate();
useEffect(() => {
const params = new URLSearchParams(window.location.search);
const authZCode = params.get("code");
const fetchDataToLS = async (codeHere: string) => {
const tokenRes = await axios.post<TKCTokenResponse>(
import.meta.env.VITE_KC_SERVER_TOKEN,
new URLSearchParams({
client_id: "myclient",
code: codeHere,
grant_type: "authorization_code",
})
);
const { access_token, refresh_token, id_token } = tokenRes.data;
localStorage.setItem("access_token", access_token);
localStorage.setItem("refresh_token", refresh_token);
localStorage.setItem("id_token", id_token);
navigate("/protected");
};
fetchDataToLS(authZCode!);
callTime++;
}, [callTime, navigate]);