We<re building a WPF windows app with ArcGIS Maps SDK .Net. First thing we did was put in place oAuth2 authentification for our partal and our ArcGIS Online. That was working fine for the last 10 months till yeaterday. Then this morning, when we try to login to our arcGIS Online the login window appear and javascript error message pops over it. Then the call to AuthenticationManager.Current.GenerateCredentialAsync(info.ServiceUri); fails...
When we connect to our portal (the ArcGIS server that we host on our server) it works fine.
What changed between yesterday and today?
Please help.
Solved! Go to Solution.
The fix is now live. Thank you for reporting this!
Hi,
I'm not aware of any current issues ArcGIS Online, and having just tried the OAuth workflow with ArcGIS Online in a WPF app, it worked as expected.
Can you share more info about your setup/project?
Thanks
I can reproduce the issue. Looks like the oauth dialog made a change making it incompatible with WPF's default Internet Explorer browser.
I've started an issue internally to see if we can't get that fixed.
You could look into using WebView2 instead of the default browser control which doesn't seem to be affected.
Thanks, hope they will resolve this soon. Meanwhile do you have a sample of the WebView2 use?
Here's an example using WebView2:
public class OAuthAuthorizeHandlerWpf : IOAuthAuthorizeHandler
{
// Embedded Browser: Use a System.Windows.Window to host the sign-in UI provided by the server.
private Window? authWindow;
// Use a TaskCompletionSource to track the completion of the authorization.
private TaskCompletionSource<IDictionary<string, string>>? taskCompletionSource;
// URL for the authorization callback result (the redirect URI configured for your application).
private string? redirectUrl;
// Function to initiate an authorization request.
// It takes the URIs for: the secured service, the authorization endpoint, and the redirect URI.
public Task<IDictionary<string, string>> AuthorizeAsync(Uri serviceUri, Uri authorizeUri, Uri redirectUri)
{
// Don't start an authorization request if one is still in progress.
if (taskCompletionSource != null && !taskCompletionSource.Task.IsCompleted)
{ throw new Exception("Authentication request already in progress."); }
// Instantiate the TaskCompletionSource to track the completion of the authorization.
taskCompletionSource = new TaskCompletionSource<IDictionary<string, string>>();
// Store the authorization and redirect URLs.
redirectUrl = redirectUri.AbsoluteUri;
// Show the sign-in page (schedule to be run on the main UI thread).
Dispatcher? dispatcher = Application.Current.Dispatcher;
if (dispatcher == null || dispatcher.CheckAccess())
{
// Currently on the UI thread, no need to dispatch.
ShowLoginWindow(authorizeUri);
}
else
{
// AuthorizeAsync was called on a separate thread, dispatch to the UI thread.
Action? authorizeOnUIAction = () => ShowLoginWindow(authorizeUri);
dispatcher.BeginInvoke(authorizeOnUIAction);
}
// Return the task associated with the TaskCompletionSource.
return taskCompletionSource.Task;
}
// A function to show a sign-in page hosted at the specified Url.
private void ShowLoginWindow(Uri authorizeUri)
{
// Instantiate a Microsoft Edge (Chromium) WebView2 control.
// See: https://learn.microsoft.com/en-us/microsoft-edge/webview2/
// Note: When releasing an app that uses Microsoft Edge WebView2, you need distribute the WebView2 Runtime, either:
// - By distributing the automatically updated Evergreen Runtime or;
// - By distributing a Fixed Version of the Runtime.
// See-also: https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution
WebView2? webView2 = new();
// Display the web browser in a new window.
authWindow = new Window
{
Icon = null,
Title = authorizeUri.Authority,
Content = webView2,
Width = 400,
Height = 500,
WindowStartupLocation = WindowStartupLocation.CenterOwner
};
// Set the app as the owner of the authentication dialog window.
if (Application.Current != null && Application.Current.MainWindow != null)
{
authWindow.Owner = Application.Current.MainWindow;
}
// Handle the window closed event.
authWindow.Closed += OnWindowClosed;
// Set the Source property of the WebView2 to the Authorize URI (e.g. {https://www.arcgis.com/sharing/oauth2/authorize?response_type=code...).
webView2.Source = authorizeUri;
// Handle the WebView2 NavigationStarting event in which we'll check if the destination URL is our Redirect URI and then process.
webView2.NavigationStarting += WebView2_NavigationStarting;
// Show the dialog to the user.
authWindow.ShowDialog();
}
private void WebView2_NavigationStarting(object? sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationStartingEventArgs e)
{
// Check for nulls/empties.
if (sender == null || sender is not WebView2 || string.IsNullOrEmpty(e.Uri))
return;
if (redirectUrl != null)
{
// Check if browser was redirected to the Redirect URI (indicates successful authentication).
if (new Uri(e.Uri).AbsoluteUri.StartsWith(redirectUrl))
{
// Cancel the page navigation.
e.Cancel = true;
// Call a function to parse key/value pairs from the response URI and set the result for the task completion source with the returned dictionary.
taskCompletionSource?.SetResult(DecodeParameters(new Uri(e.Uri)));
// Close the window.
authWindow?.Close();
}
}
}
// A function to parse key/value pairs from the provided URI.
private static Dictionary<string, string> DecodeParameters(Uri uri)
{
string? str = string.Empty;
if (!string.IsNullOrEmpty(uri.Fragment))
str = uri.Fragment[1..];
else if (!string.IsNullOrEmpty(uri.Query))
str = uri.Query[1..]; ;
// Create new dictionary of key value string pairs to hold the return values.
Dictionary<string, string> keyValueDictionary = [];
/*
* .NET 6 and above includes convenient HttpUtility class.
*/
// Parse the URI query string into a collection.
NameValueCollection nameValueCollection = HttpUtility.ParseQueryString(str);
foreach (string key in nameValueCollection.Keys)
{
keyValueDictionary[key] = nameValueCollection[key] ?? string.Empty;
}
/*
* Use .NET Framework compatible pattern.
*/
// Parse parameters into key / value pairs.
//string[] keysAndValues = str.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries);
//foreach (string kvString in keysAndValues)
//{
// string[] pair = kvString.Split('=');
// string? key = pair[0];
// string? value = string.Empty;
// if (key.Length > 1)
// {
// value = Uri.UnescapeDataString(pair[1]);
// }
// keyValueDictionary.Add(key, value);
//}
return keyValueDictionary;
}
// Handle the browser window closing.
private void OnWindowClosed(object? sender, EventArgs? e)
{
// If the browser window closes, return the focus to the main window.
if (authWindow != null && authWindow.Owner != null)
{
authWindow.Owner.Focus();
}
// If the task wasn't completed, the user must have closed the window without logging in.
if (taskCompletionSource != null && !taskCompletionSource.Task.IsCompleted)
{
// Set the task completion source exception to indicate a canceled operation.
taskCompletionSource.SetCanceled();
}
authWindow = null;
}
}
I found the WebView2 looks much better with the newer API
OAuth dialog sizing (WPF 4.7.2)
ArcGIS Online has made a fix and it will be published soon.
The fix is now live. Thank you for reporting this!
Thanks. It's working now. Saved me from having to install WebView2.