KevinnZou / compose-webview-multiplatform

WebView for JetBrains Compose Multiplatform
https://kevinnzou.github.io/compose-webview-multiplatform/
Apache License 2.0
305 stars 39 forks source link

Authorisation Code Grant flow #100

Closed JagadishaIncture closed 3 months ago

JagadishaIncture commented 3 months ago

Hi I'm working on supporting oAuth flow in 3 platforms.

here is the code base

`@Composable fun OAuth2WebView(utils: Utils,onAuthorizationCodeReceived: (String) -> Unit) {

val url="${OAuthConfig.AUTH_END_POINT}?response_type=code&client_id=${OAuthConfig.CLIENT_ID}&redirect_uri=${OAuthConfig.REDIRECT_URL}"

val webViewState = rememberWebViewState(url)

LogUtils.logDebug(StringResources.RESPONSE,url)

Column(Modifier.fillMaxSize()) {

    val loadingState = webViewState.loadingState
    if (loadingState is LoadingState.Loading) {
        LinearProgressIndicator(
            progress = loadingState.progress,
            modifier = Modifier.fillMaxWidth()
        )
    }
    WebView(webViewState, onDispose = {
        val currentUrl = webViewState.lastLoadedUrl
        LogUtils.logDebug(StringResources.RESPONSE,currentUrl.toString())
        if (currentUrl?.startsWith(OAuthConfig.REDIRECT_URL) == true) {
            utils.getQueryParameter(currentUrl,"code")?.let {
                LogUtils.logDebug(StringResources.RESPONSE,it)
                onAuthorizationCodeReceived(it)
            }
        }
    })
}

}`

Problem is when auth success we are geting callback with reDirect url and auth code which we are not geting any control.

I tried onDispose which is working when i click back button but ideally not a good solution.. please give me workaround how to get the loading urls at runtime.

KevinnZou commented 3 months ago

Hi @JagadishaIncture, I am not sure if I understood correctly. Do you want to inspect the current loading URL?

JagadishaIncture commented 3 months ago

I Need to know is there any callback to show the override url or loading url?

KevinnZou commented 3 months ago

You can inspect the state.lastLoadedUrl similar to how you inspected loadingState. However, it is recommended to use the snapshotflow as shown in this sample.

JagadishaIncture commented 3 months ago

I'm expecting some thing like

shouldOverrideUrlLoading callback

KevinnZou commented 3 months ago

Got you, we already discussed shouldOverrideUrlLoading in this issue. Please subscribe to it for future updates.

JagadishaIncture commented 3 months ago

When can We expect next release with this feature?

KevinnZou commented 3 months ago

As I mentioned in my reply, we may not be able to support this feature in the short term due to some issues on all three platforms. You are welcome to implement it and submit a PR.

fabiobeoni commented 2 weeks ago

Hi @JagadishaIncture, I am not sure if I understood correctly. Do you want to inspect the current loading URL?

Yes... :)

Let me clarify since I am working on the same usecase: authenticating users with external authentication like Google or Keycloak by Open ID protocol.

In native or Flutter apps you normally do:

  1. display the webview and load the authenticating webpage, passing a "callback url" that will actually bring the user back on the app after the authentication
  2. listen for url changes (or periodically check the webpage current url)
  3. when the authentication webpage redirects the user to the provided callback url, extract the authentication token

Since the WebView does not expose events, I tried by periodically checking the state.lastLoadedUrl but it did not return the current webview url when that url was not loaded (since the passed callback url is actually a string targeting the app)

So, exposing a state.currentUrl property (regardeless the successful loading or not), would allow to read it and complete the authentication flow.

fabiobeoni commented 2 weeks ago

Hi I'm working on supporting oAuth flow in 3 platforms.

here is the code base

`@Composable fun OAuth2WebView(utils: Utils,onAuthorizationCodeReceived: (String) -> Unit) {

val url="${OAuthConfig.AUTH_END_POINT}?response_type=code&client_id=${OAuthConfig.CLIENT_ID}&redirect_uri=${OAuthConfig.REDIRECT_URL}"

val webViewState = rememberWebViewState(url)

LogUtils.logDebug(StringResources.RESPONSE,url)

Column(Modifier.fillMaxSize()) {

    val loadingState = webViewState.loadingState
    if (loadingState is LoadingState.Loading) {
        LinearProgressIndicator(
            progress = loadingState.progress,
            modifier = Modifier.fillMaxWidth()
        )
    }
    WebView(webViewState, onDispose = {
        val currentUrl = webViewState.lastLoadedUrl
        LogUtils.logDebug(StringResources.RESPONSE,currentUrl.toString())
        if (currentUrl?.startsWith(OAuthConfig.REDIRECT_URL) == true) {
            utils.getQueryParameter(currentUrl,"code")?.let {
                LogUtils.logDebug(StringResources.RESPONSE,it)
                onAuthorizationCodeReceived(it)
            }
        }
    })
}

}`

Problem is when auth success we are geting callback with reDirect url and auth code which we are not geting any control.

I tried onDispose which is working when i click back button but ideally not a good solution.. please give me workaround how to get the loading urls at runtime.

Hi, did you find any alternative?

KevinnZou commented 2 weeks ago

@fabiobeoni Thanks for your explanation! I am working on supporting request interception in this PR. Would that resolve the problem?

JagadishaIncture commented 2 weeks ago

Hi I'm working on supporting oAuth flow in 3 platforms. here is the code base `@Composable fun OAuth2WebView(utils: Utils,onAuthorizationCodeReceived: (String) -> Unit) {

val url="${OAuthConfig.AUTH_END_POINT}?response_type=code&client_id=${OAuthConfig.CLIENT_ID}&redirect_uri=${OAuthConfig.REDIRECT_URL}"

val webViewState = rememberWebViewState(url)

LogUtils.logDebug(StringResources.RESPONSE,url)

Column(Modifier.fillMaxSize()) {

    val loadingState = webViewState.loadingState
    if (loadingState is LoadingState.Loading) {
        LinearProgressIndicator(
            progress = loadingState.progress,
            modifier = Modifier.fillMaxWidth()
        )
    }
    WebView(webViewState, onDispose = {
        val currentUrl = webViewState.lastLoadedUrl
        LogUtils.logDebug(StringResources.RESPONSE,currentUrl.toString())
        if (currentUrl?.startsWith(OAuthConfig.REDIRECT_URL) == true) {
            utils.getQueryParameter(currentUrl,"code")?.let {
                LogUtils.logDebug(StringResources.RESPONSE,it)
                onAuthorizationCodeReceived(it)
            }
        }
    })
}

}` Problem is when auth success we are geting callback with reDirect url and auth code which we are not geting any control. I tried onDispose which is working when i click back button but ideally not a good solution.. please give me workaround how to get the loading urls at runtime.

Hi, did you find any alternative?

Yes

JagadishaIncture commented 2 weeks ago

` @Composable fun OAuth2WebView(platformUtils: PlatformUtils, localSharedStorage: LocalSharedStorage, onAuthorizationCodeReceived: (String) -> Unit) {

val url="${OAuthConfig.AUTH_END_POINT}?response_type=code&client_id=${OAuthConfig.CLIENT_ID}&redirect_uri=${OAuthConfig.REDIRECT_URL}"

val webViewState = rememberWebViewState(url)

LogUtils.logDebug(StringResources.RESPONSE,url)

Scaffold {

    Column(Modifier.fillMaxSize()) {

        val loadingState = webViewState.loadingState

        if (loadingState is LoadingState.Loading) {

            LinearProgressIndicator(progress = loadingState.progress, modifier = Modifier.fillMaxWidth())

            webViewState.lastLoadedUrl?.let { currentUrl ->

                LogUtils.logDebug(StringResources.RESPONSE, currentUrl)

                if (currentUrl.startsWith(OAuthConfig.REDIRECT_URL)) {
                    platformUtils.getQueryParameter(currentUrl,"code")?.let {
                        LogUtils.logDebug(StringResources.RESPONSE,it)
                        localSharedStorage.saveAuthCode(it)
                        onAuthorizationCodeReceived(it)
                    }
                }
            }
        }
        WebView(webViewState)
    }
}

} `

fabiobeoni commented 2 weeks ago

@fabiobeoni Thanks for your explanation! I am working on supporting request interception in this PR. Would that resolve the problem?

Yes it will.

fabiobeoni commented 2 weeks ago

` @composable fun OAuth2WebView(platformUtils: PlatformUtils, localSharedStorage: LocalSharedStorage, onAuthorizationCodeReceived: (String) -> Unit) {

val url="${OAuthConfig.AUTH_END_POINT}?response_type=code&client_id=${OAuthConfig.CLIENT_ID}&redirect_uri=${OAuthConfig.REDIRECT_URL}"

val webViewState = rememberWebViewState(url)

LogUtils.logDebug(StringResources.RESPONSE,url)

Scaffold {

    Column(Modifier.fillMaxSize()) {

        val loadingState = webViewState.loadingState

        if (loadingState is LoadingState.Loading) {

            LinearProgressIndicator(progress = loadingState.progress, modifier = Modifier.fillMaxWidth())

            webViewState.lastLoadedUrl?.let { currentUrl ->

                LogUtils.logDebug(StringResources.RESPONSE, currentUrl)

                if (currentUrl.startsWith(OAuthConfig.REDIRECT_URL)) {
                    platformUtils.getQueryParameter(currentUrl,"code")?.let {
                        LogUtils.logDebug(StringResources.RESPONSE,it)
                        localSharedStorage.saveAuthCode(it)
                        onAuthorizationCodeReceived(it)
                    }
                }
            }
        }
        WebView(webViewState)
    }
}

} `

Thanks for sharing