I am loading a Google SAML Authentication URL to authenticate the user using WebView. When the user successfully logs in entering his credentials, I get this JSON response in the WebView:
{
"status": 1,
"msg": "user Authenticated !!",
"user": {
"userFirstName": "John",
"userLastName": "Doe",
"userEmail": "john@example.in",
"userType": "manager",
"manager": true,
"employee": false,
"teamLead": false
},
"token": "eyJhbGciOiJI................."
}
I want to extract this information from the WebView and store it locally.
Kotlin Multiplatform
, I am using this library:Medium Post: https://medium.com/@kevinnzou/web-everywhere-introducing-our-multiplatform-compose-webview-library-f9b1264b370
GitHub Repo: https://github.com/KevinnZou/compose-webview-multiplatform
@Composable
internal fun LoginWebView(navHostController: NavHostController? = null) {
val state = rememberWebViewState(url = SAML_DEV)
val navigator = rememberWebViewNavigator()
val jsBridge = rememberWebViewJsBridge()
jsBridge.register(LoginMessageHandler())
state.webSettings.apply {
isJavaScriptEnabled = true
}
DisposableEffect(Unit) {
state.webSettings.apply {
logSeverity = KLogSeverity.Verbose
}
onDispose { }
}
MaterialTheme {
Column {
TopAppBar(
title = { Text(text = "Login") },
navigationIcon = {
IconButton(onClick = {
if (navigator.canGoBack) {
navigator.navigateBack()
} else {
navHostController?.popBackStack()
}
}) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Back",
)
}
},
)
val loadingState = state.loadingState
if (loadingState is LoadingState.Loading) {
LinearProgressIndicator(
color = MaterialTheme.colorScheme.onPrimary,
modifier = Modifier
.height(6.dp)
.fillMaxWidth(),
)
}
WebView(
state = state,
modifier = Modifier.fillMaxSize(),
navigator = navigator,
webViewJsBridge = jsBridge,
)
LaunchedEffect(state, navigator) {
snapshotFlow { state.loadingState }
.filter { it is LoadingState.Finished }
.collect {
navigator.evaluateJavaScript(
"""
try {
// Select the <pre> element in the DOM
const preElement = document.querySelector('pre');
if (preElement) {
// Extract the JSON content from the <pre> element
const jsonResponse = preElement.innerText;
console.log("Extracted JSON response:", jsonResponse);
// Send the response to the native side using the bridge
window.JsBridge.postMessage('login', jsonResponse);
} else {
console.error("<pre> element not found");
}
} catch (e) {
console.error('Error extracting JSON response:', e);
}
""".trimIndent()
)
}
}
}
}
}
class LoginMessageHandler : IJsMessageHandler {
override fun handle(
message: JsMessage,
navigator: WebViewNavigator?,
callback: (String) -> Unit
) {
println("LoginMessageHandler handle: $message")
Logger.i {
"Greet Handler Get Message: $message"
}
//val param = processParams<User>(message)
callback(message.params)
}
override fun methodName(): String {
return "login"
}
}
According to the documentation, for your native code to be called you'll have to: window.kmpJsBridge.callNative
but you have: window.JsBridge.postMessage
which is wrong.
See the part of the documentation