//-----------------------------------------------------------------------
//
// 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);
}
}
}