transistorsoft / react-native-background-geolocation

Sophisticated, battery-conscious background-geolocation with motion-detection
http://shop.transistorsoft.com/pages/react-native-background-geolocation
MIT License
2.54k stars 424 forks source link

setConfig not always updating Authorization header #1980

Open Perronef5 opened 1 month ago

Perronef5 commented 1 month ago

Your Environment

Expected Behavior

When calling setConfig to update the configuration, particularly the Authorization token in the headers, the new configuration should be applied immediately to ensure all subsequent requests use the updated Authorization token.

Here is how we are updating the auth token after we see a 401 from the server:

    const state = await BackgroundGeolocation.getState()
    await BackgroundGeolocation.setConfig({
      ...state,
      headers: {
        Authorization: `Bearer ${token.accessToken}`,
      },
    })

Actual Behavior

After calling setConfig to update the Authorization token in the headers, the old token continues to be used for subsequent requests. This indicates that the new configuration settings, particularly the updated Authorization token, are not being applied as expected.

Steps to Reproduce

Context

The goal is to dynamically update the Authorization token in the headers of the plugin configuration to ensure that all subsequent requests use the most current Authorization token for authentication. It's important to note that not all users are experiencing this issue, but a significant portion are, which suggests the possibility of intermittent caching issues with the token update process. Additionally, our application is utilizing Redux for state management, which might be relevant in diagnosing the issue. The inconsistent behavior raises the question of whether the library might sometimes have difficulties properly caching or updating the token.

christocracy commented 1 month ago

is this issue for iOS or Android?

Perronef5 commented 1 month ago

is this issue for iOS or Android?

Mostly Android from what I can see but its definitely both iOS and Android.

We added a hack to reset the config if the state is not updated after setting the config. Hopefully that helps

christocracy commented 1 month ago

Show me all your code where you're handling 401 Unauthorized and what you're doing there.

Perronef5 commented 1 month ago
  const onHttp = useCallback(async (response: HttpEvent) => {
    if (response.status === 401) {
      /**
       * 401 is returned when the auth token has expired.
       * We need to re register and set the config again.
       */

       // HACK where we store the old auth token and try to force it into transistor since its not updating the token...
      const authToken = await getAsyncItem('mapping_auth_token')
      await BackgroundGeolocation.setConfig({
        headers: {
          Authorization: `Bearer ${authToken}`,
        },
      })
      Logger.breadcrumb('onHttp: 401 calling register')
      await register()
    }
  }, [])
      export const register = async () => {
      const somethingWeNeed = await getAsyncItem('something_we_need')

      const sessionKey = await generateSessionKey()

      const { data: initResponse } = await axios.post(
        `/first_step_in_register`,
      )

      await axios.post(
        `/second_step_in_register`,
        {
          sessionKey
        },
      )

     const {data: token } =  await axios.post(
        `/third_step_in_register`,
        {
          sessionKey
        },
      )

      // Cache token to try to stuff in later
      await setAsyncItem('mapping_auth_token', token.accessToken)

      await BackgroundGeolocation.setConfig({
        ...state,
        headers: {
          Authorization: `Bearer ${token.accessToken}`,
        },
      })

      const newState = await BackgroundGeolocation.getState()
      const headers: TransistorHeader = newState?.headers as TransistorHeader
      if (headers?.Authorization !== `Bearer ${token.accessToken}`) {
        // Tokens did not match 
        await BackgroundGeolocation.reset({
          ...newState,
          headers: {
            Authorization: `Bearer ${token.accessToken}`,
          },
        })
      }
}

Update:

We have discovered that some how the http callbacks are multi threaded and our logic was stepping on itself. Can you confirm that the onHttp runs on a multi thread?

Perronef5 commented 1 month ago

@christocracy Any update here?

christocracy commented 1 month ago

I don't like your use of reset() here and echoing back to the plugin its current state.

await BackgroundGeolocation.reset({
  ...newState,
  headers: {
    Authorization: `Bearer ${token.accessToken}`,
  },
})

Change it to this:

await BackgroundGeolocation.setConfig({
  headers: {
    Authorization: `Bearer ${token.accessToken}`,
  }
})
Perronef5 commented 1 month ago

@christocracy We only added that because the config was not being updated. What about the multi threaded part?

Perronef5 commented 1 month ago

@christocracy Any update?

christocracy commented 1 month ago

I have no update

Perronef5 commented 1 month ago

Can you confirm that the onHttp callback is happening on a multi thread? It would help for that to be in the documentation if it is

christocracy commented 1 month ago

callback is happening on a multi thread?

What do you mean by this? All event callbacks must be executed in the Main Thread (aka “UI thread”)

Perronef5 commented 1 month ago

callback is happening on a multi thread?

What do you mean by this? All event callbacks must be executed in the Main Thread (aka “UI thread”)

In our logs we see multiple instances of the http callback stepping on itself so we're thinking it was multi threaded on the native level

christocracy commented 1 month ago

we see multiple instances of the http callback stepping on itself

what makes you think that?

this plug-in takes great care with multi-threading.

Perronef5 commented 1 month ago

we see multiple instances of the http callback stepping on itself

what makes you think that?

this plug-in takes great care with multi-threading.

Our register logic requires basic nonce / session key logic. In order to establish a session key we need the nonce and once we have a session key we can issue a JWT token. This a 3 step http request process where each http request is dependent off of the one that comes before it. You can imagine how multiple onHttp callbacks would cause this logic to invalidate itself.

It would help to make it clear to the consumers that the onHttp callbacks are being called on multiple threads.

github-actions[bot] commented 6 days ago

This issue is stale because it has been open for 30 days with no activity.