/// <summary> /// Do the actual upload to Picasa /// </summary> /// <param name="surfaceToUpload">Image to upload</param> /// <param name="outputSettings"></param> /// <param name="title"></param> /// <param name="filename"></param> /// <returns>PicasaResponse</returns> public static string UploadToPicasa(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename) { // Fill the OAuth2Settings OAuth2Settings settings = new OAuth2Settings(); settings.AuthUrlPattern = AuthUrl; settings.TokenUrl = TokenUrl; settings.CloudServiceName = "Picasa"; settings.ClientId = PicasaCredentials.ClientId; settings.ClientSecret = PicasaCredentials.ClientSecret; settings.AuthorizeMode = OAuth2AuthorizeMode.LocalServer; // Copy the settings from the config, which is kept in memory and on the disk settings.RefreshToken = Config.RefreshToken; settings.AccessToken = Config.AccessToken; settings.AccessTokenExpires = Config.AccessTokenExpires; try { var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, string.Format(UploadUrl, Config.UploadUser, Config.UploadAlbum), settings); if (Config.AddFilename) { webRequest.Headers.Add("Slug", NetworkHelper.EscapeDataString(filename)); } SurfaceContainer container = new SurfaceContainer(surfaceToUpload, outputSettings, filename); container.Upload(webRequest); string response = NetworkHelper.GetResponseAsString(webRequest); return ParseResponse(response); } finally { // Copy the settings back to the config, so they are stored. Config.RefreshToken = settings.RefreshToken; Config.AccessToken = settings.AccessToken; Config.AccessTokenExpires = settings.AccessTokenExpires; Config.IsDirty = true; IniConfig.Save(); } }
/// <summary> /// Do the actual upload to Imgur /// For more details on the available parameters, see: http://api.imgur.com/resources_anon /// </summary> /// <param name="surfaceToUpload">ISurface to upload</param> /// <param name="outputSettings">OutputSettings for the image file format</param> /// <param name="title">Title</param> /// <param name="filename">Filename</param> /// <returns>ImgurInfo with details</returns> public static ImgurInfo UploadToImgur(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename) { IDictionary<string, object> otherParameters = new Dictionary<string, object>(); // add title if (title != null && Config.AddTitle) { otherParameters.Add("title", title); } // add filename if (filename != null && Config.AddFilename) { otherParameters.Add("name", filename); } string responseString; if (Config.AnonymousAccess) { // add key, we only use the other parameters for the AnonymousAccess //otherParameters.Add("key", IMGUR_ANONYMOUS_API_KEY); HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(Config.ImgurApi3Url + "/upload.xml?" + NetworkHelper.GenerateQueryParameters(otherParameters), HTTPMethod.POST); webRequest.ContentType = "image/" + outputSettings.Format; webRequest.ServicePoint.Expect100Continue = false; SetClientId(webRequest); try { using (var requestStream = webRequest.GetRequestStream()) { ImageOutput.SaveToStream(surfaceToUpload, requestStream, outputSettings); } using (WebResponse response = webRequest.GetResponse()) { using (StreamReader reader = new StreamReader(response.GetResponseStream(), true)) { responseString = reader.ReadToEnd(); } LogRateLimitInfo(response); } } catch (Exception ex) { LOG.Error("Upload to imgur gave an exeption: ", ex); throw; } } else { var oauth2Settings = new OAuth2Settings(); oauth2Settings.AuthUrlPattern = AuthUrlPattern; oauth2Settings.TokenUrl = TokenUrl; oauth2Settings.RedirectUrl = "https://imgur.com"; oauth2Settings.CloudServiceName = "Imgur"; oauth2Settings.ClientId = ImgurCredentials.CONSUMER_KEY; oauth2Settings.ClientSecret = ImgurCredentials.CONSUMER_SECRET; oauth2Settings.AuthorizeMode = OAuth2AuthorizeMode.EmbeddedBrowser; oauth2Settings.BrowserSize = new Size(680, 880); // Copy the settings from the config, which is kept in memory and on the disk oauth2Settings.RefreshToken = Config.RefreshToken; oauth2Settings.AccessToken = Config.AccessToken; oauth2Settings.AccessTokenExpires = Config.AccessTokenExpires; try { var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, Config.ImgurApi3Url + "/upload.xml", oauth2Settings); otherParameters.Add("image", new SurfaceContainer(surfaceToUpload, outputSettings, filename)); NetworkHelper.WriteMultipartFormData(webRequest, otherParameters); responseString = NetworkHelper.GetResponseAsString(webRequest); } finally { // Copy the settings back to the config, so they are stored. Config.RefreshToken = oauth2Settings.RefreshToken; Config.AccessToken = oauth2Settings.AccessToken; Config.AccessTokenExpires = oauth2Settings.AccessTokenExpires; Config.IsDirty = true; IniConfig.Save(); } } return ImgurInfo.ParseResponse(responseString); }
/// <summary> /// Authenticate via a local server by using the LocalServerCodeReceiver /// If this works, return the code /// </summary> /// <param name="settings">OAuth2Settings with the Auth / Token url etc</param> /// <returns>true if completed</returns> private static bool AuthenticateViaLocalServer(OAuth2Settings settings) { var codeReceiver = new LocalServerCodeReceiver(); IDictionary<string, string> result = codeReceiver.ReceiveCode(settings); string code; if (result.TryGetValue(CODE, out code) && !string.IsNullOrEmpty(code)) { settings.Code = code; GenerateRefreshToken(settings); return true; } string error; if (result.TryGetValue("error", out error)) { string errorDescription; if (result.TryGetValue("error_description", out errorDescription)) { throw new Exception(errorDescription); } if ("access_denied" == error) { throw new UnauthorizedAccessException("Access denied"); } else { throw new Exception(error); } } return false; }
/// <summary> /// Authenticate via an embedded browser /// If this works, return the code /// </summary> /// <param name="settings">OAuth2Settings with the Auth / Token url etc</param> /// <returns>true if completed, false if canceled</returns> private static bool AuthenticateViaEmbeddedBrowser(OAuth2Settings settings) { if (string.IsNullOrEmpty(settings.CloudServiceName)) { throw new ArgumentNullException("CloudServiceName"); } if (settings.BrowserSize == Size.Empty) { throw new ArgumentNullException("BrowserSize"); } OAuthLoginForm loginForm = new OAuthLoginForm(string.Format("Authorize {0}", settings.CloudServiceName), settings.BrowserSize, settings.FormattedAuthUrl, settings.RedirectUrl); loginForm.ShowDialog(); if (loginForm.IsOk) { string code; if (loginForm.CallbackParameters.TryGetValue(CODE, out code) && !string.IsNullOrEmpty(code)) { settings.Code = code; GenerateRefreshToken(settings); return true; } } return false; }
/// <summary> /// Generate an OAuth 2 Token by using the supplied code /// </summary> /// <param name="code">Code to get the RefreshToken</param> /// <param name="settings">OAuth2Settings to update with the information that was retrieved</param> public static void GenerateRefreshToken(OAuth2Settings settings) { IDictionary<string, object> data = new Dictionary<string, object>(); // Use the returned code to get a refresh code data.Add(CODE, settings.Code); data.Add(CLIENT_ID, settings.ClientId); data.Add(REDIRECT_URI, settings.RedirectUrl); data.Add(CLIENT_SECRET, settings.ClientSecret); data.Add(GRANT_TYPE, AUTHORIZATION_CODE); foreach (string key in settings.AdditionalAttributes.Keys) { data.Add(key, settings.AdditionalAttributes[key]); } HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(settings.TokenUrl, HTTPMethod.POST); NetworkHelper.UploadFormUrlEncoded(webRequest, data); string accessTokenJsonResult = NetworkHelper.GetResponseAsString(webRequest, true); IDictionary<string, object> refreshTokenResult = JSONHelper.JsonDecode(accessTokenJsonResult); if (refreshTokenResult.ContainsKey("error")) { if (refreshTokenResult.ContainsKey("error_description")) { throw new Exception(string.Format("{0} - {1}", refreshTokenResult["error"], refreshTokenResult["error_description"])); } else { throw new Exception((string)refreshTokenResult["error"]); } } // gives as described here: https://developers.google.com/identity/protocols/OAuth2InstalledApp // "access_token":"1/fFAGRNJru1FTz70BzhT3Zg", // "expires_in":3920, // "token_type":"Bearer", // "refresh_token":"1/xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI" settings.AccessToken = (string)refreshTokenResult[ACCESS_TOKEN] as string; settings.RefreshToken = (string)refreshTokenResult[REFRESH_TOKEN] as string; object seconds = refreshTokenResult[EXPIRES_IN]; if (seconds != null) { settings.AccessTokenExpires = DateTimeOffset.Now.AddSeconds((double)seconds); } settings.Code = null; }
/// <summary> /// Go out and retrieve a new access token via refresh-token with the TokenUrl in the settings /// Will upate the access token, refresh token, expire date /// </summary> /// <param name="settings"></param> public static void GenerateAccessToken(OAuth2Settings settings) { IDictionary<string, object> data = new Dictionary<string, object>(); data.Add(REFRESH_TOKEN, settings.RefreshToken); data.Add(CLIENT_ID, settings.ClientId); data.Add(CLIENT_SECRET, settings.ClientSecret); data.Add(GRANT_TYPE, REFRESH_TOKEN); foreach (string key in settings.AdditionalAttributes.Keys) { data.Add(key, settings.AdditionalAttributes[key]); } HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(settings.TokenUrl, HTTPMethod.POST); NetworkHelper.UploadFormUrlEncoded(webRequest, data); string accessTokenJsonResult = NetworkHelper.GetResponseAsString(webRequest, true); // gives as described here: https://developers.google.com/identity/protocols/OAuth2InstalledApp // "access_token":"1/fFAGRNJru1FTz70BzhT3Zg", // "expires_in":3920, // "token_type":"Bearer", IDictionary<string, object> accessTokenResult = JSONHelper.JsonDecode(accessTokenJsonResult); if (accessTokenResult.ContainsKey("error")) { if ("invalid_grant" == (string)accessTokenResult["error"]) { // Refresh token has also expired, we need a new one! settings.RefreshToken = null; settings.AccessToken = null; settings.AccessTokenExpires = DateTimeOffset.MinValue; settings.Code = null; return; } else { if (accessTokenResult.ContainsKey("error_description")) { throw new Exception(string.Format("{0} - {1}", accessTokenResult["error"], accessTokenResult["error_description"])); } else { throw new Exception((string)accessTokenResult["error"]); } } } settings.AccessToken = (string)accessTokenResult[ACCESS_TOKEN] as string; if (accessTokenResult.ContainsKey(REFRESH_TOKEN)) { // Refresh the refresh token :) settings.RefreshToken = (string)accessTokenResult[REFRESH_TOKEN] as string; } object seconds = accessTokenResult[EXPIRES_IN]; if (seconds != null) { settings.AccessTokenExpires = DateTimeOffset.Now.AddSeconds((double)seconds); } else { settings.AccessTokenExpires = DateTimeOffset.MaxValue; } }
/// <summary> /// CreateWebRequest ready for OAuth 2 access /// </summary> /// <param name="method">HTTPMethod</param> /// <param name="url"></param> /// <param name="settings">OAuth2Settings</param> /// <returns>HttpWebRequest</returns> public static HttpWebRequest CreateOAuth2WebRequest(HTTPMethod method, string url, OAuth2Settings settings) { CheckAndAuthenticateOrRefresh(settings); HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url, method); AddOAuth2Credentials(webRequest, settings); return webRequest; }
/// <summary> /// Check and authenticate or refresh tokens /// </summary> /// <param name="settings">OAuth2Settings</param> public static void CheckAndAuthenticateOrRefresh(OAuth2Settings settings) { // Get Refresh / Access token if (string.IsNullOrEmpty(settings.RefreshToken)) { if (!Authenticate(settings)) { throw new Exception("Authentication cancelled"); } } if (settings.IsAccessTokenExpired) { GenerateAccessToken(settings); // Get Refresh / Access token if (string.IsNullOrEmpty(settings.RefreshToken)) { if (!Authenticate(settings)) { throw new Exception("Authentication cancelled"); } GenerateAccessToken(settings); } } if (settings.IsAccessTokenExpired) { throw new Exception("Authentication failed"); } }
/// <summary> /// Authenticate by using the mode specified in the settings /// </summary> /// <param name="settings">OAuth2Settings</param> /// <returns>false if it was canceled, true if it worked, exception if not</returns> public static bool Authenticate(OAuth2Settings settings) { bool completed = true; switch (settings.AuthorizeMode) { case OAuth2AuthorizeMode.LocalServer: completed = AuthenticateViaLocalServer(settings); break; case OAuth2AuthorizeMode.EmbeddedBrowser: completed = AuthenticateViaEmbeddedBrowser(settings); break; default: throw new NotImplementedException(string.Format("Authorize mode '{0}' is not 'yet' implemented.", settings.AuthorizeMode)); } return completed; }
/// <summary> /// Simple helper to add the Authorization Bearer header /// </summary> /// <param name="webRequest">WebRequest</param> /// <param name="settings">OAuth2Settings</param> public static void AddOAuth2Credentials(HttpWebRequest webRequest, OAuth2Settings settings) { if (!string.IsNullOrEmpty(settings.AccessToken)) { webRequest.Headers.Add("Authorization", "Bearer " + settings.AccessToken); } }
/// <summary> /// The OAuth code receiver /// </summary> /// <param name="authorizationUrl"></param> /// <returns>Dictionary with values</returns> public IDictionary<string, string> ReceiveCode(OAuth2Settings oauth2Settings) { // Set the redirect URL on the settings oauth2Settings.RedirectUrl = RedirectUri; _cloudServiceName = oauth2Settings.CloudServiceName; using (var listener = new HttpListener()) { listener.Prefixes.Add(oauth2Settings.RedirectUrl); try { listener.Start(); // Get the formatted FormattedAuthUrl string authorizationUrl = oauth2Settings.FormattedAuthUrl; LOG.DebugFormat("Open a browser with: {0}", authorizationUrl); Process.Start(authorizationUrl); // Wait to get the authorization code response. var context = listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener); _ready.Reset(); while (!context.AsyncWaitHandle.WaitOne(1000, true)) { LOG.Debug("Waiting for response"); } } catch (Exception) { // Make sure we can clean up, also if the thead is aborted _ready.Set(); throw; } finally { _ready.WaitOne(); listener.Close(); } } return _returnValues; }