Home > Back-end >  Ktor - NullPointerException with Bearer Token
Ktor - NullPointerException with Bearer Token

Time:09-24

I'm trying to implement a Login Process in Android with Ktor, where when the user logs-in, some user preferences are fetched from a server. I am using MVI architecture with State Events that are triggered either from the Fragment, or from the ViewModel, in a modular design.

The steps go like this:

  1. Press Login button
  2. Trigger "Login" Event
  3. Implement "Login" Use Case
  4. Save Token in Encrypted Shared Preferences
  5. Change LoginState "isLoggedIn" boolean value to "true"
  6. Observe this change in the Fragment where I'm collecting the Login State
  7. Trigger the "GetPreferences" Event
  8. Implement "GetPreferences" Use Case by fetching the User Prefs from the Server.

I'm getting an exception that the fetching is Unauthorized. It seems that the token in the service builder is null, where it should not be. From debugging, it also seems that the builder is initiated before any action, so it might take a null value from the token, and then not take the new one.

I also tried to Trigger the "GetPreferences" Event after Logging-in, in the Home Screen, and I don't have this problem. (but I want to first get the Prefs, and then if the call is successful go to Home Screen)

Any ideas?

I am getting this exception:

2022-09-21 14:00:17.216 19987-19987/? W/System.err: java.lang.NullPointerException
2022-09-21 14:00:17.216 19987-19987/? W/System.err:     at com.example.workoutplans_datasource.network.WorkoutPlanService$Factory$build$1$3$1$1.invokeSuspend(WorkoutPlanService.kt:65)

This is the Factory from the WorkoutPlanService:

companion object Factory {
    fun build(
       sessionManager: SessionManager
    ): WorkoutPlanService {

        val token = sessionManager.getToken()

        return WorkoutPlanServiceImpl(
            httpClient = HttpClient(Android) {

                install(Logging) {
                    level = LogLevel.ALL
                }

                install(HttpTimeout) {
                    requestTimeoutMillis = 15000L
                    connectTimeoutMillis = 15000L
                    socketTimeoutMillis = 15000L
                }

                install(Auth) {
                    bearer {
                        loadTokens {
                            BearerTokens(token!!, token)
                        }
                    }
                }

                install(JsonFeature) {
                    serializer = KotlinxSerializer(
                        kotlinx.serialization.json.Json {
                            ignoreUnknownKeys =
                                true // if the server returns extra fields, ignore them
                        }
                    )
                }
            }
        )
    }
}

CodePudding user response:

The problem is that the Service is built before a token is generated, therefore the token is null and the NullPointerException is thrown.

I changed the code so that token is a lateinit var, and the getToken function is inside the bearer config, like that, and it works:

    companion object Factory {
        fun build(
           sessionManager: SessionManager
        ): WorkoutPlanService {


            return WorkoutPlanServiceImpl(
                httpClient = HttpClient(Android) {

                    install(Logging) {
                        logger = Logger.DEFAULT
                        level = LogLevel.HEADERS
                    }

                    install(HttpTimeout) {
                        requestTimeoutMillis = 15000L
                        connectTimeoutMillis = 15000L
                        socketTimeoutMillis = 15000L
                    }

                    install(Auth) {

                        lateinit var token: String

                        bearer {
                            loadTokens {
                                token = sessionManager.getToken().toString()
                                BearerTokens(accessToken = token, refreshToken = token)
                            }
                        }
                    }

                    install(JsonFeature) {
                        serializer = KotlinxSerializer(
                            kotlinx.serialization.json.Json {
                                ignoreUnknownKeys =
                                    true // if the server returns extra fields, ignore them
                            }
                        )
                    }
                }
            )
        }
    }
}
  • Related