From 24103742444d021f059b6650017b142799e5d574 Mon Sep 17 00:00:00 2001 From: algosketch Date: Wed, 22 Nov 2023 22:10:24 +0900 Subject: [PATCH 01/11] =?UTF-8?q?refactor=20:=20authRepositoryImpl?= =?UTF-8?q?=EA=B3=BC=20data=20store=20=EC=82=AC=EC=9D=B4=EC=97=90=20dataSo?= =?UTF-8?q?urce=20=EB=86=93=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/datasource/TokenLocalDataSource.kt | 28 +++++++++++++++++++ .../data/repository/AuthRepositoryImpl.kt | 17 ++++------- 2 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 android/core/data/src/main/java/com/ohdodok/catchytape/core/data/datasource/TokenLocalDataSource.kt diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/datasource/TokenLocalDataSource.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/datasource/TokenLocalDataSource.kt new file mode 100644 index 0000000..6a728a0 --- /dev/null +++ b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/datasource/TokenLocalDataSource.kt @@ -0,0 +1,28 @@ +package com.ohdodok.catchytape.core.data.datasource + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +class TokenLocalDataSource @Inject constructor( + private val dataStore: DataStore +) { + + private val idTokenKey = stringPreferencesKey("idToken") + private val accessTokenKey = stringPreferencesKey("accessToken") + + suspend fun saveAccessToken(token: String) { + dataStore.edit { preferences -> preferences[accessTokenKey] = token } + } + + suspend fun saveIdToken(token: String) { + dataStore.edit { preferences -> preferences[idTokenKey] = token } + } + + suspend fun getIdToken(): String = + dataStore.data.map { preferences -> preferences[idTokenKey] ?: "" }.first() +} \ No newline at end of file diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt index 7206729..f4de1b0 100644 --- a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt +++ b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt @@ -1,10 +1,8 @@ package com.ohdodok.catchytape.core.data.repository -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit -import androidx.datastore.preferences.core.stringPreferencesKey import com.ohdodok.catchytape.core.data.api.UserApi +import com.ohdodok.catchytape.core.data.datasource.TokenLocalDataSource import com.ohdodok.catchytape.core.data.model.LoginRequest import com.ohdodok.catchytape.core.data.model.SignUpRequest import com.ohdodok.catchytape.core.domain.repository.AuthRepository @@ -12,17 +10,13 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map -import java.lang.RuntimeException import javax.inject.Inject class AuthRepositoryImpl @Inject constructor( private val userApi: UserApi, - private val preferenceDataStore: DataStore + private val tokenDataSource: TokenLocalDataSource, ) : AuthRepository { - private val idTokenKey = stringPreferencesKey("idToken") - private val accessTokenKey = stringPreferencesKey("accessToken") - override fun loginWithGoogle(googleToken: String): Flow = flow { val response = userApi.login(LoginRequest(idToken = googleToken)) if (response.isSuccessful) { @@ -51,15 +45,14 @@ class AuthRepositoryImpl @Inject constructor( override suspend fun saveAccessToken(token: String) { - preferenceDataStore.edit { preferences -> preferences[accessTokenKey] = token } + tokenDataSource.saveAccessToken(token) } override suspend fun saveIdToken(token: String) { - preferenceDataStore.edit { preferences -> preferences[idTokenKey] = token } + tokenDataSource.saveIdToken(token) } - override suspend fun getIdToken(): String = - preferenceDataStore.data.map { preferences -> preferences[idTokenKey] ?: "" }.first() + override suspend fun getIdToken(): String = tokenDataSource.getIdToken() override fun isDuplicatedNickname(nickname: String): Flow = flow { val response = userApi.verifyDuplicatedNickname(nickname = nickname) From 1a84976234945e68b66091a6e1bd0262864eb11e Mon Sep 17 00:00:00 2001 From: algosketch Date: Wed, 22 Nov 2023 23:50:49 +0900 Subject: [PATCH 02/11] =?UTF-8?q?feat=20:=20auth=20interceptor=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/datasource/TokenLocalDataSource.kt | 9 ++++-- .../catchytape/core/data/di/NetworkModule.kt | 32 ++++++++++++++++--- .../core/data/di/qualifier/interceptor.kt | 6 ++++ .../catchytape/core/data/store/TokenStore.kt | 13 ++++++++ 4 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/qualifier/interceptor.kt create mode 100644 android/core/data/src/main/java/com/ohdodok/catchytape/core/data/store/TokenStore.kt diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/datasource/TokenLocalDataSource.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/datasource/TokenLocalDataSource.kt index 6a728a0..346a728 100644 --- a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/datasource/TokenLocalDataSource.kt +++ b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/datasource/TokenLocalDataSource.kt @@ -15,6 +15,12 @@ class TokenLocalDataSource @Inject constructor( private val idTokenKey = stringPreferencesKey("idToken") private val accessTokenKey = stringPreferencesKey("accessToken") + suspend fun getAccessToken(): String = + dataStore.data.map { preferences -> preferences[accessTokenKey] ?: "" }.first() + + suspend fun getIdToken(): String = + dataStore.data.map { preferences -> preferences[idTokenKey] ?: "" }.first() + suspend fun saveAccessToken(token: String) { dataStore.edit { preferences -> preferences[accessTokenKey] = token } } @@ -22,7 +28,4 @@ class TokenLocalDataSource @Inject constructor( suspend fun saveIdToken(token: String) { dataStore.edit { preferences -> preferences[idTokenKey] = token } } - - suspend fun getIdToken(): String = - dataStore.data.map { preferences -> preferences[idTokenKey] ?: "" }.first() } \ No newline at end of file diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/NetworkModule.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/NetworkModule.kt index 3f759e7..2a555c2 100644 --- a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/NetworkModule.kt +++ b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/NetworkModule.kt @@ -2,11 +2,14 @@ package com.ohdodok.catchytape.core.data.di import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import com.ohdodok.catchytape.core.data.BuildConfig +import com.ohdodok.catchytape.core.data.di.qualifier.AuthInterceptor +import com.ohdodok.catchytape.core.data.store.TokenStore import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import kotlinx.serialization.json.Json +import okhttp3.Interceptor import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor @@ -14,20 +17,41 @@ import retrofit2.Retrofit import timber.log.Timber import javax.inject.Singleton - @Module @InstallIn(SingletonComponent::class) object NetworkModule { + @AuthInterceptor + @Singleton + @Provides + fun provideAuthInterceptor(tokenStore: TokenStore): Interceptor { + + return Interceptor { chain -> + val newRequest = chain.request().newBuilder() + .addHeader("Authorization", tokenStore.token) + .build() + + chain.proceed(newRequest) + } + } + @Singleton @Provides - fun provideOkHttpClient(): OkHttpClient { + fun provideLoggingInterceptor(): HttpLoggingInterceptor { val logger = HttpLoggingInterceptor.Logger { message -> Timber.tag("okHttp").d(message) } - val httpInterceptor = HttpLoggingInterceptor(logger) + return HttpLoggingInterceptor(logger) .setLevel(HttpLoggingInterceptor.Level.BODY) + } + @Singleton + @Provides + fun provideOkHttpClient( + loggingInterceptor: HttpLoggingInterceptor, + @AuthInterceptor authInterceptor: Interceptor, + ): OkHttpClient { return OkHttpClient.Builder() - .addInterceptor(httpInterceptor) + .addInterceptor(loggingInterceptor) + .addInterceptor(authInterceptor) .build() } diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/qualifier/interceptor.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/qualifier/interceptor.kt new file mode 100644 index 0000000..52fffef --- /dev/null +++ b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/qualifier/interceptor.kt @@ -0,0 +1,6 @@ +package com.ohdodok.catchytape.core.data.di.qualifier + +import javax.inject.Qualifier + +@Qualifier +annotation class AuthInterceptor diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/store/TokenStore.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/store/TokenStore.kt new file mode 100644 index 0000000..172a271 --- /dev/null +++ b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/store/TokenStore.kt @@ -0,0 +1,13 @@ +package com.ohdodok.catchytape.core.data.store + +import javax.inject.Inject + +class TokenStore @Inject constructor() { + + var token: String = "" + private set + + fun updateToken(newToken: String) { + token = newToken + } +} \ No newline at end of file From 11d3faae3d08d9ce6099982b23756769ef7132f3 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 23 Nov 2023 00:54:23 +0900 Subject: [PATCH 03/11] =?UTF-8?q?feat=20:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=EC=9D=84=20=EC=8B=9C=EB=8F=84=ED=95=98=EB=A9=B4=20=EB=A9=94?= =?UTF-8?q?=EB=AA=A8=EB=A6=AC=EC=97=90=20token=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/repository/AuthRepositoryImpl.kt | 27 ++++++++++--------- .../core/domain/repository/AuthRepository.kt | 4 +-- ...seCase.kt => AutomaticallyLoginUseCase.kt} | 4 +-- .../feature/login/LoginViewModel.kt | 17 +++--------- 4 files changed, 22 insertions(+), 30 deletions(-) rename android/core/domain/src/main/java/com/ohdodok/catchytape/core/domain/usecase/{GetIdTokenUseCase.kt => AutomaticallyLoginUseCase.kt} (59%) diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt index f4de1b0..197e04d 100644 --- a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt +++ b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt @@ -1,27 +1,26 @@ package com.ohdodok.catchytape.core.data.repository -import androidx.datastore.preferences.core.edit import com.ohdodok.catchytape.core.data.api.UserApi import com.ohdodok.catchytape.core.data.datasource.TokenLocalDataSource import com.ohdodok.catchytape.core.data.model.LoginRequest import com.ohdodok.catchytape.core.data.model.SignUpRequest +import com.ohdodok.catchytape.core.data.store.TokenStore import com.ohdodok.catchytape.core.domain.repository.AuthRepository import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.map import javax.inject.Inject class AuthRepositoryImpl @Inject constructor( private val userApi: UserApi, private val tokenDataSource: TokenLocalDataSource, + private val tokenStore: TokenStore, ) : AuthRepository { override fun loginWithGoogle(googleToken: String): Flow = flow { val response = userApi.login(LoginRequest(idToken = googleToken)) if (response.isSuccessful) { response.body()?.let { loginResponse -> - saveIdToken(googleToken) + tokenStore.updateToken(loginResponse.accessToken) emit(loginResponse.accessToken) } } else if (response.code() == 401) { @@ -34,7 +33,7 @@ class AuthRepositoryImpl @Inject constructor( val response = userApi.signUp(SignUpRequest(idToken = googleToken, nickname = nickname)) if (response.isSuccessful) { response.body()?.let { loginResponse -> - saveIdToken(googleToken) + tokenStore.updateToken(loginResponse.accessToken) emit(loginResponse.accessToken) } } else { @@ -43,17 +42,10 @@ class AuthRepositoryImpl @Inject constructor( } } - override suspend fun saveAccessToken(token: String) { tokenDataSource.saveAccessToken(token) } - override suspend fun saveIdToken(token: String) { - tokenDataSource.saveIdToken(token) - } - - override suspend fun getIdToken(): String = tokenDataSource.getIdToken() - override fun isDuplicatedNickname(nickname: String): Flow = flow { val response = userApi.verifyDuplicatedNickname(nickname = nickname) @@ -63,4 +55,15 @@ class AuthRepositoryImpl @Inject constructor( else -> throw RuntimeException("네트워크 에러") // fixme : 예외 처리 로직이 정해지면 수정 } } + + override suspend fun tryLoginAutomatically(): Boolean { + val accessToken = tokenDataSource.getAccessToken() + + return if(accessToken.isNotBlank()) { + tokenStore.updateToken(accessToken) + true + } else { + false + } + } } \ No newline at end of file diff --git a/android/core/domain/src/main/java/com/ohdodok/catchytape/core/domain/repository/AuthRepository.kt b/android/core/domain/src/main/java/com/ohdodok/catchytape/core/domain/repository/AuthRepository.kt index 4189825..24a75d4 100644 --- a/android/core/domain/src/main/java/com/ohdodok/catchytape/core/domain/repository/AuthRepository.kt +++ b/android/core/domain/src/main/java/com/ohdodok/catchytape/core/domain/repository/AuthRepository.kt @@ -10,9 +10,7 @@ interface AuthRepository { suspend fun saveAccessToken(token: String) - suspend fun saveIdToken(token: String) - - suspend fun getIdToken(): String + suspend fun tryLoginAutomatically(): Boolean fun isDuplicatedNickname(nickname: String): Flow diff --git a/android/core/domain/src/main/java/com/ohdodok/catchytape/core/domain/usecase/GetIdTokenUseCase.kt b/android/core/domain/src/main/java/com/ohdodok/catchytape/core/domain/usecase/AutomaticallyLoginUseCase.kt similarity index 59% rename from android/core/domain/src/main/java/com/ohdodok/catchytape/core/domain/usecase/GetIdTokenUseCase.kt rename to android/core/domain/src/main/java/com/ohdodok/catchytape/core/domain/usecase/AutomaticallyLoginUseCase.kt index 83db5a2..27ec79c 100644 --- a/android/core/domain/src/main/java/com/ohdodok/catchytape/core/domain/usecase/GetIdTokenUseCase.kt +++ b/android/core/domain/src/main/java/com/ohdodok/catchytape/core/domain/usecase/AutomaticallyLoginUseCase.kt @@ -3,8 +3,8 @@ package com.ohdodok.catchytape.core.domain.usecase import com.ohdodok.catchytape.core.domain.repository.AuthRepository import javax.inject.Inject -class GetIdTokenUseCase @Inject constructor( +class AutomaticallyLoginUseCase @Inject constructor( private val authRepository: AuthRepository ) { - suspend operator fun invoke() = authRepository.getIdToken() + suspend operator fun invoke(): Boolean = authRepository.tryLoginAutomatically() } \ No newline at end of file diff --git a/android/feature/login/src/main/java/com/ohdodok/catchytape/feature/login/LoginViewModel.kt b/android/feature/login/src/main/java/com/ohdodok/catchytape/feature/login/LoginViewModel.kt index fb311a2..67267d1 100644 --- a/android/feature/login/src/main/java/com/ohdodok/catchytape/feature/login/LoginViewModel.kt +++ b/android/feature/login/src/main/java/com/ohdodok/catchytape/feature/login/LoginViewModel.kt @@ -2,10 +2,9 @@ package com.ohdodok.catchytape.feature.login import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.ohdodok.catchytape.core.domain.usecase.GetIdTokenUseCase +import com.ohdodok.catchytape.core.domain.usecase.AutomaticallyLoginUseCase import com.ohdodok.catchytape.core.domain.usecase.LoginUseCase import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.catch @@ -17,8 +16,7 @@ import javax.inject.Inject @HiltViewModel class LoginViewModel @Inject constructor( private val loginUseCase: LoginUseCase, - private val tokenUseCase: GetIdTokenUseCase - + private val automaticallyLoginUseCase: AutomaticallyLoginUseCase ) : ViewModel() { private val _events = MutableSharedFlow() @@ -27,7 +25,6 @@ class LoginViewModel @Inject constructor( var isAutoLoginFinished: Boolean = false private set - fun login(token: String, isAutoLogin: Boolean = false) { loginUseCase(token) .catch { @@ -39,18 +36,12 @@ class LoginViewModel @Inject constructor( }.launchIn(viewModelScope) } - fun automaticallyLogin() { viewModelScope.launch { - val idToken = tokenUseCase() - if (idToken.isNotEmpty()) { - login(idToken, true) - } - delay(1000) - isAutoLoginFinished = true + val isLoggedIn = automaticallyLoginUseCase() + if (isLoggedIn) _events.emit(LoginEvent.NavigateToHome) } } - } sealed interface LoginEvent { From 70dfa3490a84bbbf51d8f7e1905417b30d161116 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 23 Nov 2023 01:01:01 +0900 Subject: [PATCH 04/11] =?UTF-8?q?feat=20:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=EC=9D=84=20=EC=99=84=EB=A3=8C=ED=95=98=EB=A9=B4=20?= =?UTF-8?q?=ED=99=88=20=ED=99=94=EB=A9=B4=EC=9C=BC=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ohdodok/catchytape/feature/login/LoginFragment.kt | 4 +--- .../com/ohdodok/catchytape/feature/login/NicknameFragment.kt | 5 +++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/android/feature/login/src/main/java/com/ohdodok/catchytape/feature/login/LoginFragment.kt b/android/feature/login/src/main/java/com/ohdodok/catchytape/feature/login/LoginFragment.kt index 72c7f4e..66f72a0 100644 --- a/android/feature/login/src/main/java/com/ohdodok/catchytape/feature/login/LoginFragment.kt +++ b/android/feature/login/src/main/java/com/ohdodok/catchytape/feature/login/LoginFragment.kt @@ -21,8 +21,6 @@ class LoginFragment : BaseFragment(R.layout.fragment_login private val viewModel: LoginViewModel by activityViewModels() - private val loginActivity by lazy { activity as LoginActivity } - private val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestIdToken(BuildConfig.GOOGLE_CLIENT_ID) .requestEmail() @@ -58,7 +56,7 @@ class LoginFragment : BaseFragment(R.layout.fragment_login val intent = Intent() intent.component = ComponentName("com.ohdodok.catchytape", "com.ohdodok.catchytape.MainActivity") startActivity(intent) - loginActivity.finish() + activity?.finish() } is LoginEvent.NavigateToNickName -> { diff --git a/android/feature/login/src/main/java/com/ohdodok/catchytape/feature/login/NicknameFragment.kt b/android/feature/login/src/main/java/com/ohdodok/catchytape/feature/login/NicknameFragment.kt index e2fc9ce..bfc54ab 100644 --- a/android/feature/login/src/main/java/com/ohdodok/catchytape/feature/login/NicknameFragment.kt +++ b/android/feature/login/src/main/java/com/ohdodok/catchytape/feature/login/NicknameFragment.kt @@ -1,5 +1,7 @@ package com.ohdodok.catchytape.feature.login +import android.content.ComponentName +import android.content.Intent import android.os.Bundle import android.view.View import android.widget.TextView @@ -41,6 +43,9 @@ class NicknameFragment : BaseFragment(R.layout.fragment viewModel.events.collect { event -> when (event) { is NicknameEvent.NavigateToHome -> { + val intent = Intent() + intent.component = ComponentName("com.ohdodok.catchytape", "com.ohdodok.catchytape.MainActivity") + startActivity(intent) activity?.finish() } } From f4f4cf4f18a0c1a40cc40bcadbe3cea4d364c909 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 23 Nov 2023 01:31:46 +0900 Subject: [PATCH 05/11] =?UTF-8?q?fix=20:=20=EB=A6=AC=ED=8C=A9=ED=84=B0?= =?UTF-8?q?=EB=A7=81=20=EA=B3=BC=EC=A0=95=EC=97=90=EC=84=9C=20=EC=83=9D?= =?UTF-8?q?=EA=B8=B4=20=EB=AC=B4=ED=95=9C=20=EC=8A=A4=ED=94=8C=EB=9E=98?= =?UTF-8?q?=EC=8B=9C=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/ohdodok/catchytape/feature/login/LoginViewModel.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/android/feature/login/src/main/java/com/ohdodok/catchytape/feature/login/LoginViewModel.kt b/android/feature/login/src/main/java/com/ohdodok/catchytape/feature/login/LoginViewModel.kt index 67267d1..c065a53 100644 --- a/android/feature/login/src/main/java/com/ohdodok/catchytape/feature/login/LoginViewModel.kt +++ b/android/feature/login/src/main/java/com/ohdodok/catchytape/feature/login/LoginViewModel.kt @@ -40,6 +40,7 @@ class LoginViewModel @Inject constructor( viewModelScope.launch { val isLoggedIn = automaticallyLoginUseCase() if (isLoggedIn) _events.emit(LoginEvent.NavigateToHome) + isAutoLoginFinished = true } } } From 876b0c4b200a951305180e1fcdad7a44f19c7763 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 23 Nov 2023 01:46:59 +0900 Subject: [PATCH 06/11] =?UTF-8?q?fix=20:=20token=EC=97=90=20bearer=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/ohdodok/catchytape/core/data/di/NetworkModule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/NetworkModule.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/NetworkModule.kt index 2a555c2..eaeaaa5 100644 --- a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/NetworkModule.kt +++ b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/NetworkModule.kt @@ -28,7 +28,7 @@ object NetworkModule { return Interceptor { chain -> val newRequest = chain.request().newBuilder() - .addHeader("Authorization", tokenStore.token) + .addHeader("Authorization", "Bearer ${tokenStore.token}") .build() chain.proceed(newRequest) From a4da1fabcad4bb7707b7a758f279e774ece0150b Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 23 Nov 2023 01:54:24 +0900 Subject: [PATCH 07/11] =?UTF-8?q?feat=20:=20=EC=9C=A0=ED=9A=A8=20token=20?= =?UTF-8?q?=EA=B2=80=EC=82=AC=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/ohdodok/catchytape/core/data/api/UserApi.kt | 6 ++++++ .../core/data/repository/AuthRepositoryImpl.kt | 9 +++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/api/UserApi.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/api/UserApi.kt index 7038dd0..659df72 100644 --- a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/api/UserApi.kt +++ b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/api/UserApi.kt @@ -7,6 +7,7 @@ import com.ohdodok.catchytape.core.data.model.SignUpRequest import retrofit2.Response import retrofit2.http.Body import retrofit2.http.GET +import retrofit2.http.Header import retrofit2.http.POST import retrofit2.http.Path @@ -26,4 +27,9 @@ interface UserApi { suspend fun verifyDuplicatedNickname( @Path("nickname") nickname: String, ): Response + + @GET("users/verify") + suspend fun verify( + @Header("Authorization") accessToken: String, + ): Response } \ No newline at end of file diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt index 197e04d..11cdd8e 100644 --- a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt +++ b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt @@ -59,11 +59,8 @@ class AuthRepositoryImpl @Inject constructor( override suspend fun tryLoginAutomatically(): Boolean { val accessToken = tokenDataSource.getAccessToken() - return if(accessToken.isNotBlank()) { - tokenStore.updateToken(accessToken) - true - } else { - false - } + if(accessToken.isBlank()) return false + + return userApi.verify("Bearer $accessToken").isSuccessful } } \ No newline at end of file From 1909be741a95c21db43121fa760512fe0ac380b3 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 23 Nov 2023 01:58:50 +0900 Subject: [PATCH 08/11] =?UTF-8?q?refactor=20:=20token=20data=20source=20?= =?UTF-8?q?=EB=8B=A4=EC=8B=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/datasource/TokenLocalDataSource.kt | 31 ------------------- .../data/repository/AuthRepositoryImpl.kt | 18 ++++++++--- 2 files changed, 13 insertions(+), 36 deletions(-) delete mode 100644 android/core/data/src/main/java/com/ohdodok/catchytape/core/data/datasource/TokenLocalDataSource.kt diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/datasource/TokenLocalDataSource.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/datasource/TokenLocalDataSource.kt deleted file mode 100644 index 346a728..0000000 --- a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/datasource/TokenLocalDataSource.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.ohdodok.catchytape.core.data.datasource - -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.core.edit -import androidx.datastore.preferences.core.stringPreferencesKey -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.map -import javax.inject.Inject - -class TokenLocalDataSource @Inject constructor( - private val dataStore: DataStore -) { - - private val idTokenKey = stringPreferencesKey("idToken") - private val accessTokenKey = stringPreferencesKey("accessToken") - - suspend fun getAccessToken(): String = - dataStore.data.map { preferences -> preferences[accessTokenKey] ?: "" }.first() - - suspend fun getIdToken(): String = - dataStore.data.map { preferences -> preferences[idTokenKey] ?: "" }.first() - - suspend fun saveAccessToken(token: String) { - dataStore.edit { preferences -> preferences[accessTokenKey] = token } - } - - suspend fun saveIdToken(token: String) { - dataStore.edit { preferences -> preferences[idTokenKey] = token } - } -} \ No newline at end of file diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt index 11cdd8e..2af6158 100644 --- a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt +++ b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt @@ -1,21 +1,28 @@ package com.ohdodok.catchytape.core.data.repository +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey import com.ohdodok.catchytape.core.data.api.UserApi -import com.ohdodok.catchytape.core.data.datasource.TokenLocalDataSource import com.ohdodok.catchytape.core.data.model.LoginRequest import com.ohdodok.catchytape.core.data.model.SignUpRequest import com.ohdodok.catchytape.core.data.store.TokenStore import com.ohdodok.catchytape.core.domain.repository.AuthRepository import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map import javax.inject.Inject class AuthRepositoryImpl @Inject constructor( private val userApi: UserApi, - private val tokenDataSource: TokenLocalDataSource, private val tokenStore: TokenStore, + private val dataStore: DataStore ) : AuthRepository { + private val accessTokenKey = stringPreferencesKey("accessToken") + override fun loginWithGoogle(googleToken: String): Flow = flow { val response = userApi.login(LoginRequest(idToken = googleToken)) if (response.isSuccessful) { @@ -43,7 +50,7 @@ class AuthRepositoryImpl @Inject constructor( } override suspend fun saveAccessToken(token: String) { - tokenDataSource.saveAccessToken(token) + dataStore.edit { preferences -> preferences[accessTokenKey] = token } } override fun isDuplicatedNickname(nickname: String): Flow = flow { @@ -57,9 +64,10 @@ class AuthRepositoryImpl @Inject constructor( } override suspend fun tryLoginAutomatically(): Boolean { - val accessToken = tokenDataSource.getAccessToken() + val accessToken = + dataStore.data.map { preferences -> preferences[accessTokenKey] ?: "" }.first() - if(accessToken.isBlank()) return false + if (accessToken.isBlank()) return false return userApi.verify("Bearer $accessToken").isSuccessful } From 3b336d83bc470bdc5063788960028bcfa06ec1e5 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 23 Nov 2023 10:58:00 +0900 Subject: [PATCH 09/11] =?UTF-8?q?refactor=20:=20interceptor=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/data/di/qualifier/{interceptor.kt => AuthInterceptor.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/qualifier/{interceptor.kt => AuthInterceptor.kt} (100%) diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/qualifier/interceptor.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/qualifier/AuthInterceptor.kt similarity index 100% rename from android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/qualifier/interceptor.kt rename to android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/qualifier/AuthInterceptor.kt From 8bb7c821739bb909877957ed5fae59410b2fb283 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 23 Nov 2023 11:09:02 +0900 Subject: [PATCH 10/11] =?UTF-8?q?fix=20:=20token=20data=20source=EB=A5=BC?= =?UTF-8?q?=20=EC=9D=B4=EC=9A=A9=ED=95=B4=20=ED=86=A0=ED=81=B0=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EB=B0=8F=20=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/datasource/TokenLocalDataSource.kt | 23 +++++++++++++++++++ .../catchytape/core/data/di/NetworkModule.kt | 8 +++++-- .../data/repository/AuthRepositoryImpl.kt | 20 ++++------------ 3 files changed, 33 insertions(+), 18 deletions(-) create mode 100644 android/core/data/src/main/java/com/ohdodok/catchytape/core/data/datasource/TokenLocalDataSource.kt diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/datasource/TokenLocalDataSource.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/datasource/TokenLocalDataSource.kt new file mode 100644 index 0000000..2bc0e49 --- /dev/null +++ b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/datasource/TokenLocalDataSource.kt @@ -0,0 +1,23 @@ +package com.ohdodok.catchytape.core.data.datasource + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +class TokenLocalDataSource @Inject constructor( + private val dataStore: DataStore +) { + + private val accessTokenKey = stringPreferencesKey("accessToken") + + suspend fun getAccessToken(): String = + dataStore.data.map { preferences -> preferences[accessTokenKey] ?: "" }.first() + + suspend fun saveAccessToken(token: String) { + dataStore.edit { preferences -> preferences[accessTokenKey] = token } + } +} \ No newline at end of file diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/NetworkModule.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/NetworkModule.kt index eaeaaa5..686b9d7 100644 --- a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/NetworkModule.kt +++ b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/NetworkModule.kt @@ -2,12 +2,14 @@ package com.ohdodok.catchytape.core.data.di import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import com.ohdodok.catchytape.core.data.BuildConfig +import com.ohdodok.catchytape.core.data.datasource.TokenLocalDataSource import com.ohdodok.catchytape.core.data.di.qualifier.AuthInterceptor import com.ohdodok.catchytape.core.data.store.TokenStore import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.runBlocking import kotlinx.serialization.json.Json import okhttp3.Interceptor import okhttp3.MediaType.Companion.toMediaType @@ -24,11 +26,13 @@ object NetworkModule { @AuthInterceptor @Singleton @Provides - fun provideAuthInterceptor(tokenStore: TokenStore): Interceptor { + fun provideAuthInterceptor(tokenDataSource: TokenLocalDataSource): Interceptor { + + val accessToken = runBlocking { tokenDataSource.getAccessToken() } return Interceptor { chain -> val newRequest = chain.request().newBuilder() - .addHeader("Authorization", "Bearer ${tokenStore.token}") + .addHeader("Authorization", "Bearer $accessToken") .build() chain.proceed(newRequest) diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt index 2af6158..6885e01 100644 --- a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt +++ b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/repository/AuthRepositoryImpl.kt @@ -1,33 +1,23 @@ package com.ohdodok.catchytape.core.data.repository -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.core.edit -import androidx.datastore.preferences.core.stringPreferencesKey import com.ohdodok.catchytape.core.data.api.UserApi +import com.ohdodok.catchytape.core.data.datasource.TokenLocalDataSource import com.ohdodok.catchytape.core.data.model.LoginRequest import com.ohdodok.catchytape.core.data.model.SignUpRequest -import com.ohdodok.catchytape.core.data.store.TokenStore import com.ohdodok.catchytape.core.domain.repository.AuthRepository import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.map import javax.inject.Inject class AuthRepositoryImpl @Inject constructor( private val userApi: UserApi, - private val tokenStore: TokenStore, - private val dataStore: DataStore + private val tokenDataSource: TokenLocalDataSource, ) : AuthRepository { - private val accessTokenKey = stringPreferencesKey("accessToken") - override fun loginWithGoogle(googleToken: String): Flow = flow { val response = userApi.login(LoginRequest(idToken = googleToken)) if (response.isSuccessful) { response.body()?.let { loginResponse -> - tokenStore.updateToken(loginResponse.accessToken) emit(loginResponse.accessToken) } } else if (response.code() == 401) { @@ -40,7 +30,6 @@ class AuthRepositoryImpl @Inject constructor( val response = userApi.signUp(SignUpRequest(idToken = googleToken, nickname = nickname)) if (response.isSuccessful) { response.body()?.let { loginResponse -> - tokenStore.updateToken(loginResponse.accessToken) emit(loginResponse.accessToken) } } else { @@ -50,7 +39,7 @@ class AuthRepositoryImpl @Inject constructor( } override suspend fun saveAccessToken(token: String) { - dataStore.edit { preferences -> preferences[accessTokenKey] = token } + tokenDataSource.saveAccessToken(token) } override fun isDuplicatedNickname(nickname: String): Flow = flow { @@ -64,8 +53,7 @@ class AuthRepositoryImpl @Inject constructor( } override suspend fun tryLoginAutomatically(): Boolean { - val accessToken = - dataStore.data.map { preferences -> preferences[accessTokenKey] ?: "" }.first() + val accessToken = tokenDataSource.getAccessToken() if (accessToken.isBlank()) return false From 75868500031ccb1ce114aa976380b8275a4f21bc Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 23 Nov 2023 14:10:18 +0900 Subject: [PATCH 11/11] =?UTF-8?q?refactor=20:=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../catchytape/core/data/di/NetworkModule.kt | 1 - .../catchytape/core/data/store/TokenStore.kt | 13 ------------- 2 files changed, 14 deletions(-) delete mode 100644 android/core/data/src/main/java/com/ohdodok/catchytape/core/data/store/TokenStore.kt diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/NetworkModule.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/NetworkModule.kt index 686b9d7..1fe3996 100644 --- a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/NetworkModule.kt +++ b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/di/NetworkModule.kt @@ -4,7 +4,6 @@ import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFact import com.ohdodok.catchytape.core.data.BuildConfig import com.ohdodok.catchytape.core.data.datasource.TokenLocalDataSource import com.ohdodok.catchytape.core.data.di.qualifier.AuthInterceptor -import com.ohdodok.catchytape.core.data.store.TokenStore import dagger.Module import dagger.Provides import dagger.hilt.InstallIn diff --git a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/store/TokenStore.kt b/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/store/TokenStore.kt deleted file mode 100644 index 172a271..0000000 --- a/android/core/data/src/main/java/com/ohdodok/catchytape/core/data/store/TokenStore.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.ohdodok.catchytape.core.data.store - -import javax.inject.Inject - -class TokenStore @Inject constructor() { - - var token: String = "" - private set - - fun updateToken(newToken: String) { - token = newToken - } -} \ No newline at end of file