Skip to content

Commit

Permalink
[feature/refresh-token] 토큰 자동 갱신 로직 추가 및 자동 로그인 기능 추가 (#390)
Browse files Browse the repository at this point in the history
* 토큰 자동 갱신 로직 추가

* Qualifier 수정하고 자동로그인 로직 추가

* remove useless expression

* remove useless expressions and apply ktlint
  • Loading branch information
l2hyunwoo authored Oct 8, 2023
1 parent 5c5e041 commit 1a5c03b
Show file tree
Hide file tree
Showing 11 changed files with 112 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.sopt.official.data.authenticator

import android.content.Context
import com.jakewharton.processphoenix.ProcessPhoenix
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.runBlocking
import okhttp3.Authenticator
import okhttp3.Request
import okhttp3.Response
import okhttp3.Route
import org.sopt.official.common.di.Auth
import org.sopt.official.data.model.request.RefreshRequest
import org.sopt.official.data.persistence.SoptDataStore
import org.sopt.official.data.service.AuthService
import org.sopt.official.feature.auth.AuthActivity
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class SoptAuthenticator @Inject constructor(
private val dataStore: SoptDataStore,
@Auth(false) private val service: AuthService,
@ApplicationContext private val context: Context
) : Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
if (response.code == 401) {
val refreshToken = dataStore.refreshToken
val newTokens = runCatching {
runBlocking {
service.refresh(RefreshRequest(refreshToken))
}
}.onSuccess {
dataStore.refreshToken = it.refreshToken
dataStore.accessToken = it.accessToken
dataStore.playgroundToken = it.playgroundToken
}.onFailure {
dataStore.clear()
Timber.e(it)
ProcessPhoenix.triggerRebirth(context, AuthActivity.newInstance(context))
}.getOrThrow()

return response.request.newBuilder()
.header("Authorization", newTokens.accessToken)
.build()
}
return null
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.sopt.official.data.repository

import org.sopt.official.common.di.Auth
import org.sopt.official.data.model.request.RefreshRequest
import org.sopt.official.data.persistence.SoptDataStore
import org.sopt.official.data.service.AuthService
Expand All @@ -9,11 +10,12 @@ import org.sopt.official.domain.repository.AuthRepository
import javax.inject.Inject

class AuthRepositoryImpl @Inject constructor(
private val service: AuthService,
@Auth private val service: AuthService,
@Auth(false) private val noneAuthService: AuthService,
private val dataStore: SoptDataStore
) : AuthRepository {
override suspend fun refresh(token: String) = runCatching {
service.refresh(RefreshRequest(token)).toEntity()
noneAuthService.refresh(RefreshRequest(token)).toEntity()
}

override fun save(token: Token) {
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/java/org/sopt/official/di/AuthModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@ import javax.inject.Singleton
object AuthModule {
@Provides
@Singleton
@Auth
fun provideAuthService(@AppRetrofit retrofit: Retrofit): AuthService = retrofit.create(AuthService::class.java)

@Provides
@Singleton
@Auth(false)
fun provideNoneAuthService(@AppRetrofit(false) retrofit: Retrofit): AuthService = retrofit.create(AuthService::class.java)

@Provides
@Singleton
fun provideAuthRepository(repository: AuthRepositoryImpl): AuthRepository = repository
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/org/sopt/official/di/HomeModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ import javax.inject.Singleton
object HomeModule {
@Provides
@Singleton
fun provideMainViewService(
fun provideHomeService(
@AppRetrofit retrofit: Retrofit
): HomeService = retrofit.create(HomeService::class.java)

@Provides
@Singleton
fun provideMainRepository(repository: DefaultHomeRepository): HomeRepository = repository
fun provideHomeRepository(repository: DefaultHomeRepository): HomeRepository = repository
}
30 changes: 28 additions & 2 deletions app/src/main/java/org/sopt/official/di/NetModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import org.sopt.official.common.di.AppRetrofit
import org.sopt.official.common.di.Auth
import org.sopt.official.common.di.Logging
import org.sopt.official.common.di.OperationRetrofit
import org.sopt.official.data.authenticator.SoptAuthenticator
import retrofit2.Converter
import retrofit2.Retrofit
import javax.inject.Singleton
Expand All @@ -32,12 +33,25 @@ object NetModule {

@Provides
@Singleton
@Auth
fun provideOkHttpClient(
@Logging loggingInterceptor: Interceptor,
@Auth authInterceptor: Interceptor,
authenticator: SoptAuthenticator
): OkHttpClient = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.addInterceptor(authInterceptor)
.authenticator(authenticator)
.apply { FlipperInitializer.addFlipperNetworkPlugin(this) }
.build()

@Provides
@Singleton
@Auth(false)
fun provideNonAuthOkHttpClient(
@Logging loggingInterceptor: Interceptor,
): OkHttpClient = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.apply { FlipperInitializer.addFlipperNetworkPlugin(this) }
.build()

Expand All @@ -57,7 +71,19 @@ object NetModule {
@Provides
@Singleton
fun provideAppRetrofit(
client: OkHttpClient,
@Auth client: OkHttpClient,
converter: Converter.Factory
): Retrofit = Retrofit.Builder()
.client(client)
.addConverterFactory(converter)
.baseUrl(if (BuildConfig.DEBUG) BuildConfig.devApi else BuildConfig.newApi)
.build()

@AppRetrofit(false)
@Provides
@Singleton
fun provideNoneAuthAppRetrofit(
@Auth(false) client: OkHttpClient,
converter: Converter.Factory
): Retrofit = Retrofit.Builder()
.client(client)
Expand All @@ -69,7 +95,7 @@ object NetModule {
@Provides
@Singleton
fun provideOperationRetrofit(
client: OkHttpClient,
@Auth client: OkHttpClient,
converter: Converter.Factory
): Retrofit = Retrofit.Builder()
.client(client)
Expand Down
14 changes: 14 additions & 0 deletions app/src/main/java/org/sopt/official/feature/auth/AuthActivity.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.sopt.official.feature.auth

import android.animation.ObjectAnimator
import android.content.Context
import android.content.Intent
import android.graphics.Paint
import android.os.Bundle
import android.view.animation.AnimationUtils
Expand All @@ -18,6 +20,7 @@ import org.sopt.official.R
import org.sopt.official.auth.PlaygroundAuth
import org.sopt.official.auth.data.PlaygroundAuthDatasource
import org.sopt.official.auth.data.remote.model.response.OAuthToken
import org.sopt.official.common.di.Auth
import org.sopt.official.data.model.request.AuthRequest
import org.sopt.official.data.persistence.SoptDataStore
import org.sopt.official.data.service.AuthService
Expand All @@ -36,6 +39,7 @@ class AuthActivity : AppCompatActivity() {
private val binding by viewBinding(ActivityAuthBinding::inflate)
private val viewModel by viewModels<AuthViewModel>()

@Auth
@Inject
lateinit var authService: AuthService

Expand All @@ -44,6 +48,9 @@ class AuthActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (dataStore.accessToken.isNotEmpty()) {
startActivity(HomeActivity.getIntent(this, UserStatus.valueOf(dataStore.userStatus)))
}
setContentView(binding.root)
initUi()
initAnimation()
Expand Down Expand Up @@ -119,4 +126,11 @@ class AuthActivity : AppCompatActivity() {
startActivity(HomeActivity.getIntent(this, UserStatus.UNAUTHENTICATED))
}
}

companion object {
@JvmStatic
fun newInstance(context: Context) = Intent(context, AuthActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,9 @@ class HomeActivity : AppCompatActivity() {
descriptionSmall.text = item.description?.let { stringOf(it) }
root.setOnSingleClickListener {
tracker.track(
type = EventType.CLICK, name = item.clickEventName, properties = mapOf("view_type" to args?.value)
type = EventType.CLICK,
name = item.clickEventName,
properties = mapOf("view_type" to args?.value)
)
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(item.url))
startActivity(intent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ annotation class Logging

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class Auth
annotation class Auth(val needed: Boolean = true)

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AppRetrofit
annotation class AppRetrofit(val authNeeded: Boolean = true)

@Qualifier
@Retention(AnnotationRetention.BINARY)
Expand Down
1 change: 1 addition & 0 deletions feature/auth/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ android {
}

dependencies {
implementation(projects.core.common)
implementation(libs.customtab)
implementation(libs.retrofit)
implementation(libs.retrofit.kotlin.serialization.converter)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import org.sopt.official.auth.PlaygroundError
import org.sopt.official.auth.data.remote.AuthService
import org.sopt.official.auth.data.remote.model.request.RequestToken
import org.sopt.official.auth.data.remote.model.response.OAuthToken
import org.sopt.official.common.di.Auth
import java.net.UnknownHostException

internal class RemotePlaygroundAuthDatasource(
private val authService: AuthService
@Auth private val authService: AuthService
) : PlaygroundAuthDatasource {

override suspend fun oauth(code: String): Result<OAuthToken> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ internal object RemoteModule {
@Provides
@Singleton
fun provideUserService(
@AppRetrofit retrofit: Retrofit
@AppRetrofit(true) retrofit: Retrofit
): SoptampUserService = retrofit.create(SoptampUserService::class.java)
}

0 comments on commit 1a5c03b

Please sign in to comment.