Index: Src/GoogleApis.Auth.DotNet4/GoogleApis.Auth.DotNet4.csproj
===================================================================
--- a/Src/GoogleApis.Auth.DotNet4/GoogleApis.Auth.DotNet4.csproj
+++ b/Src/GoogleApis.Auth.DotNet4/GoogleApis.Auth.DotNet4.csproj
@@ -75,6 +75,7 @@
+
Index: Src/GoogleApis.Auth.DotNet4/OAuth2/GoogleWebAuthenticationBroker.cs
===================================================================
new file mode 100644
--- /dev/null
+++ b/Src/GoogleApis.Auth.DotNet4/OAuth2/GoogleWebAuthenticationBroker.cs
@@ -0,0 +1,104 @@
+/*
+Copyright 2013 Google Inc
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Google.Apis.Util.Store;
+
+namespace Google.Apis.Auth.OAuth2
+{
+ /// A helper utility to manage the authorization code flow.
+ public class GoogleWebAuthenticationBroker
+ {
+ /// The folder which is used by the .
+ public static string Folder = "Google.Apis.Auth";
+
+ /// Asynchronously authenticates the specified user.
+ ///
+ /// In case no data store is specified, will be used by
+ /// default.
+ ///
+ /// The client secrets.
+ ///
+ /// The scopes which indicate the Google API access your application is requesting.
+ ///
+ /// The user to authenticate.
+ /// Cancellation token to cancel an operation.
+ /// The data store, if not specified a file data store will be used.
+ /// User credential.
+ public static async Task AuthenticateAsync(ClientSecrets clientSecrets,
+ IEnumerable scopes, string user, CancellationToken taskCancellationToken,
+ IDataStore dataStore = null)
+ {
+ var initializer = new GoogleAuthorizationCodeFlow.Initializer
+ {
+ ClientSecrets = clientSecrets,
+ };
+ return await AuthenticateAsyncCore(initializer, scopes, user, taskCancellationToken, dataStore);
+ }
+
+ /// Asynchronously authenticates the specified user.
+ ///
+ /// In case no data store is specified, will be used by
+ /// default.
+ ///
+ ///
+ /// The client secrets stream. The authorization code flow constructor is responsible for disposing the stream.
+ ///
+ ///
+ /// The scopes which indicate the Google API access your application is requesting.
+ ///
+ /// The user to authenticate.
+ /// Cancellation token to cancel an operation.
+ /// The data store, if not specified a file data store will be used.
+ /// User credential.
+ public static async Task AuthenticateAsync(Stream clientSecretsStream,
+ IEnumerable scopes, string user, CancellationToken taskCancellationToken,
+ IDataStore dataStore = null)
+ {
+ var initializer = new GoogleAuthorizationCodeFlow.Initializer
+ {
+ ClientSecretsStream = clientSecretsStream,
+ };
+ return await AuthenticateAsyncCore(initializer, scopes, user, taskCancellationToken, dataStore);
+ }
+
+ /// The core logic for asynchronously authenticating the specified user.
+ /// The authorization code initializer.
+ ///
+ /// The scopes which indicate the Google API access your application is requesting.
+ ///
+ /// The user to authenticate.
+ /// Cancellation token to cancel an operation.
+ /// The data store, if not specified a file data store will be used.
+ /// User credential.
+ private static async Task AuthenticateAsyncCore(AuthorizationCodeFlow.Initializer initializer,
+ IEnumerable scopes, string user, CancellationToken taskCancellationToken,
+ IDataStore dataStore = null)
+ {
+ initializer.Scopes = scopes;
+ initializer.DataStore = dataStore ?? new FileDataStore(Folder);
+ var flow = new GoogleAuthorizationCodeFlow(initializer);
+
+ // Create authorization code installed app instance and authorize the user.
+ return await new AuthorizationCodeInstalledApp(flow, new LocalServerCodeReceiver()).Authorize
+ (user, taskCancellationToken);
+ }
+ }
+}
\ No newline at end of file
Index: Src/GoogleApis.Auth.Tests/GoogleApis.Auth.Tests.csproj
===================================================================
new file mode 100644
--- /dev/null
+++ b/Src/GoogleApis.Auth.Tests/GoogleApis.Auth.Tests.csproj
@@ -0,0 +1,124 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {548D6C9B-A97B-4316-91AC-9AAD35202884}
+ Library
+ Properties
+ Google.Apis.Auth
+ Google.Apis.Auth.Tests
+ v4.0
+ 512
+ ..\..\
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\..\packages\Microsoft.Bcl.Async.1.0.16\lib\net40\Microsoft.Threading.Tasks.dll
+
+
+ ..\..\packages\Microsoft.Bcl.Async.1.0.16\lib\net40\Microsoft.Threading.Tasks.Extensions.dll
+
+
+ ..\..\packages\Microsoft.Bcl.Async.1.0.16\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll
+
+
+ ..\..\packages\Moq.4.1.1309.1617\lib\net40\Moq.dll
+
+
+ ..\..\packages\Newtonsoft.Json.5.0.6\lib\net40\Newtonsoft.Json.dll
+
+
+ ..\..\packages\NUnit.2.6.2\lib\nunit.framework.dll
+
+
+
+
+
+ False
+ ..\..\packages\Microsoft.Net.Http.2.1.10\lib\net40\System.Net.Http.dll
+
+
+ ..\..\packages\Microsoft.Net.Http.2.1.10\lib\net40\System.Net.Http.Extensions.dll
+
+
+ ..\..\packages\Microsoft.Net.Http.2.1.10\lib\net40\System.Net.Http.Primitives.dll
+
+
+ False
+ ..\..\packages\Microsoft.Net.Http.2.1.10\lib\net40\System.Net.Http.WebRequest.dll
+
+
+ ..\..\packages\Microsoft.Bcl.1.0.19\lib\net40\System.Runtime.dll
+
+
+ ..\..\packages\Microsoft.Bcl.1.0.19\lib\net40\System.Threading.Tasks.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {0aaaf32e-2bf0-49c5-bc2d-90874cfb5510}
+ GoogleApis.Auth
+
+
+ {9a8aa9ef-6904-43d8-8a26-0ab62069c2dc}
+ GoogleApis.Tests
+
+
+ {826cf988-eee8-4b75-8f53-b7e851a17baa}
+ GoogleApis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: Src/GoogleApis.Auth.Tests/OAuth2/AuthorizationCodeFlowTests.cs
===================================================================
new file mode 100644
--- /dev/null
+++ b/Src/GoogleApis.Auth.Tests/OAuth2/AuthorizationCodeFlowTests.cs
@@ -0,0 +1,425 @@
+/*
+Copyright 2013 Google Inc
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Moq;
+using NUnit.Framework;
+
+using Google.Apis.Auth.OAuth2.Requests;
+using Google.Apis.Auth.OAuth2.Responses;
+using Google.Apis.Http;
+using Google.Apis.Json;
+using Google.Apis.Testing;
+using Google.Apis.Tests;
+using Google.Apis.Util;
+using Google.Apis.Util.Store;
+
+namespace Google.Apis.Auth.OAuth2
+{
+ /// Tests for .
+ [TestFixture]
+ public class AuthorizationCodeFlowTests
+ {
+ private const string TokenUrl = "https://token.com";
+ private const string AuthorizationCodeUrl = "https://authorization.com";
+
+ #region Constructor
+
+ [Test]
+ public void TestConstructor_ArgumentException()
+ {
+ // ClientSecrets are missing.
+ try
+ {
+ new AuthorizationCodeFlow(new AuthorizationCodeFlow.Initializer(
+ "https://authorization_code.com", "https://token.com"));
+ Assert.Fail();
+ }
+ catch (ArgumentException ex)
+ {
+ Assert.True(ex.Message.Contains("You MUST set ClientSecret or ClientSecretStream"));
+ }
+ }
+
+ [Test]
+ public void TestConstructor_DefaultValues()
+ {
+ var flow = CreateFlow();
+ Assert.NotNull(flow.AccessMethod);
+ Assert.That(flow.AccessMethod, Is.InstanceOf());
+ Assert.That(flow.AuthorizationServerUrl, Is.EqualTo("https://authorization.com"));
+ Assert.NotNull(flow.ClientSecrets);
+ Assert.That(flow.ClientSecrets.ClientId, Is.EqualTo("id"));
+ Assert.That(flow.ClientSecrets.ClientSecret, Is.EqualTo("secret"));
+ Assert.That(flow.Clock, Is.InstanceOf());
+ Assert.Null(flow.DataStore);
+ Assert.NotNull(flow.HttpClient);
+ Assert.NotNull(flow.Scopes);
+ Assert.That(flow.TokenServerUrl, Is.EqualTo("https://token.com"));
+
+ Assert.That(flow.HttpClient.MessageHandler.UnsuccessfulResponseHandlers.Count(), Is.EqualTo(1));
+ Assert.That(flow.HttpClient.MessageHandler.UnsuccessfulResponseHandlers.First(),
+ Is.InstanceOf());
+ }
+
+ #endregion
+
+ #region LoadToken
+
+ [Test]
+ public void LoadTokenAsync_NoDataStore()
+ {
+ var flow = CreateFlow();
+ Assert.Null(flow.LoadTokenAsync("user", CancellationToken.None).Result);
+ }
+
+ [Test]
+ public void LoadTokenAsync_NullResponse()
+ {
+ TaskCompletionSource tcs = new TaskCompletionSource();
+ tcs.SetResult(null);
+ Assert.Null(SubtestLoadTokenAsync(tcs));
+ }
+
+ [Test]
+ public void LoadTokenAsync_TokenResponse()
+ {
+ TokenResponse response = new TokenResponse
+ {
+ AccessToken = "access"
+ };
+
+ TaskCompletionSource tcs = new TaskCompletionSource();
+ tcs.SetResult(response);
+ var result = SubtestLoadTokenAsync(tcs);
+ Assert.That(result, Is.EqualTo(response));
+ }
+
+ private TokenResponse SubtestLoadTokenAsync(TaskCompletionSource tcs)
+ {
+ var mock = new Mock();
+ mock.Setup(ds => ds.GetAsync("user")).Returns(tcs.Task);
+ var flow = CreateFlow(dataStore: mock.Object);
+ var result = flow.LoadTokenAsync("user", CancellationToken.None).Result;
+ mock.Verify(ds => ds.GetAsync("user"));
+ return result;
+ }
+
+ #endregion
+
+ #region CreateAuthorizationCodeRequest
+
+ [Test]
+ public void TestCreateAuthorizationCodeRequest()
+ {
+ var request = CreateFlow(scopes: new[] { "a", "b" }).CreateAuthorizationCodeRequest("redirect");
+ Assert.That(request.AuthorizationServerUrl, Is.EqualTo(new Uri(AuthorizationCodeUrl)));
+ Assert.That(request.ClientId, Is.EqualTo("id"));
+ Assert.That(request.RedirectUri, Is.EqualTo("redirect"));
+ Assert.That(request.ResponseType, Is.EqualTo("code"));
+ Assert.That(request.Scope, Is.EqualTo("a b"));
+ Assert.Null(request.State);
+ }
+
+ #endregion
+
+ [Test]
+ public void TestExchangeCodeForTokenAsync()
+ {
+ var mock = new Mock();
+ var handler = new FetchTokenMessageHandler();
+ handler.AuthorizationCodeTokenRequest = new AuthorizationCodeTokenRequest()
+ {
+ Code = "c0de",
+ RedirectUri = "redIrect",
+ Scope = "a"
+ };
+ MockHttpClientFactory mockFactory = new MockHttpClientFactory(handler);
+
+ TaskCompletionSource