Skip to content

Commit b3c9b3f

Browse files
authored
Logout on Unauthorized access (#1864)
1 parent 150df36 commit b3c9b3f

File tree

4 files changed

+80
-3
lines changed

4 files changed

+80
-3
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2025 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
7+
*
8+
* See https://github.com/openMF/mobile-wallet/blob/master/LICENSE.md
9+
*/
10+
package org.mifospay.core.common
11+
12+
import kotlinx.coroutines.flow.MutableStateFlow
13+
14+
object GlobalAuthManager {
15+
val isUnauthorized = MutableStateFlow(false)
16+
17+
fun markUnauthorized() {
18+
isUnauthorized.value = true
19+
}
20+
21+
fun reset() {
22+
isUnauthorized.value = false
23+
}
24+
}

core/designsystem/src/commonMain/kotlin/org/mifospay/core/designsystem/component/AlertDialog.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ fun MifosDialogBox(
2222
title: String,
2323
showDialogState: Boolean,
2424
confirmButtonText: String,
25-
dismissButtonText: String,
25+
dismissButtonText: String? = null,
2626
onConfirm: () -> Unit,
2727
onDismiss: () -> Unit,
2828
modifier: Modifier = Modifier,
@@ -48,8 +48,10 @@ fun MifosDialogBox(
4848
}
4949
},
5050
dismissButton = {
51-
TextButton(onClick = onDismiss) {
52-
Text(text = dismissButtonText)
51+
if (!dismissButtonText.isNullOrBlank()) {
52+
TextButton(onClick = onDismiss) {
53+
Text(text = dismissButtonText)
54+
}
5355
}
5456
},
5557
)

core/network/src/commonMain/kotlin/org/mifospay/core/network/utils/KtorInterceptor.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ import io.ktor.client.HttpClient
1313
import io.ktor.client.plugins.HttpClientPlugin
1414
import io.ktor.client.request.HttpRequestPipeline
1515
import io.ktor.client.request.header
16+
import io.ktor.client.statement.HttpResponsePipeline
17+
import io.ktor.http.HttpStatusCode
1618
import io.ktor.util.AttributeKey
19+
import org.mifospay.core.common.GlobalAuthManager
1720
import org.mifospay.core.datastore.UserPreferencesRepository
1821

1922
class KtorInterceptor(
@@ -38,6 +41,13 @@ class KtorInterceptor(
3841
}
3942
}
4043
}
44+
45+
scope.responsePipeline.intercept(HttpResponsePipeline.After) {
46+
if (context.response.status == HttpStatusCode.Unauthorized) {
47+
GlobalAuthManager.markUnauthorized()
48+
}
49+
proceedWith(subject)
50+
}
4151
}
4252

4353
override fun prepare(block: Config.() -> Unit): KtorInterceptor {
@@ -75,6 +85,13 @@ class KtorInterceptorRe(
7585
}
7686
}
7787
}
88+
89+
scope.responsePipeline.intercept(HttpResponsePipeline.After) {
90+
if (context.response.status == HttpStatusCode.Unauthorized) {
91+
GlobalAuthManager.markUnauthorized()
92+
}
93+
proceedWith(subject)
94+
}
7895
}
7996

8097
override fun prepare(block: ConfigRe.() -> Unit): KtorInterceptorRe {

mifospay-shared/src/commonMain/kotlin/org/mifospay/shared/MifosPayApp.kt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,19 @@
1010
package org.mifospay.shared
1111

1212
import androidx.compose.runtime.Composable
13+
import androidx.compose.runtime.LaunchedEffect
1314
import androidx.compose.runtime.getValue
15+
import androidx.compose.runtime.mutableStateOf
16+
import androidx.compose.runtime.remember
1417
import androidx.compose.ui.Modifier
1518
import androidx.lifecycle.compose.collectAsStateWithLifecycle
1619
import androidx.navigation.compose.rememberNavController
1720
import org.koin.compose.koinInject
1821
import org.koin.compose.viewmodel.koinViewModel
22+
import org.mifospay.core.common.GlobalAuthManager
1923
import org.mifospay.core.data.util.NetworkMonitor
2024
import org.mifospay.core.data.util.TimeZoneMonitor
25+
import org.mifospay.core.designsystem.component.MifosDialogBox
2126
import org.mifospay.core.designsystem.theme.MifosTheme
2227
import org.mifospay.shared.MainUiState.Success
2328
import org.mifospay.shared.navigation.MifosNavGraph.LOGIN_GRAPH
@@ -43,6 +48,35 @@ private fun MifosPayApp(
4348
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
4449
val navController = rememberNavController()
4550

51+
var showErrorDialog = remember { mutableStateOf<Boolean>(false) }
52+
val isUnauthorized by GlobalAuthManager.isUnauthorized.collectAsStateWithLifecycle()
53+
54+
LaunchedEffect(isUnauthorized) {
55+
if (isUnauthorized) {
56+
showErrorDialog.value = true
57+
}
58+
}
59+
60+
if (showErrorDialog.value) {
61+
MifosDialogBox(
62+
title = "Unauthorized User",
63+
showDialogState = showErrorDialog.value,
64+
confirmButtonText = "Ok",
65+
onConfirm = {
66+
showErrorDialog.value = false
67+
viewModel.logOut()
68+
navController.navigate(LOGIN_GRAPH) {
69+
popUpTo(navController.graph.id) {
70+
inclusive = true
71+
}
72+
}
73+
GlobalAuthManager.reset()
74+
},
75+
onDismiss = {},
76+
message = "Please login again to continue",
77+
)
78+
}
79+
4680
val navDestination = when (uiState) {
4781
is MainUiState.Loading -> LOGIN_GRAPH
4882
is Success -> if ((uiState as Success).userData.authenticated) {

0 commit comments

Comments
 (0)