Forum Discussion

Steve_PP's avatar
Steve_PP
Experienced Partner
7 days ago

C# WebBrowser -> WebView2 solution

Hi

 

Here is a solution for those with C#.NET apps that need to update the internal browser their desktop app invokes when OAuth is required due to breaking changes enforced by MYOB in Aug 2024. In summary, you need to replace WebBrowser with WebView2 (NuGet: Microsoft.Web.WebView2)

 

If you used the MYOB template of many years ago, you can replace OAuthLogin.GetAuthorizationCode with this:

 

        public static async Task<string> GetAuthorizationCode(IApiConfiguration config)
        {
            // Format the URL for the OAuth server login
            string url = string.Format("{0}?client_id={1}&redirect_uri={2}&scope={3}&response_type=code",
                                       CsOAuthServer, config.ClientId, HttpUtility.UrlEncode(config.RedirectUrl), CsOAuthScope);

            // Create a new form with WebView2
            var frm = new Form();
            var webView2 = new WebView2
            {
                Dock = DockStyle.Fill
            };
            frm.Controls.Add(webView2);

            // Set up a TaskCompletionSource to signal when the form can close
            var tcs = new TaskCompletionSource<string>();

            // Subscribe to NavigationCompleted to capture the OAuth redirect
            webView2.CoreWebView2InitializationCompleted += (sender, args) =>
            {
                if (args.IsSuccess)
                {
                    // Navigate to the OAuth login URL
                    webView2.CoreWebView2.Navigate(url);
                }
            };

            // Capture the URL in NavigationStarting
            webView2.NavigationStarting += (sender, args) =>
            {
                // Store the URI when navigation starts
                _currentUri = args.Uri;
            };

            // Use NavigationCompleted to check if the URI contains the authorization code
            webView2.NavigationCompleted += (sender, args) =>
            {
                if (_currentUri.Contains("code="))
                {
                    // Extract the authorization code from the URL
                    string code = ExtractAuthorizationCode(_currentUri);

                    // Signal the TaskCompletionSource that the form can close with the code
                    tcs.SetResult(code);

                    // Close the form
                    frm.Invoke((MethodInvoker)(() => frm.Close()));
                }
            };

            // Initialize WebView2 control asynchronously
            await webView2.EnsureCoreWebView2Async(null);

            frm.Size = new Size(800, 600);
            frm.Show();

            // Wait until the TaskCompletionSource is signaled (form is closed)
            string authCode = await tcs.Task;

            return authCode;

        }

 

Notes:

 

1. Change the call to the updated method to include an await:

_oAuthKeyService.OAuthResponse = oauthService.GetTokens(await   OAuthLogin.GetAuthorizationCode(_configurationCloud));

 

2. Make the containing method async

private async void Login()

 

3. You will probably get a brief flash of 'Failed navigation' as the http://desktop redirect page is shown before the form drops away. Maybe someone can sort that. Ironically, I don't need this code any more based on what I have been forced to learn this weekend.

33 Replies

  • Raff's avatar
    Raff
    Contributing Partner

    Hi Steve

     

    Many thanks for providing the code.  My app is still working but if I ever need to get new tokens or install the app on another machine I'll run into issues and hence very interested in your solution.

     

    I have replaced the old GetAuthorizationCode code with yours, however on my machine it hangs on the  "await webView2.EnsureCoreWebView2Async(null);".  

     

    I'm guessing it is an environmental issue but if you or anyone else has any suggestion sit would be greatly appreciated.

     

    Thanks

     

  • Here's my VB.Net version of Steve's module for accessing Auth Code using WebView2
    -----------------------------------------------------------------------------------------------------

    Module OAuthLogin

     

    Private Const csOAuthServer As String = "https://secure.myob.com/oauth2/account/authorize/"
    Private Const csOAuthLogoff As String = "https://secure.myob.com/oauth2/account/LogOff/"
    Private Const csOAuthScope As String = "CompanyFile"

       

    Private MyCurrentURI As String = ""
    Private MyUrl As String = ""
    Private MyAuthCode As String = ""

    Private WithEvents webVW2 As New Microsoft.Web.WebView2.WinForms.WebView2

       

    Public Function GetAuthorizationCode_WEB2(config As IApiConfiguration) As String
            Try
                MyUrl = String.Format("{0}?client_id={1}&redirect_uri={2}&scope={3}&response_type=code", csOAuthServer, config.ClientId, HttpUtility.UrlEncode(config.RedirectUrl), csOAuthScope)
                MyCurrentURI = ""

                Dim frm As Form = New Form()
                frm.ShowIcon = False

                webVW2 = New Microsoft.Web.WebView2.WinForms.WebView2
                webVW2.Dock = DockStyle.Fill
                frm.Controls.Add(webVW2)

                webVW2.Source = New Uri(MyUrl)

                frm.Size = New Size(800, 600)
                frm.ShowDialog()

                Return MyAuthCode

            Catch ex As Exception
                Return MyAuthCode
            End Try
        End Function


        Private Function ExtractAuthorizationCode(ByVal uri As String) As String
            Try
                Dim uriObj As New Uri(uri)
                Dim queryParams = HttpUtility.ParseQueryString(uriObj.Query)
                Return queryParams("code")
            Catch ex As Exception
                Return ""
            End Try
        End Function

     

     

      Private Sub webVW2_CoreWebView2InitializationCompleted(sender As Object, e As CoreWebView2InitializationCompletedEventArgs) Handles webVW2.CoreWebView2InitializationCompleted
            Try
                If e.IsSuccess Then
                    webVW2.CoreWebView2.Navigate(MyUrl)
                End If
            Catch ex As Exception
            End Try
        End Sub

     

    Private Sub webVW2_NavigationStarting(sender As Object, e As CoreWebView2NavigationStartingEventArgs) Handles webVW2.NavigationStarting
            Try
                MyCurrentURI = e.Uri
            Catch ex As Exception
            End Try
        End Sub

     

    Private Sub webVW2_NavigationCompleted(sender As Object, e As CoreWebView2NavigationCompletedEventArgs) Handles webVW2.NavigationCompleted
            Try
                If MyCurrentURI.Contains("code=") Then
                    MyAuthCode = ExtractAuthorizationCode(MyCurrentURI)
                End If
            Catch ex As Exception
            End Try
        End Sub

     

    End Module

  • eJulia's avatar
    eJulia
    Trusted Partner

    I am still mystified as to why people keep on about IE versions and Edge. I am trying to make a WinForms app work and the relevant thing is what dlls are needed and how they are used, not what browser I, or my users, use. I suspect the issue is that the perpetrators did not understand that fact.

     

    I suspect that is an issue about where that //desktop redirect goes but the real meat is not that but the data that is returned. I have two apps that use the same code. One now works having disabled the error that arises from the redirect, but on my test PC as opposed to my development PC it launches Edge, quite unnecessarily as far as I can see. 

     

    Can anyone explain what that redirect is meant to do? I have never understood that. There is no reason I can see why an actual external browser should come into it. 

     

    The other app fails, despite using the exact same code (same source files!) The only difference, apart from addressing a wider set of end points which should not be relevant, is a different code and secret. 

     

    Off to examine possible differences in their csproj files.

    • The_Doc's avatar
      The_Doc
      Ultimate Partner

      Hi eJulia 

       

      I think this talk about IE versions and Edge browsers may just be a red herring - see my earlier post. I am awaiting until MYOB actually roll out the changes where ( IE is no longer supported). 

       

      However, to explain some of your questions - and I am no expert in OAuth - I have just picked it up as required - the 1st call you need to make to the MYOB secure token server is an http GET call - and ( though I have no app in VB.NET or VC#.NET - I am proficient in both these languages though prefer VB.NET because it is easier)  - but to make this initial call to actually get your Access_code you need to say "Hey MYOB I am a registered developer here is my ident" - and MYOB will say - yep know who you are you logged onto my.myob.com.au a few minutes ago so here is your access_code ( or please authenticate who you are - here is a logon to myob - and you get a logon popup) 

       

      You can't do all this in a VB.NET/VC#.NET WinForm directly as it is a GET HTTP call.

       

      You can either embed into your Form a webview browser - which then does this for you or you can actually use HttpWebRequest class to perform a request and retrieve a response from a given URL ( however, this likely wouldn't handle the MYOB logon if required).

       

      Hence the talk about web browsers and the default in VB.NET, VC#.NET and MSAccess are IE based ones. and hence the talk about webbrowser2, webviewer2, Antview2 which are Edge browser based.

       

      Or you can just take the initial http GET call and manually put the url into a browser - IE, Opera, Edge etc - and in the past the return was a blank page - and you could capture the page source code using a method Object.Document.....   and inbedded in that returned payload was the access_code.

       

      That stopped working - seems MYOB weren't sending this json payload anymore - hence the error when your redirect url was http://desktop - but interestingly there wa a return url from the server - but to capture it you needed to know a different method - it wasn't in the document returned (json formatted page) - it was in a url.

       

      It can be complicated to understand - now the redirect url has to match what you put on the developers tab in your developer licencing - there is a field called redirect_url - and this typically is used to send the http GET call back to where it came from ( hence preventing a malicious intercept)  - but this can be be a formatted webcart page or similar - but if you don't have such a page then the default is http://desktop - which as mentioned had a formatted payload and didn't cause an error.

       

      Probably confused you by now.

       

      Why I couldn't understand why this didn't seem logical about the Edge browser - and Tana in api tech kept saying you need a Webviewer2 browser - for 2 weeks - was that you actually didn't.

       

      Any browser worked and still does - what MYOB had done is stuffed up the payload so now you got an error - which we all thought was "an error" - and the codes were wrong  - until I manually took the returned access_code and pasted it to my refresh code and it worked.

       

      Hope all this explanation helps - it really is a mess up by MYOB and I still advocate at the present - keep your code as is - any browser will work - do 2 things

       

      1. Alter the method used to capture the returned string which contains a legit access_code
      2. Alter the extraction of your access_code, access_token and refresh_token ( as these have all changed in size, look and position in the returned payload

       

      And you will find you will be back online.

       

      The Doc

       

       

      https://stackoverflow.com/questions/92522/http-get-in-vb-net?rq=4

      • The_Doc's avatar
        The_Doc
        Ultimate Partner

        Hi eJulia 

         

        Forgot to mention the link at the bottom talks about using the HTTPWeb class in VB.NET.

         

        The Doc

  • eJulia's avatar
    eJulia
    Trusted Partner

    Interesting stackoverflow post. It might have been about using VB.NET but the code example was C# so I tried it out and got back a page of junk from MYOB which was a dreadful ad for the whole API concept, raving on and listing all the end points as if the recipient was a newby user who might be about to start developing an add-on. Certainly not stuff to throw at add-on users as if they were beginner developers.

     

    It possibly sheds light on the thinking behind all this. Zero has been rated as having better add-ons so someone in marketing, who has no idea what change management is, and thinks everything that comes in via the internet has a browser at the other end, has provoked this awful process round behind the back of those in MYOB who know anything about software and change management etc. It is the kindest theory I have been able to formulate so far!