Skip to content

Commit

Permalink
[App] Check Invalid Token
Browse files Browse the repository at this point in the history
  • Loading branch information
ajou4095 committed Feb 20, 2024
1 parent 6289af3 commit ac63f9d
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 7 deletions.
3 changes: 2 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ dependencies {
implementation(libs.hilt.android)
ksp(libs.hilt.android.compiler)

implementation(libs.bundles.androidx.data)
implementation(libs.androidx.compose.lifecycle)
implementation(libs.androidx.lifecycle.process)
implementation(libs.bundles.network)

implementation(platform(libs.firebase.bom))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,63 @@
package com.ray.template.android

import android.app.Application
import android.content.Intent
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import com.ray.template.android.domain.repository.AuthenticationRepository
import com.ray.template.android.presentation.ui.invalid.InvalidJwtTokenActivity
import dagger.hilt.android.HiltAndroidApp
import io.sentry.Sentry
import javax.inject.Inject
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.launch
import timber.log.Timber

@HiltAndroidApp
open class TemplateApplication : Application() {

@Inject
lateinit var authenticationRepository: AuthenticationRepository

private val handler = CoroutineExceptionHandler { _, exception ->
Timber.d(exception)
Sentry.captureException(exception)
Firebase.crashlytics.recordException(exception)
}

override fun onCreate() {
super.onCreate()
initializeFirebase()
observeRefreshTokenValidation()
}

// Firebase Initialize
private fun initializeFirebase() {
Firebase.analytics
}

private fun observeRefreshTokenValidation() {
with(ProcessLifecycleOwner.get()) {
lifecycleScope.launch(handler) {
repeatOnLifecycle(Lifecycle.State.STARTED) {
authenticationRepository.isRefreshTokenInvalid.collect { isRefreshTokenInvalid ->
if (isRefreshTokenInvalid) {
val intent = Intent(
this@TemplateApplication,
InvalidJwtTokenActivity::class.java
).apply {
flags =
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
startActivity(intent)
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import com.ray.template.android.domain.model.error.ServerException
import com.ray.template.android.domain.repository.AuthenticationRepository
import javax.inject.Inject
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class MockAuthenticationRepository @Inject constructor(
private val sharedPreferencesManager: SharedPreferencesManager
Expand All @@ -23,18 +26,30 @@ class MockAuthenticationRepository @Inject constructor(
set(value) = sharedPreferencesManager.setBoolean(IS_REGISTERED, value)
get() = sharedPreferencesManager.getBoolean(IS_REGISTERED, false)

private val _isRefreshTokenInvalid: MutableStateFlow<Boolean> = MutableStateFlow(false)
override val isRefreshTokenInvalid: StateFlow<Boolean> = _isRefreshTokenInvalid.asStateFlow()

override suspend fun refreshToken(
refreshToken: String
): Result<JwtToken> {
randomShortDelay()
return Result.success(
JwtToken(
accessToken = "mock_access_token",
refreshToken = "mock_refresh_token"
return if (refreshToken.isEmpty()) {
Result.failure(
ServerException("MOCK_ERROR", "refreshToken 이 만료되었습니다.")
)
} else {
Result.success(
JwtToken(
accessToken = "mock_access_token",
refreshToken = "mock_refresh_token"
)
)
).onSuccess { token ->
}.onSuccess { token ->
this.refreshToken = token.refreshToken
this.accessToken = token.accessToken
_isRefreshTokenInvalid.value = false
}.onFailure { exception ->
_isRefreshTokenInvalid.value = true
}.map { token ->
JwtToken(
accessToken = token.accessToken,
Expand Down Expand Up @@ -86,6 +101,7 @@ class MockAuthenticationRepository @Inject constructor(
}.onSuccess {
this.refreshToken = "mock_access_token"
this.accessToken = "mock_refresh_token"
isRegistered = true
}
}

Expand All @@ -106,6 +122,7 @@ class MockAuthenticationRepository @Inject constructor(
}.onSuccess {
this.refreshToken = ""
this.accessToken = ""
isRegistered = false
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import com.ray.template.android.data.remote.network.api.AuthenticationApi
import com.ray.template.android.domain.model.authentication.JwtToken
import com.ray.template.android.domain.repository.AuthenticationRepository
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class RealAuthenticationRepository @Inject constructor(
private val authenticationApi: AuthenticationApi,
Expand All @@ -18,6 +21,9 @@ class RealAuthenticationRepository @Inject constructor(
set(value) = sharedPreferencesManager.setString(ACCESS_TOKEN, value)
get() = sharedPreferencesManager.getString(ACCESS_TOKEN, "")

private val _isRefreshTokenInvalid: MutableStateFlow<Boolean> = MutableStateFlow(false)
override val isRefreshTokenInvalid: StateFlow<Boolean> = _isRefreshTokenInvalid.asStateFlow()

override suspend fun refreshToken(
refreshToken: String
): Result<JwtToken> {
Expand All @@ -26,6 +32,10 @@ class RealAuthenticationRepository @Inject constructor(
).onSuccess { token ->
this.refreshToken = token.refreshToken
this.accessToken = token.accessToken
_isRefreshTokenInvalid.value = false
}.onFailure { exception ->
// TODO : ID 체크
_isRefreshTokenInvalid.value = true
}.map { token ->
JwtToken(
accessToken = token.accessToken,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package com.ray.template.android.domain.repository

import com.ray.template.android.domain.model.authentication.JwtToken
import kotlinx.coroutines.flow.StateFlow

interface AuthenticationRepository {

var refreshToken: String

var accessToken: String

val isRefreshTokenInvalid: StateFlow<Boolean>

suspend fun refreshToken(
refreshToken: String
): Result<JwtToken>
Expand Down
3 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ kotlinx-datetime = "0.5.0"
hilt = "2.50"
hilt-compose = "1.1.0"
# AndroidX
androidx-lifecycle-process = "2.7.0"
androidx-core = "1.12.0"
androidx-appcompat = "1.6.1"
androidx-room = "2.6.1"
Expand Down Expand Up @@ -60,6 +61,8 @@ kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" }
hilt-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hilt-compose" }
# AndroidX
androidx-lifecycle-process = { group = "androidx.lifecycle", name = "lifecycle-process", version.ref = "androidx-lifecycle-process" }
# AndroidX Presentation
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
Expand Down
4 changes: 4 additions & 0 deletions presentation/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,9 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity
android:name=".ui.invalid.InvalidJwtTokenActivity"
android:exported="false" />
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.ray.template.android.presentation.ui.invalid

import android.content.Intent
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import com.ray.template.android.presentation.R
import com.ray.template.android.presentation.common.theme.TemplateTheme
import com.ray.template.android.presentation.common.view.DialogScreen
import com.ray.template.android.presentation.ui.main.MainActivity
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class InvalidJwtTokenActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
InvalidJwtTokenScreen()
}
}

@Composable
fun InvalidJwtTokenScreen(
viewModel: InvalidJwtTokenViewModel = hiltViewModel()
) {
val context = LocalContext.current
TemplateTheme {
DialogScreen(
title = stringResource(R.string.invalid_jwt_token_dialog_title),
message = stringResource(R.string.invalid_jwt_token_dialog_content),
onConfirm = {
val intent = Intent(context, MainActivity::class.java)
context.startActivity(intent)
finishAfterTransition()
},
onDismissRequest = {}
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.ray.template.android.presentation.ui.invalid

import com.ray.template.android.presentation.common.base.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

@HiltViewModel
class InvalidJwtTokenViewModel @Inject constructor() : BaseViewModel()
2 changes: 2 additions & 0 deletions presentation/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@
<string name="error_dialog_unavailable_server_content">현재 서버와의 접속이 원활하지 않습니다.\n잠시 후 다시 진행해주세요.</string>

<string name="onboarding_confirm">다음</string>
<string name="invalid_jwt_token_dialog_title">회원 오류</string>
<string name="invalid_jwt_token_dialog_content">로그인이 만료되었습니다.\n다시 로그인해주세요.</string>
</resources>

0 comments on commit ac63f9d

Please sign in to comment.