Code

Create AuthInterceptor.kt — the token on every call

In the code column — AuthInterceptor.kt. It implements the Interceptor interface: intercept() receives every request, checks the path, and if it's not public — adds Authorization: Bearer <token>. The token comes from TokenStorage (lesson 8a).

Now the interceptor needs to be plugged into the network. Remember lesson 6's coreModule — it only had Retrofit and PicaApi. Now it grows to its real shape: an OkHttpClient with the interceptor, and Retrofit uses it:

// coreModule — add OkHttp with auth
single<OkHttpClient> {
    OkHttpClient.Builder()
        .addInterceptor(AuthInterceptor(get()))          // get() = TokenStorage (8a)
        .addInterceptor(HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY     // see requests in Logcat
        })
        .build()
}

single<Retrofit> {
    Retrofit.Builder()
        .baseUrl("http://10.0.2.2:8080/")
        .client(get())                                    // ← use the OkHttp with auth
        .addConverterFactory(get<Json>().asConverterFactory("application/json".toMediaType()))
        .build()
}

One line — .client(get()) — and every Retrofit request now goes through the interceptor. No ViewModel or repository changes.

Verify. Log in, then in Logcat (HttpLoggingInterceptor) look at a protected request — you should see the header:

--> GET http://10.0.2.2:8080/orders
Authorization: Bearer eyJhbGciOi...

While --> POST http://10.0.2.2:8080/login has no such header — because it's a public path.

Gotcha (chicken and egg). If the interceptor doesn't exclude /login and /register, it will try to attach a token to the login request too. But at login there's no token yet (or it's expired) — and the server returns 401. Result: you can't log in to get a token, because logging in requires... a token. A vicious cycle. That's why the publicPaths list is mandatory — it lets these paths through without the header.