Basic Auth with OkHttp and Retrofit

November 5, 2022

This post contains a basic example of performing an HTTP GET request to an endpoint that requires Basic Authentication in a Kotlin Android project using the following libraries:

  • OkHttp

  • Retrofit

  • Dagger/Hilt

  • Moshi (or another JSON library, if applicable)

Gradle dependencies

Use the most current versions that work for your project.

Top-level build.gradle

plugins {
    ...
    id 'com.google.dagger.hilt.android' version '2.44' apply false
}

App-level build.gradle

plugins {
    ...
    id 'dagger.hilt.android.plugin'
}


dependencies {
    ...
    implementation 'com.google.dagger:hilt-android:2.44'
    kapt 'com.google.dagger:hilt-compiler:2.44'

    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.okhttp3:okhttp:4.9.3'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.9.3'

    implementation 'com.squareup.moshi:moshi-kotlin:1.14.0'
    implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
    kapt 'com.squareup.moshi:moshi-kotlin-codegen:1.14.0'
}

Define your model (if applicable)

We will be loading a list of Lessons. The payload shape is as follows:

[
  {
     "lesson_id": 1,
     "name": "English 101"
  },
  {
     "lesson_id": 2,
     "name": "English 102"
  }
]

The Moshi annotations used below are only applicable in our API contract.

@JsonClass(generateAdapter = true)
data class Lesson(
  @Json(name = "lesson_id")
  val id: Int,
  val name: String
)

Set up Retrofit Interface

private const val FETCH_LESSONS_URL = "/api/lessons"

interface LessonsApi {
  @Headers("Content-Type: application/json;charset=UTF-8")
  @GET("$BASE_URL$FETCH_LESSONS_URL")
  suspend fun getLessons(
    @Query("date") date: String
  ): List<Lesson>
}

Configure Dagger Module

Let’s make the outgoing requests go through a custom Interceptor which sets the “Authorization” header using Credentials.basic(username, password)

const val BASE_URL = "https://example.com"

@Module
@InstallIn(SingletonComponent::class)
class AppModule {

  private val loggingInterceptor = HttpLoggingInterceptor().apply {
    level = HttpLoggingInterceptor.Level.BODY
  }

  private val okHttpBuilder = OkHttpClient.Builder()
    .addInterceptor(loggingInterceptor)

  private val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .build()

  @Provides
  fun provideLessonsApi(): LessonsApi {
    okHttpBuilder.addInterceptor(
      BasicAuthInterceptor(
        BuildConfig.BASIC_AUTH_USERNAME,
        BuildConfig.BASIC_AUTH_PASSWORD
      )
    )

    return Retrofit.Builder()
      .baseUrl(BASE_URL)
      .addConverterFactory(MoshiConverterFactory.create(moshi))
      .client(okHttpBuilder.build())
      .build()
      .create(LessonsApi::class.java)
  }
}

class BasicAuthInterceptor(username: String, password: String) : Interceptor {
  private val credentials: String = Credentials.basic(username, password)

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val request: Request = chain.request()
    val authenticatedRequest: Request = request.newBuilder()
      .header("Authorization", credentials).build()
    return chain.proceed(authenticatedRequest)
  }
}

Define Repository

We’ll define a Repository interface and its implementation. We’ll be injecting the interface in a ViewModel.

interface LessonsRepository {
  suspend fun getLessons(date: String): Result<List<Lesson>>
}
class LessonsRepositoryImpl @Inject constructor(
  val api: LessonsApi
) : LessonsRepository {
  override suspend fun getLessons(date: String): Result<List<Lesson>> {
    return runCatching {
      api.getLessons(date)
    }
  }
}

Make the LessonsRepository injectable via interface:

@Module
@InstallIn(SingletonComponent::class)
abstract class RepoModule {

  @Binds
  abstract fun bindsLessonsRepository(repo: LessonsRepositoryImpl): LessonsRepository
}

And use the repository in your ViewModel:

@HiltViewModel
class LessonsViewModel @Inject constructor(
  private val lessonsRepository: LessonsRepository
) : ViewModel()

That’s it. Now your LessonsRepository lookups will be authenticated using Basic Authentication.

Previous
Previous

Compose by example: Intrinsics

Next
Next

Animation in Jetpack Compose