Index: Src/GoogleApis.Auth.WP81/OAuth2/AuthorizationCodeBroker.cs =================================================================== new file mode 100644 --- /dev/null +++ b/Src/GoogleApis.Auth.WP81/OAuth2/AuthorizationCodeBroker.cs @@ -0,0 +1,109 @@ +/* +Copyright 2014 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.Threading; +using System.Threading.Tasks; +using Windows.Security.Authentication.Web; +using Windows.ApplicationModel.Core; +using Windows.UI.Core; + +using Google.Apis.Auth.OAuth2.Requests; +using Google.Apis.Auth.OAuth2.Responses; +using Google.Apis.Util.Store; + +namespace Google.Apis.Auth.OAuth2 +{ + /// + /// OAuth 2.0 verification code receiver for Windows Phone 8.1 application that opens a Google account form so the + /// users can enter their credentials and accept the application access to their token. + /// + /// This broker's most important method is . + /// The first time it is being called, it calls WebAuthenticationBroker.AuthenticateAndContinue + /// so the users can authorize the app to access their private resources. While calling this method the app is + /// suspended. + /// + /// When the app is being activated again, the developer MUST store the auth result (which may contain the access + /// code or an error) in the storage with the SerializableWebAuthResult.Name key. + /// Then when the auth framework calls again the it looks in the storage to find + /// the auth result, and if a code was received the framework will convert it in turn to access and refresh tokens. + /// + public class AuthorizationCodeBroker : ICodeReceiver + { + #region ICodeReceiver Members + + public string RedirectUri + { + get { return GoogleAuthConsts.LocalhostRedirectUri; } + } + + public async Task ReceiveCodeAsync(AuthorizationCodeRequestUrl url, + CancellationToken taskCancellationToken) + { + TaskCompletionSource tcs = + new TaskCompletionSource(); + await ReceiveCodeAsync(url, tcs); + return tcs.Task.Result; + } + + #endregion + + private async Task InvokeFromUIThread(Action action) + { + await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, + () => action()); + } + + /// Asynchronously receives the authorization code. + /// The authorization code request URL. + /// Task completion source whose result will be set to the authorization code. + private async Task ReceiveCodeAsync(AuthorizationCodeRequestUrl url, + TaskCompletionSource tcs) + { + var result = await PasswordVaultDataStore.Default.GetAsync( + SerializableWebAuthResult.Name); + if (result == null) + { + // We should run WebAuthenticationBroker.AuthenticateAndContinue from the UI thread ONLY. + await InvokeFromUIThread(() => WebAuthenticationBroker.AuthenticateAndContinue(url.Build(), + new Uri(GoogleAuthConsts.LocalhostRedirectUri), null, WebAuthenticationOptions.None)); + + // No need to return anything, cause the application is going to be suspended now. + return; + } + + const string Code = "code="; + const string Error = "error="; + // Get the index of the error or the code. + var index = result.ResponseData.IndexOf(Code); + index = index != -1 ? index : result.ResponseData.IndexOf(Error); + + if (index != -1) + { + tcs.SetResult(new AuthorizationCodeResponseUrl(result.ResponseData.Substring(index))); + return; + } + + tcs.SetException(new TokenResponseException( + new TokenErrorResponse + { + Error = result.ResponseStatus.ToString(), + ErrorDescription = "The WebAuthenticationBroker didn't return a code or an error. Details: " + + result.ResponseErrorDetail, + })); + } + } +} \ No newline at end of file