Authenticate with browser

(I recreated this pull request because I used `master` as a head.)

Hey folks, thanks for sharing your code.

We couldn't use Xamarin.Auth as is because we needed to support authentication via browser instead of presenting modal view controller. The reason is, we noticed a lot of people are already logged into Twitter/Facebook in Safari but don't remember / care to enter their passwords when presented with a modal form.

![](http://i.imgur.com/zsD62hW.png)

This pull requests adds `void AuthenticateWithBrowser (IExternalUrlManager)` to `WebAuthenticator` as an alternative to presenting a view controller. Here, `IExternalUrlManager` is a platform-agnostic way to open an external URL in browser and wait for a callback. I'm not sure how it can be implemented in Android, but an iOS implementation is shown in `HandleOpenUrlManager`. A drawback of this approach is that you need to call its `HandleOpenUrl` and `WillEnterForeground` in `AppDelegate` for it to work, but I find this reasonable and I don't see an easy workaround.

We have a corresponding update for Xamarin.Social (not yet ready as a pull request) that uses this method:

		/// <summary>
		/// Attempts to authenticate user with this service using the web browser.
		/// </summary>
		/// <param name="externalUrlManager">A function that registers a callback in HandleOpenUrl chain for given scheme.</param>
		/// <remarks>
		/// To implement authorication via Safari on iOS, pass <see cref="HandleOpenUrlManager.Instance"/>.
		/// Note that it will *not* work unless you call its <c>HandleOpenUrl</c> and <c>WillEnterForeground</c> methods in your <c>AppDelegate</c>'s respective methods.
		/// </remarks>
		public virtual Task<IEnumerable<Account>> GetAccountsAsync (IExternalUrlManager externalUrlManager)
		{
			if (externalUrlManager == null)
				throw new ArgumentNullException ("externalUrlManager", "This overload needs an external URL manager. " +
					"For iOS, you can use HandleOpenUrlManager.Instance, given that you call " +
					"its HandleOpenUrl and OnWentForeground methods from your AppDelegate.");

			var tcs = new TaskCompletionSource<IEnumerable<Account>> ();

			var authenticator = GetAuthenticator () as WebAuthenticator;
			if (authenticator == null)
				throw new NotSupportedException ("This service does not support authentication via web browser.");

			authenticator.Completed += (sender, e) => {
				if (e.IsAuthenticated) {
					AccountStore.Create ().Save (e.Account, this.ServiceId);
					tcs.SetResult (new [] { e.Account });
				} else {
					tcs.SetException (new UnauthorizedAccessException ("Authentication failed."));
				}
			};

			authenticator.AuthenticateWithBrowser (externalUrlManager);
			return tcs.Task;
		}

So if you want to log into Twitter with Safari, you add this to your `AppDelegate`:

        public override bool OpenUrl (UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
        {
            return HandleOpenUrlManager.Instance.HandleOpenUrl (new Uri (url.AbsoluteString));
        }

        public override void WillEnterForeground (UIApplication application)
        {
            HandleOpenUrlManager.Instance.WillEnterForeground ();
        }

and call

    twitterService.GetAccountsAsync (HandleOpenUrlManager.Instance).ContinueWith (...)


Unfortunately I didn't start this in a branch, so there is an unrelated change in this pull request: adding support for JSON auth_token callbacks in `OAuth2Authenticator`. These are required for supporting Google login. I wanted as little dependencies as possible so I used `System.Json`—is this okay? I didn't want to write another parser..

If you want me to split them, I will, but in a few days. The change is small though.

Finally, Miguel asked me to send patches as MIT/X11, but I'm not sure how this should be done. Should we annotate commits? Add license headers to new files? Modify existing headers by adding another license? I'm confused.

Thanks for considering this!