Mehedi Hassan Piash [Sr. Software Engineer]

November 19, 2022

Authenticate api if accessToken expired in retrofit api client in android [Kotlin]

November 19, 2022 Posted by Piash , No comments

 

// retrofit
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0"
implementation "com.squareup.okhttp3:logging-interceptor:4.9.3"
/**
* Interceptor to add auth token to requests
*/

class AuthInterceptor(context: Context) : Interceptor {
private val sessionManager = SessionManager(context)

override fun intercept(chain: Interceptor.Chain): Response {
val requestBuilder = chain.request().newBuilder()
sessionManager.fetchAuthToken()?.let {
requestBuilder.addHeader(
"Authorization", "Bearer $it"
)
}
return chain.proceed(requestBuilder.build())
}
}
/**
* Authenticator to get the RefreshToken if current token is expired
*/

class AuthenticateApi(context: Context) : Authenticator {
private val sessionManager = SessionManager(context)

override fun authenticate(route: Route?, response: Response): Request? {
val apiClient = Retrofit.Builder().baseUrl(ApiUrl.BASE_URL) // api base url
.addConverterFactory(GsonConverterFactory.create()).build()
.create(ApiService::class.java)
return runBlocking {
// call login api again for getting accessToken in runBlocking so that it will wait until api response come
val apiResponse = apiClient.login(LoginBody("piash599@gmail.com", "p1234", "user"))
if (apiResponse.isSuccess) {
val accessToken = apiResponse.data?.accessToken
accessToken?.let {
sessionManager.saveAuthToken(accessToken)
}
response.request.newBuilder().addHeader(
"Authorization", "Bearer $accessToken"
).build()
} else {
null
}
}
}
}
interface ApiService {
@POST(ApiUrl.USER_LOGIN)
suspend fun login(@Body loginBody: LoginBody): BaseModel<LoginResponse>
}
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
/**
* Provides BaseUrl as string
*/

@Singleton
@Provides
fun provideBaseURL(): String {
return ApiUrl.BASE_URL
}

/**
* Provides LoggingInterceptor for api information
*/

@Singleton
@Provides
fun provideLoggingInterceptor(): HttpLoggingInterceptor {
return HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
}

/**
* Provides Auth interceptor for access token
*/

@Singleton
@Provides
fun provideAuthInterceptor(@ApplicationContext context: Context): AuthInterceptor {
return AuthInterceptor(context)
}

/**
* Provides AuthenticateApi to check api is authenticate or not
*/

@Singleton
@Provides
fun provideAuthenticateApi(@ApplicationContext context: Context): AuthenticateApi {
return AuthenticateApi(context)
}

/**
* Provides custom OkkHttp
*/

@Singleton
@Provides
fun provideOkHttpClient(
loggingInterceptor: HttpLoggingInterceptor,
authInterceptor: AuthInterceptor,
authenticateApi: AuthenticateApi
)
: OkHttpClient {
val okHttpClient = OkHttpClient().newBuilder()

okHttpClient.callTimeout(40, TimeUnit.SECONDS)
okHttpClient.connectTimeout(40, TimeUnit.SECONDS)
okHttpClient.readTimeout(40, TimeUnit.SECONDS)
okHttpClient.writeTimeout(40, TimeUnit.SECONDS)
okHttpClient.addInterceptor(loggingInterceptor)
okHttpClient.addInterceptor(authInterceptor)
okHttpClient.authenticator(authenticateApi)
okHttpClient.build()
return okHttpClient.build()
}
/**
* Provides converter factory for retrofit
*/

@Singleton
@Provides
fun provideConverterFactory(): Converter.Factory {
return GsonConverterFactory.create()
}

/**
* Provides ApiServices client for Retrofit
*/

@Singleton
@Provides
fun provideRetrofitClient(
baseUrl: String, okHttpClient: OkHttpClient, converterFactory: Converter.Factory
)
: Retrofit {
return Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpClient)
.addConverterFactory(converterFactory)
.build()
}
/**
* Provides Api Service using retrofit
*/

@Singleton
@Provides
fun provideRestApiService(retrofit: Retrofit): ApiService {
return retrofit.create(ApiService::class.java)
}

}
/**
* Session manager to save and fetch data from SharedPreferences
*/

class SessionManager (context: Context) {
private var prefs: SharedPreferences = context.getSharedPreferences(context.getString(R.string.app_name), Context.MODE_PRIVATE)

companion object {
const val USER_TOKEN = "user_token"
}

/**
* Function to save auth token
*/

fun saveAuthToken(token: String) {
val editor = prefs.edit()
editor.putString(USER_TOKEN, token)
editor.apply()
}

/**
* Function to fetch auth token
*/

fun fetchAuthToken(): String? {
return prefs.getString(USER_TOKEN, null)
}

/**
* Function to fetch auth token
*/

fun clearAuthToken() {
prefs.edit().clear().commit()
}
}

0 comments:

Post a Comment