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

fix: Ensure cookies authentication prior to webview loading #312

Merged
merged 1 commit into from
May 6, 2024
Merged
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
15 changes: 15 additions & 0 deletions core/src/main/java/org/openedx/core/extension/ViewExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ import android.graphics.Rect
import android.util.DisplayMetrics
import android.view.View
import android.view.ViewGroup
import android.webkit.WebView
import android.widget.Toast
import androidx.fragment.app.DialogFragment
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.openedx.core.system.AppCookieManager

fun Context.dpToPixel(dp: Int): Float {
return dp * (resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
Expand Down Expand Up @@ -46,3 +50,14 @@ fun DialogFragment.setWidthPercent(percentage: Int) {
fun Context.toastMessage(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}

fun WebView.loadUrl(url: String, scope: CoroutineScope, cookieManager: AppCookieManager) {
if (cookieManager.isSessionCookieMissingOrExpired()) {
scope.launch {
cookieManager.tryToRefreshSessionCookie()
loadUrl(url)
}
} else {
loadUrl(url)
}
}
14 changes: 2 additions & 12 deletions core/src/main/java/org/openedx/core/system/AppCookieManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import java.util.concurrent.TimeUnit
class AppCookieManager(private val config: Config, private val api: CookiesApi) {

companion object {
private const val REV_934_COOKIE =
"REV_934=mobile; expires=Tue, 31 Dec 2021 12:00:20 GMT; domain=.edx.org;"
private val FRESHNESS_INTERVAL = TimeUnit.HOURS.toMillis(1)
}

Expand All @@ -34,19 +32,11 @@ class AppCookieManager(private val config: Config, private val api: CookiesApi)
}

fun clearWebViewCookie() {
CookieManager.getInstance().removeAllCookies { result ->
if (result) {
authSessionCookieExpiration = -1
}
}
CookieManager.getInstance().removeAllCookies(null)
authSessionCookieExpiration = -1
}

fun isSessionCookieMissingOrExpired(): Boolean {
return authSessionCookieExpiration < System.currentTimeMillis()
}

fun setMobileCookie() {
CookieManager.getInstance().setCookie(config.getApiHostURL(), REV_934_COOKIE)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,30 @@ import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import android.webkit.*
import android.webkit.JavascriptInterface
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
Expand All @@ -33,10 +50,15 @@ import androidx.fragment.app.Fragment
import kotlinx.coroutines.launch
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.openedx.core.extension.isEmailValid
import org.openedx.core.extension.loadUrl
import org.openedx.core.system.AppCookieManager
import org.openedx.core.ui.*
import org.openedx.core.ui.ConnectionErrorView
import org.openedx.core.ui.WindowSize
import org.openedx.core.ui.rememberWindowSize
import org.openedx.core.ui.roundBorderWithoutBottom
import org.openedx.core.ui.theme.OpenEdXTheme
import org.openedx.core.ui.theme.appColors
import org.openedx.core.ui.windowSizeValue
import org.openedx.core.utils.EmailUtil

class HtmlUnitFragment : Fragment() {
Expand Down Expand Up @@ -268,13 +290,15 @@ private fun HTMLContentView(
}
isVerticalScrollBarEnabled = false
isHorizontalScrollBarEnabled = false
loadUrl(url)

loadUrl(url, coroutineScope, cookieManager)
}
},
update = { webView ->
if (!isLoading && injectJSList.isNotEmpty()) {
injectJSList.forEach { webView.evaluateJavascript(it, null) }
}
})
}
)
}

Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
Expand All @@ -41,10 +42,14 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.zIndex
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.openedx.core.extension.loadUrl
import org.openedx.core.extension.toastMessage
import org.openedx.core.presentation.dialog.alert.ActionDialogFragment
import org.openedx.core.presentation.dialog.alert.InfoDialogFragment
import org.openedx.core.system.AppCookieManager
import org.openedx.core.ui.ConnectionErrorView
import org.openedx.core.ui.HandleUIMessage
import org.openedx.core.ui.Toolbar
Expand Down Expand Up @@ -119,6 +124,7 @@ class ProgramFragment(private val myPrograms: Boolean = false) : Fragment() {
windowSize = windowSize,
uiState = uiState,
contentUrl = getInitialUrl(),
cookieManager = viewModel.cookieManager,
canShowBackBtn = arguments?.getString(ARG_PATH_ID, "")
?.isNotEmpty() == true,
uriScheme = viewModel.uriScheme,
Expand Down Expand Up @@ -182,15 +188,11 @@ class ProgramFragment(private val myPrograms: Boolean = false) : Fragment() {
onSettingsClick = {
viewModel.navigateToSettings(requireActivity().supportFragmentManager)
},
refreshSessionCookie = {
viewModel.refreshCookie()
},
)
}
}
}


private fun getInitialUrl(): String {
return arguments?.let { args ->
val pathId = args.getString(ARG_PATH_ID) ?: ""
Expand Down Expand Up @@ -219,6 +221,7 @@ private fun ProgramInfoScreen(
windowSize: WindowSize,
uiState: ProgramUIState?,
contentUrl: String,
cookieManager: AppCookieManager,
uriScheme: String,
canShowBackBtn: Boolean,
hasInternetConnection: Boolean,
Expand All @@ -227,10 +230,10 @@ private fun ProgramInfoScreen(
onSettingsClick: () -> Unit,
onBackClick: () -> Unit,
onUriClick: (String, WebViewLink.Authority) -> Unit,
refreshSessionCookie: () -> Unit = {},
) {
val scaffoldState = rememberScaffoldState()
val configuration = LocalConfiguration.current
val coroutineScope = rememberCoroutineScope()
val isLoading = uiState is ProgramUIState.Loading

when (uiState) {
Expand Down Expand Up @@ -290,7 +293,11 @@ private fun ProgramInfoScreen(
uriScheme = uriScheme,
isAllLinksExternal = true,
onWebPageLoaded = onWebPageLoaded,
refreshSessionCookie = refreshSessionCookie,
refreshSessionCookie = {
coroutineScope.launch {
cookieManager.tryToRefreshSessionCookie()
}
},
onUriClick = onUriClick,
)

Expand All @@ -301,7 +308,7 @@ private fun ProgramInfoScreen(
webView
},
update = {
webView.loadUrl(contentUrl)
webView.loadUrl(contentUrl, coroutineScope, cookieManager)
}
)
} else {
Expand Down Expand Up @@ -339,6 +346,7 @@ fun MyProgramsPreview() {
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
uiState = ProgramUIState.Loading,
contentUrl = "https://www.example.com/",
cookieManager = koinViewModel<ProgramViewModel>().cookieManager,
uriScheme = "",
canShowBackBtn = false,
hasInternetConnection = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class ProgramViewModel(

val programConfig get() = config.getProgramConfig().webViewConfig

val cookieManager get() = edxCookieManager

val hasInternetConnection: Boolean get() = networkConnection.isOnline()

private val _uiState = MutableSharedFlow<ProgramUIState>(
Expand Down Expand Up @@ -104,8 +106,4 @@ class ProgramViewModel(
fun navigateToSettings(fragmentManager: FragmentManager) {
router.navigateToSettings(fragmentManager)
}

fun refreshCookie() {
viewModelScope.launch { edxCookieManager.tryToRefreshSessionCookie() }
}
}
Loading