| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- using System;
- using System.Collections.Generic;
- using System.Net;
- using System.Text;
- using System.Threading.Tasks;
- using Xamarin.Auth;
- using Xamarin.Utilities;
- namespace XamarinFormsGoogleDriveAPI.Droid.Renderer
- {
- public class RefreshOAuth2Authenticator : WebRedirectAuthenticator
- {
- string clientId;
- string clientSecret;
- string scope;
- Uri authorizeUrl;
- Uri accessTokenUrl;
- Uri redirectUrl;
- GetUsernameAsyncFunc getUsernameAsync;
- string requestState;
- bool reportedForgery;
- public string ClientId
- {
- get { return clientId; }
- }
- public string ClientSecret
- {
- get { return clientSecret; }
- }
- public string Scope
- {
- get { return scope; }
- }
- public Uri AuthorizeUrl
- {
- get { return authorizeUrl; }
- }
- public Uri RedirectUrl
- {
- get { return redirectUrl; }
- }
- public Uri AccessTokenUrl
- {
- get { return accessTokenUrl; }
- }
- public RefreshOAuth2Authenticator(string clientId, string scope, Uri authorizeUrl, Uri redirectUrl, GetUsernameAsyncFunc getUsernameAsync = null)
- : this(redirectUrl)
- {
- if (string.IsNullOrEmpty(clientId))
- {
- throw new ArgumentException("clientId must be provided", nameof(clientId));
- }
- this.clientId = clientId;
- this.scope = scope ?? "";
- if (authorizeUrl == null)
- {
- throw new ArgumentNullException(nameof(authorizeUrl));
- }
- this.authorizeUrl = authorizeUrl;
- this.getUsernameAsync = getUsernameAsync;
- this.redirectUrl = redirectUrl;
- accessTokenUrl = null;
- }
- public RefreshOAuth2Authenticator(string clientId, string clientSecret, string scope, Uri authorizeUrl, Uri redirectUrl, Uri accessTokenUrl, GetUsernameAsyncFunc getUsernameAsync = null)
- : this(redirectUrl, clientSecret, accessTokenUrl)
- {
- if (string.IsNullOrEmpty(clientId))
- {
- throw new ArgumentException("clientId must be provided", nameof(clientId));
- }
- this.clientId = clientId;
- if (string.IsNullOrEmpty(clientSecret))
- {
- throw new ArgumentException("clientSecret must be provided", nameof(clientSecret));
- }
- this.clientSecret = clientSecret;
- this.scope = scope ?? "";
- if (authorizeUrl == null)
- {
- throw new ArgumentNullException(nameof(authorizeUrl));
- }
- this.authorizeUrl = authorizeUrl;
- if (accessTokenUrl == null)
- {
- throw new ArgumentNullException(nameof(accessTokenUrl));
- }
- this.accessTokenUrl = accessTokenUrl;
- this.redirectUrl = redirectUrl;
- this.getUsernameAsync = getUsernameAsync;
- }
- RefreshOAuth2Authenticator(Uri redirectUrl, string clientSecret = null, Uri accessTokenUrl = null)
- : base(redirectUrl, redirectUrl)
- {
- this.clientSecret = clientSecret;
- this.accessTokenUrl = accessTokenUrl;
- this.redirectUrl = redirectUrl;
- //
- // Generate a unique state string to check for forgeries
- //
- var chars = new char[16];
- var rand = new Random();
- for (var i = 0; i < chars.Length; i++)
- {
- chars[i] = (char)rand.Next('a', 'z' + 1);
- }
- requestState = new string(chars);
- }
- bool IsImplicit => accessTokenUrl == null;
- public override Task<Uri> GetInitialUrlAsync()
- {
- var url = new Uri(string.Format(
- "{0}?client_id={1}&redirect_uri={2}&response_type={3}&scope={4}&state={5}&access_type=offline&prompt=consent",
- authorizeUrl.AbsoluteUri,
- Uri.EscapeDataString(clientId),
- Uri.EscapeDataString(RedirectUrl.AbsoluteUri),
- IsImplicit ? "token" : "code",
- Uri.EscapeDataString(scope),
- Uri.EscapeDataString(requestState)));
- var tcs = new TaskCompletionSource<Uri>();
- tcs.SetResult(url);
- return tcs.Task;
- }
- public virtual async Task<IDictionary<string, string>> RequestRefreshTokenAsync(string refreshToken)
- {
- var queryValues = new Dictionary<string, string>
- {
- {"refresh_token", refreshToken},
- {"client_id", ClientId},
- {"grant_type", "refresh_token"}
- };
- if (!string.IsNullOrEmpty(ClientSecret))
- {
- queryValues["client_secret"] = ClientSecret;
- }
- try
- {
- var accountProperties = await RequestAccessTokenAsync(queryValues).ConfigureAwait(false);
- OnRetrievedAccountProperties(accountProperties);
- return accountProperties;
- }
- catch (Exception e)
- {
- OnError(e);
- throw;
- // maybe don't need this? this will throw the exception in order to maintain backward compatibility, but maybe could just return -1 or something instead?
- }
- }
- protected override void OnPageEncountered(Uri url, IDictionary<string, string> query, IDictionary<string, string> fragment)
- {
- var all = new Dictionary<string, string>(query);
- foreach (var kv in fragment)
- all[kv.Key] = kv.Value;
- //
- // Check for forgeries
- //
- if (all.ContainsKey("state"))
- {
- if (all["state"] != requestState && !reportedForgery)
- {
- reportedForgery = true;
- OnError("Invalid state from server. Possible forgery!");
- return;
- }
- }
- //
- // Continue processing
- //
- base.OnPageEncountered(url, query, fragment);
- }
- protected override void OnRedirectPageLoaded(Uri url, IDictionary<string, string> query, IDictionary<string, string> fragment)
- {
- //
- // Look for the access_token
- //
- if (fragment.ContainsKey("access_token"))
- {
- //
- // We found an access_token
- //
- OnRetrievedAccountProperties(fragment);
- }
- else if (!IsImplicit)
- {
- //
- // Look for the code
- //
- if (query.ContainsKey("code"))
- {
- var code = query["code"];
- RequestAccessTokenAsync(code).ContinueWith(task =>
- {
- if (task.IsFaulted)
- {
- OnError(task.Exception);
- }
- else
- {
- OnRetrievedAccountProperties(task.Result);
- }
- }, TaskScheduler.FromCurrentSynchronizationContext());
- }
- else
- {
- OnError("Expected code in response, but did not receive one.");
- }
- }
- else
- {
- OnError("Expected access_token in response, but did not receive one.");
- }
- }
- Task<IDictionary<string, string>> RequestAccessTokenAsync(string code)
- {
- var queryValues = new Dictionary<string, string> {
- { "grant_type", "authorization_code" },
- { "code", code },
- { "redirect_uri", RedirectUrl.AbsoluteUri },
- { "client_id", clientId }
- };
- if (!string.IsNullOrEmpty(clientSecret))
- {
- queryValues["client_secret"] = clientSecret;
- }
- return RequestAccessTokenAsync(queryValues);
- }
- protected Task<IDictionary<string, string>> RequestAccessTokenAsync(IDictionary<string, string> queryValues)
- {
- var query = queryValues.FormEncode();
- var req = WebRequest.Create(accessTokenUrl);
- req.Method = "POST";
- var body = Encoding.UTF8.GetBytes(query);
- req.ContentLength = body.Length;
- req.ContentType = "application/x-www-form-urlencoded";
- using (var s = req.GetRequestStream())
- {
- s.Write(body, 0, body.Length);
- }
- return req.GetResponseAsync().ContinueWith(task =>
- {
- var text = task.Result.GetResponseText();
- // Parse the response
- var data = text.Contains("{") ? WebEx.JsonDecode(text) : WebEx.FormDecode(text);
- if (data.ContainsKey("error"))
- {
- throw new AuthException("Error authenticating: " + data["error"]);
- }
- if (data.ContainsKey("access_token"))
- {
- return data;
- }
- throw new AuthException("Expected access_token in access token response, but did not receive one.");
- });
- }
- protected virtual void OnRetrievedAccountProperties(IDictionary<string, string> accountProperties)
- {
- //
- // Now we just need a username for the account
- //
- if (getUsernameAsync != null)
- {
- getUsernameAsync(accountProperties).ContinueWith(task =>
- {
- if (task.IsFaulted)
- {
- OnError(task.Exception);
- }
- else
- {
- OnSucceeded(task.Result, accountProperties);
- }
- }, TaskScheduler.FromCurrentSynchronizationContext());
- }
- else
- {
- OnSucceeded("", accountProperties);
- }
- }
- }
- }
|