authts / oidc-client-ts

OpenID Connect (OIDC) and OAuth2 protocol support for browser-based JavaScript applications
https://authts.github.io/oidc-client-ts/
Apache License 2.0
1.37k stars 207 forks source link

Custom State store doesn't seems to propagate correctly #939

Closed minuz closed 9 months ago

minuz commented 1 year ago

Hi, I believe I found an bug on the library and here's a bit of a context. My team created a wrapper around this library so we can have a consolidated way to provide authentication through our react apps. On react apps, this wrapper works fine, but I started to look into a new mobile app (react-native) and wanted to use the wrapper, but I keep getting:

 ERROR  ReferenceError: Can't find variable: localStorage

So I started to dig a bit and the wrapper essentially receives an extended version of UserManagerSettings interface, which then is passed into the UserManager class.

const useAuthState = (props: AuthProviderProps) => {
  const { onSigninCallback, onRemoveUser, ...config } = props;
  const [userManager] = useState(() => new UserManager(config));
  const [state, dispatch] = useReducer(reducer, initialAuthState);

  const api = useUserManager(userManager, dispatch, state, onRemoveUser);
  useSigninCallback({ userManager, dispatch, onSigninCallback });
  useUserManagerEvents(userManager, dispatch);

  return { ...state, ...api };
};

On the implementation side:

  <AuthProvider {...AUTH_CONFIG}>
    <StatusBar barStyle="dark-content" />
    <SafeAreaView style={styles.safeAreaView}>
      <AppContent />
    </SafeAreaView>
  </AuthProvider>

AUTH_CONFIG:

export const AUTH_CONFIG: UserManagerSettings = {
  ...
  userStore: new WebStorageStateStore({ store: new InMemoryWebStorage() }),
};

So I believe the bug comes when setting the userStore property from the UserManager. The UserManager class consumes the value provided by the config. However, internally on the UserManager, it creates a new instance of OidcClient with the same config and on its constructor, creates an instance of OidcClientSettingsStore, again, with the same settings, except that at this stage, the property is no longer called userStore and it's now stateStore and since its value is not manually set from the userStore prop, it comes as undefined and therefore, the following logic will always fall into the default implementation:

https://github.com/authts/oidc-client-ts/blob/b3337c85c1a4e412a5077e20efd2217280fc8a51/src/OidcClientSettings.ts#L275

    // OidcClientSettings.ts
    ...
    if (stateStore) {
        this.stateStore = stateStore;
    }
    else {
        const store = typeof window !== "undefined" ? window.localStorage : new InMemoryWebStorage();
        this.stateStore = new WebStorageStateStore({ store });
    }

Unless I mis-read something, I believe that's the cause for my wrapper not to work on my mobile as the window object does exist, but the localStorage doesn't thus throwing the error.

Please let me know if you need more information about it.

pamapa commented 1 year ago

I think you are confused with userStore and stateStore. userStore is where the user (id token, access token) is stored. stateStore is only used transient for the authorization process, such that the library knows which data were initially sent to the authz server and now when the authz server calls back we need to know...

pamapa commented 9 months ago

This issue (question) staled, closing it for now.