Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

토큰 만료 시, 재 로그인 요청 흐름 #214

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.lateinit.rightweight.data.api

import com.lateinit.rightweight.data.model.remote.DeleteAccountResponse
import com.lateinit.rightweight.data.model.remote.LoginRequestBody
import com.lateinit.rightweight.data.model.remote.LoginResponse
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded
Expand All @@ -21,5 +23,6 @@ interface AuthApiService {
suspend fun deleteAccount(
@Query("key") key: String,
@Field("idToken") idToken: String
)
}
): Response<DeleteAccountResponse>

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.lateinit.rightweight.data.api

import com.lateinit.rightweight.data.model.remote.RefreshTokenResponse
import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded
import retrofit2.http.POST
import retrofit2.http.Query

interface TokenApiService {
@FormUrlEncoded
@POST("token")
suspend fun refreshIdToken(
@Query("key") key: String,
@Field("refresh_token") refreshToken: String,
@Field("grant_type") grantType: String = "refresh_token"
): RefreshTokenResponse
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.lateinit.rightweight.data.datasource.remote

import com.lateinit.rightweight.data.model.remote.LoginResponse
import com.lateinit.rightweight.data.model.remote.RefreshTokenResponse

interface LoginDataSource {

suspend fun login(key: String, token: String): LoginResponse

suspend fun deleteAccount(key: String, idToken: String)

suspend fun refreshIdToken(key: String, refreshToken: String): RefreshTokenResponse
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
package com.lateinit.rightweight.data.datasource.remote.impl

import com.lateinit.rightweight.data.api.AuthApiService
import com.lateinit.rightweight.data.model.remote.LoginResponse
import com.lateinit.rightweight.data.api.TokenApiService
import com.lateinit.rightweight.data.datasource.remote.LoginDataSource
import com.lateinit.rightweight.data.model.remote.LoginRequestBody
import com.lateinit.rightweight.data.model.remote.LoginResponse
import com.lateinit.rightweight.data.model.remote.PostBody
import com.lateinit.rightweight.data.model.remote.RefreshTokenResponse
import com.lateinit.rightweight.ui.MainViewModel
import javax.inject.Inject

class LoginDataSourceImpl @Inject constructor(
val api: AuthApiService
private val authApi: AuthApiService,
private val tokenApi: TokenApiService
) : LoginDataSource {

override suspend fun login(key: String, token: String): LoginResponse {
val postBody = PostBody(token, "google.com").toString()
return api.loginToFirebase(
return authApi.loginToFirebase(
key, LoginRequestBody(
postBody, "http://localhost",
returnIdpCredential = true,
Expand All @@ -23,6 +27,14 @@ class LoginDataSourceImpl @Inject constructor(
}

override suspend fun deleteAccount(key: String, idToken: String) {
api.deleteAccount(key, idToken)
val deleteAccount = authApi.deleteAccount(key, idToken)
if (deleteAccount.isSuccessful.not()) {
throw MainViewModel.IdTokenExpiredException("IdToken expired.")
}
}
}

override suspend fun refreshIdToken(key: String, refreshToken: String): RefreshTokenResponse {
return tokenApi.refreshIdToken(key, refreshToken)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ data class User(
val email: String,
val displayName: String,
val photoUrl: String,
val idToken: String
val idToken: String,
val oauthIdToken: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.lateinit.rightweight.data.model.remote

data class DeleteAccountResponse(val kind: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.lateinit.rightweight.data.model.remote

import com.google.gson.annotations.SerializedName

data class RefreshTokenResponse(
@SerializedName("refresh_token")
val refreshToken: String,
@SerializedName("id_token")
val idToken: String
)
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.lateinit.rightweight.data.repository

import com.lateinit.rightweight.data.model.remote.LoginResponse
import com.lateinit.rightweight.data.model.remote.RefreshTokenResponse

interface LoginRepository {

suspend fun login(key: String, token: String): LoginResponse

suspend fun deleteAccount(key: String, idToken: String)

suspend fun refreshIdToken(key: String, refreshToken: String): RefreshTokenResponse
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.lateinit.rightweight.data.repository.impl

import com.lateinit.rightweight.data.model.remote.LoginResponse
import com.lateinit.rightweight.data.datasource.remote.LoginDataSource
import com.lateinit.rightweight.data.model.remote.LoginResponse
import com.lateinit.rightweight.data.model.remote.RefreshTokenResponse
import com.lateinit.rightweight.data.repository.LoginRepository
import javax.inject.Inject

Expand All @@ -16,4 +17,8 @@ class LoginRepositoryImpl @Inject constructor(
override suspend fun deleteAccount(key: String, idToken: String) {
loginDataSource.deleteAccount(key, idToken)
}

override suspend fun refreshIdToken(key: String, refreshToken: String): RefreshTokenResponse {
return loginDataSource.refreshIdToken(key, refreshToken)
}
}
21 changes: 21 additions & 0 deletions app/src/main/java/com/lateinit/rightweight/di/ApiModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.lateinit.rightweight.di
import com.lateinit.rightweight.BuildConfig
import com.lateinit.rightweight.data.api.AuthApiService
import com.lateinit.rightweight.data.api.RoutineApiService
import com.lateinit.rightweight.data.api.TokenApiService
import com.lateinit.rightweight.data.api.UserApiService
import dagger.Module
import dagger.Provides
Expand Down Expand Up @@ -39,6 +40,12 @@ class ApiModule {
return retrofit.create(UserApiService::class.java)
}

@Provides
@Singleton
fun provideTokenApiService(@Named("Token") retrofit: Retrofit): TokenApiService {
return retrofit.create(TokenApiService::class.java)
}

@Provides
@Singleton
@Named("Auth")
Expand Down Expand Up @@ -67,6 +74,20 @@ class ApiModule {
.build()
}

@Provides
@Singleton
@Named("Token")
fun provideTokenRetrofit(
okHttpClient: OkHttpClient,
gsonConverterFactory: GsonConverterFactory
): Retrofit {
return Retrofit.Builder()
.baseUrl("https://securetoken.googleapis.com/v1/")
.addConverterFactory(gsonConverterFactory)
.client(okHttpClient)
.build()
}

@Provides
@Singleton
fun provideGsonConvertFactory(): GsonConverterFactory {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.lateinit.rightweight.di

import com.lateinit.rightweight.data.api.AuthApiService
import com.lateinit.rightweight.data.api.RoutineApiService
import com.lateinit.rightweight.data.api.TokenApiService
import com.lateinit.rightweight.data.api.UserApiService
import com.lateinit.rightweight.data.dataStore.AppPreferencesDataStore
import com.lateinit.rightweight.data.database.AppDatabase
Expand Down Expand Up @@ -40,9 +41,10 @@ class DataSourceModule {
@Provides
@Singleton
fun provideLoginDataSource(
api: AuthApiService
authApi: AuthApiService,
tokenApi: TokenApiService
): LoginDataSource {
return LoginDataSourceImpl(api)
return LoginDataSourceImpl(authApi, tokenApi)
}

@Provides
Expand Down
24 changes: 17 additions & 7 deletions app/src/main/java/com/lateinit/rightweight/ui/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,23 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
viewModel.deleteAccount(getString(R.string.google_api_key))
collectOnLifecycle {
viewModel.networkState.collect {
if (it == NetworkState.SUCCESS) {
logout()
} else {
Snackbar.make(binding.root, R.string.wrong_connection, Snackbar.LENGTH_LONG)
.apply {
anchorView = binding.bottomNavigation
}.show()
when (it) {
NetworkState.SUCCESS -> {
logout()
}
NetworkState.TOKEN_EXPIRED -> {
viewModel.refreshIdToken(getString(R.string.google_api_key))
Snackbar.make(binding.root, R.string.retry_message, Snackbar.LENGTH_LONG)
.apply {
anchorView = binding.bottomNavigation
}.show()
}
else -> {
Snackbar.make(binding.root, R.string.wrong_connection, Snackbar.LENGTH_LONG)
.apply {
anchorView = binding.bottomNavigation
}.show()
}
}
}
}
Expand Down
20 changes: 18 additions & 2 deletions app/src/main/java/com/lateinit/rightweight/ui/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class MainViewModel @Inject constructor(
is SocketException -> sendNetworkResultEvent(NetworkState.BAD_INTERNET)
is HttpException -> sendNetworkResultEvent(NetworkState.PARSE_ERROR)
is UnknownHostException -> sendNetworkResultEvent(NetworkState.WRONG_CONNECTION)
is IdTokenExpiredException -> sendNetworkResultEvent(NetworkState.TOKEN_EXPIRED)
else -> sendNetworkResultEvent(NetworkState.OTHER_ERROR)
}
}
Expand All @@ -80,9 +81,9 @@ class MainViewModel @Inject constructor(
}

fun deleteAccount(key: String) {
val userInfo = userInfo.value?.idToken ?: return
val idToken = userInfo.value?.idToken ?: return
viewModelScope.launch(networkExceptionHandler) {
loginRepository.deleteAccount(key, userInfo)
loginRepository.deleteAccount(key, idToken)
sendNetworkResultEvent(NetworkState.SUCCESS)
}
}
Expand All @@ -94,6 +95,20 @@ class MainViewModel @Inject constructor(
}
}

fun refreshIdToken(key: String) {
val user = userInfo.value ?: return

viewModelScope.launch {
val loginResponse = loginRepository.login(key, user.oauthIdToken)
userRepository.saveUser(
user.copy(
idToken = loginResponse.idToken,
oauthIdToken = loginResponse.oauthIdToken
)
)
}
}

fun backup() {
viewModelScope.launch(networkExceptionHandler) {
_loadingState.emit(LoadingState.BACKUP)
Expand Down Expand Up @@ -321,4 +336,5 @@ class MainViewModel @Inject constructor(
}
}

class IdTokenExpiredException(message: String) : Exception(message)
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class LoginActivity : AppCompatActivity() {
NetworkState.SUCCESS -> {
moveToHomeActivity(false)
}
NetworkState.TOKEN_EXPIRED -> {}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,22 @@ class LoginViewModel @Inject constructor(
private suspend fun saveUser(loginResponse: LoginResponse) {
with(loginResponse) {
userRepository.saveUser(
User(localId, "", "", "", email, displayName, photoUrl, idToken)
User(
localId,
"",
"",
"",
email,
displayName,
photoUrl,
idToken,
oauthIdToken
)
)
}
}
}

enum class NetworkState {
NO_ERROR, BAD_INTERNET, PARSE_ERROR, WRONG_CONNECTION, OTHER_ERROR, SUCCESS
NO_ERROR, BAD_INTERNET, PARSE_ERROR, WRONG_CONNECTION, OTHER_ERROR, SUCCESS, TOKEN_EXPIRED
}
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,5 @@
<string name="restore_loading_message">데이터를 복원하는 중 입니다.</string>
<string name="backup_loading_message">데이터를 백업하는 중 입니다.</string>
<string name="get_loading_message">데이터를 가져오는 중 입니다</string>
<string name="retry_message">다시 시도해주세요.</string>
</resources>