Skip to content

Commit

Permalink
Merge pull request #152 from boostcampwm2023/android/feature/149
Browse files Browse the repository at this point in the history
로그인 상태라면 인증 헤더 넣기
  • Loading branch information
HamBP authored Nov 23, 2023
2 parents fb89128 + 7586850 commit 006e4ea
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -26,4 +27,9 @@ interface UserApi {
suspend fun verifyDuplicatedNickname(
@Path("nickname") nickname: String,
): Response<NicknameResponse>

@GET("users/verify")
suspend fun verify(
@Header("Authorization") accessToken: String,
): Response<Unit>
}
Original file line number Diff line number Diff line change
@@ -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<Preferences>
) {

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 }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,59 @@ 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 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
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import timber.log.Timber
import javax.inject.Singleton


@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

@AuthInterceptor
@Singleton
@Provides
fun provideOkHttpClient(): OkHttpClient {
fun provideAuthInterceptor(tokenDataSource: TokenLocalDataSource): Interceptor {

val accessToken = runBlocking { tokenDataSource.getAccessToken() }

return Interceptor { chain ->
val newRequest = chain.request().newBuilder()
.addHeader("Authorization", "Bearer $accessToken")
.build()

chain.proceed(newRequest)
}
}

@Singleton
@Provides
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()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.ohdodok.catchytape.core.data.di.qualifier

import javax.inject.Qualifier

@Qualifier
annotation class AuthInterceptor
Original file line number Diff line number Diff line change
@@ -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.domain.repository.AuthRepository
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<Preferences>
private val tokenDataSource: TokenLocalDataSource,
) : AuthRepository {

private val idTokenKey = stringPreferencesKey("idToken")
private val accessTokenKey = stringPreferencesKey("accessToken")

override fun loginWithGoogle(googleToken: String): Flow<String> = flow {
val response = userApi.login(LoginRequest(idToken = googleToken))
if (response.isSuccessful) {
response.body()?.let { loginResponse ->
saveIdToken(googleToken)
emit(loginResponse.accessToken)
}
} else if (response.code() == 401) {
Expand All @@ -40,7 +30,6 @@ class AuthRepositoryImpl @Inject constructor(
val response = userApi.signUp(SignUpRequest(idToken = googleToken, nickname = nickname))
if (response.isSuccessful) {
response.body()?.let { loginResponse ->
saveIdToken(googleToken)
emit(loginResponse.accessToken)
}
} else {
Expand All @@ -49,18 +38,10 @@ 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 }
}

override suspend fun getIdToken(): String =
preferenceDataStore.data.map { preferences -> preferences[idTokenKey] ?: "" }.first()

override fun isDuplicatedNickname(nickname: String): Flow<Boolean> = flow {
val response = userApi.verifyDuplicatedNickname(nickname = nickname)

Expand All @@ -70,4 +51,12 @@ class AuthRepositoryImpl @Inject constructor(
else -> throw RuntimeException("네트워크 에러") // fixme : 예외 처리 로직이 정해지면 수정
}
}

override suspend fun tryLoginAutomatically(): Boolean {
val accessToken = tokenDataSource.getAccessToken()

if (accessToken.isBlank()) return false

return userApi.verify("Bearer $accessToken").isSuccessful
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Boolean>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ class LoginFragment : BaseFragment<FragmentLoginBinding>(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()
Expand Down Expand Up @@ -58,7 +56,7 @@ class LoginFragment : BaseFragment<FragmentLoginBinding>(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 -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<LoginEvent>()
Expand All @@ -27,7 +25,6 @@ class LoginViewModel @Inject constructor(
var isAutoLoginFinished: Boolean = false
private set


fun login(token: String, isAutoLogin: Boolean = false) {
loginUseCase(token)
.catch {
Expand All @@ -39,18 +36,13 @@ class LoginViewModel @Inject constructor(
}.launchIn(viewModelScope)
}


fun automaticallyLogin() {
viewModelScope.launch {
val idToken = tokenUseCase()
if (idToken.isNotEmpty()) {
login(idToken, true)
}
delay(1000)
val isLoggedIn = automaticallyLoginUseCase()
if (isLoggedIn) _events.emit(LoginEvent.NavigateToHome)
isAutoLoginFinished = true
}
}

}

sealed interface LoginEvent {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -41,6 +43,9 @@ class NicknameFragment : BaseFragment<FragmentNicknameBinding>(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()
}
}
Expand Down

0 comments on commit 006e4ea

Please sign in to comment.