J86
J86

Reputation: 15237

ReactJS with IdentityServer4 and oidc-client-js Error: "No code in response"

I am trying to get ReactJS working with IdentityServer4 via oidc-client-js library.

PROBLEM

I click the login button and I get redirected to IdentityServer4, once I login, I get redirect to /login-callback and then from there I get redirected to / but I get the following error in the console:

reactjs error no state in response

I am not sure what I'm doing wrong, but I've tried all sorts and nothing seems to work.

CODE

All code is open-source and sits here.

App.jsx

// other code ommited, but this is a route
<AuthHandshake path="/login-callback" />

AuthHandshake.jsx

import React from "react";
import { AuthConsumer } from "../AuthProvider/AuthProvider";

function AuthHandshake() {
  return <AuthConsumer>{value => value.loginCallback()}</AuthConsumer>;
}

export default AuthHandshake;

AuthProvider.jsx

import React, { useState } from "react";
import { navigate } from "@reach/router";
import { UserManager, WebStorageStateStore } from "oidc-client";
import AuthContext from "../../contexts/AuthContext";
import { IDENTITY_CONFIG } from "../../utils/authConfig";

IDENTITY_CONFIG.userStore = new WebStorageStateStore({
  store: window.localStorage
});

const userManager = new UserManager(IDENTITY_CONFIG);

const login = () => {
  console.log("Login button click handled.");
  userManager.signinRedirect();
};

const logout = () => {
  userManager.signoutRedirect();
};

export function AuthProvider(props) {
  const [user, setUser] = useState(null);

  const loginCallback = () => {
    userManager.signinRedirectCallback().then(
      user => {
        window.history.replaceState(
          {},
          window.document.title,
          window.location.origin
        );
        setUser(user);
        navigate("/");
      },
      error => {
        console.error(error);
      }
    );
  };

  const providerValue = {
    login: login,
    logout: logout,
    loginCallback: loginCallback,
    isAuthenticated: user
  };

  const Provider = () => {
    if (user) {
      return (
        <AuthContext.Provider value={providerValue}>
          {props.children}
        </AuthContext.Provider>
      );
    } else {
      return <div className="auth-provider">{props.children}</div>;
    }
  };

  return (
    <>
      <AuthContext.Provider value={providerValue}>
        {props.children}
      </AuthContext.Provider>
    </>
  );
}

export const AuthConsumer = AuthContext.Consumer;

On the IdentityServer side, I have set the post logout redirect to the same thing /login-callback

new Client
{
  ClientId = "bejebeje-react-local",
  ClientName = "Bejebeje ReactJS SPA Client",
  AllowedGrantTypes = GrantTypes.Code,
  RequirePkce = true,
  RequireClientSecret = false,
  RequireConsent = false,
  RedirectUris = { "http://localhost:1234/login-callback" },
  PostLogoutRedirectUris = { "http://localhost:1234/logout-callback" },
  AllowedCorsOrigins = { "http://localhost:1234" },
  AllowedScopes = { "openid", "profile", "bejebeje-api-local" },
  AllowOfflineAccess = true,
  RefreshTokenUsage = TokenUsage.ReUse,
}

Where am I going wrong?

Upvotes: 2

Views: 5570

Answers (2)

Charitha Goonewardena
Charitha Goonewardena

Reputation: 4714

I had to add response_mode: 'query' to the UserManager.

var mgr = new Oidc.UserManager({ response_mode: 'query' });
mgr.signinRedirectCallback().then(res => {
    window.location = "/signin-callback";
}).catch(error => {
    window.location = "/";
})

Upvotes: 2

hashbytes
hashbytes

Reputation: 799

Try this here.. https://github.com/JwanKhalaf/Bejebeje.React/blob/bug/fix-no-code-response-on-redirect/src/components/AuthProvider/AuthProvider.jsx#L15

Basically, when you are sending a login request from the client (OIDC-client.js), the client sends a unique identifier (State) along with the login request in the URL. The client saves this value in the storage option you choose (local/session). After successful sign-in, server issues tokens in the response redirect url and it also includes the unique identifier client initially sent in login request. Watch the login request url and response url in chrome Developer Tools.

When OIDC Client Library receives the sign-in response, it grabs the unique identifier from the URL and matches with the value it kept in the local/session storage at the time of login request. If they don't match, the client will not accept the token issued by the server.

This is just one level of additional security to make sure, the client is not accepting any tokens issued by any random server or any bookmarked URL.

Hope this helps.!!

usermanager.signinRedirect({ state: { bar: 15 } });

Upvotes: 2

Related Questions