//----------------------------------------------------------------------- // // Copyright (c) Outercurve Foundation. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth2 { using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using DotNetOpenAuth.Messaging; /// /// A WinForms control that hosts a mini-browser for hosting by native applications to /// allow the user to authorize the client without leaving the application. /// public partial class ClientAuthorizationView : UserControl { /// /// Initializes a new instance of the class. /// public ClientAuthorizationView() { this.InitializeComponent(); this.Authorization = new AuthorizationState(); } /// /// Occurs when the authorization flow has completed. /// public event EventHandler Completed; /// /// Gets the authorization tracking object. /// public IAuthorizationState Authorization { get; private set; } /// /// Gets or sets the client used to coordinate the authorization flow. /// public UserAgentClient Client { get; set; } /// /// Gets the set of scopes that describe the requested level of access. /// public HashSet Scope { get { return this.Authorization.Scope; } } /// /// Gets or sets the callback URL used to indicate the flow has completed. /// public Uri Callback { get { return this.Authorization.Callback; } set { this.Authorization.Callback = value; } } /// /// Gets a value indicating whether the authorization flow has been completed. /// public bool IsCompleted { get { return this.Authorization == null || this.Authorization.AccessToken != null; } } /// /// Gets a value indicating whether authorization has been granted. /// /// Null if is false public bool? IsGranted { get { if (this.Authorization == null) { return false; } return this.Authorization.AccessToken != null ? (bool?)true : null; } } /// /// Gets a value indicating whether authorization has been rejected. /// /// Null if is false public bool? IsRejected { get { bool? granted = this.IsGranted; return granted.HasValue ? (bool?)(!granted.Value) : null; } } /// /// Gets or sets a value indicating whether the implicit grant type should be used instead of the authorization code grant. /// /// /// true if [request implicit grant]; otherwise, false. /// public bool RequestImplicitGrant { get; set; } /// /// Called when the authorization flow has been completed. /// protected virtual void OnCompleted() { var completed = this.Completed; if (completed != null) { completed(this, new ClientAuthorizationCompleteEventArgs(this.Authorization)); } } /// /// Raises the event. /// /// An that contains the event data. [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Avoid bug in .NET WebBrowser control.")] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "It's a new instance we control.")] protected override async void OnLoad(EventArgs e) { base.OnLoad(e); Uri authorizationUrl = await this.Client.RequestUserAuthorizationAsync(this.Authorization, implicitResponseType: this.RequestImplicitGrant); this.webBrowser1.Navigate(authorizationUrl.AbsoluteUri); // use AbsoluteUri to workaround bug in WebBrowser that calls Uri.ToString instead of Uri.AbsoluteUri leading to escaping errors. } /// /// Tests whether two URLs are equal for purposes of detecting the conclusion of authorization. /// /// The first location. /// The second location. /// The components to compare. /// true if the given components are equal. private static bool SignificantlyEqual(Uri location1, Uri location2, UriComponents components) { string value1 = location1.GetComponents(components, UriFormat.Unescaped); string value2 = location2.GetComponents(components, UriFormat.Unescaped); return string.Equals(value1, value2, StringComparison.Ordinal); } /// /// Handles the Navigating event of the webBrowser1 control. /// /// The source of the event. /// The instance containing the event data. private async void WebBrowser1_Navigating(object sender, WebBrowserNavigatingEventArgs e) { await this.ProcessLocationChangedAsync(e.Url); } /// /// Processes changes in the URL the browser has navigated to. /// /// The location. /// /// A task that completes with the asynchronous operation. /// private async Task ProcessLocationChangedAsync(Uri location) { if (SignificantlyEqual(location, this.Authorization.Callback, UriComponents.SchemeAndServer | UriComponents.Path)) { try { await this.Client.ProcessUserAuthorizationAsync(location, this.Authorization); } catch (ProtocolException ex) { var options = (MessageBoxOptions)0; if (this.RightToLeft == System.Windows.Forms.RightToLeft.Yes) { options |= MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign; } MessageBox.Show(this, ex.ToStringDescriptive(), ex.GetType().Name, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, options); } finally { this.OnCompleted(); } } } /// /// Handles the Navigated event of the webBrowser1 control. /// /// The source of the event. /// The instance containing the event data. private async void WebBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e) { await this.ProcessLocationChangedAsync(e.Url); } /// /// Handles the LocationChanged event of the webBrowser1 control. /// /// The source of the event. /// The instance containing the event data. private async void WebBrowser1_LocationChanged(object sender, EventArgs e) { await this.ProcessLocationChangedAsync(this.webBrowser1.Url); } } }