diff --git a/SimpleOAuth.Net-Master.sln b/SimpleOAuth.Net-Master.sln
deleted file mode 100644
index df8f441..0000000
--- a/SimpleOAuth.Net-Master.sln
+++ /dev/null
@@ -1,82 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 11.00
-# Visual Studio 2010
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleOAuth", "SimpleOAuth\SimpleOAuth.csproj", "{7195A9BE-2949-4819-9BEB-7B5F366B7CBC}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleOAuth.WP.Mango", "SimpleOAuth.WP.Mango\SimpleOAuth.WP.Mango.csproj", "{A0632EE3-C5C8-4ED9-9CE4-27486B2B8295}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleOAuthTester", "SimpleOAuthTester\SimpleOAuthTester.csproj", "{70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleOAuthTester.WP.Mango", "SimpleOAuthTester.WP.Mango\SimpleOAuthTester.WP.Mango.csproj", "{F22A9EAE-F610-4260-89A8-4E5856AF33C4}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleOAuthTwitter", "SimpleOAuthTwitter\SimpleOAuthTwitter.csproj", "{797DB1AA-32A5-4EEE-B5BD-73667CEE112F}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Debug|Mixed Platforms = Debug|Mixed Platforms
- Debug|x86 = Debug|x86
- Release|Any CPU = Release|Any CPU
- Release|Mixed Platforms = Release|Mixed Platforms
- Release|x86 = Release|x86
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Debug|x86.ActiveCfg = Debug|Any CPU
- {7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Release|Any CPU.Build.0 = Release|Any CPU
- {7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Release|x86.ActiveCfg = Release|Any CPU
- {A0632EE3-C5C8-4ED9-9CE4-27486B2B8295}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A0632EE3-C5C8-4ED9-9CE4-27486B2B8295}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A0632EE3-C5C8-4ED9-9CE4-27486B2B8295}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {A0632EE3-C5C8-4ED9-9CE4-27486B2B8295}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {A0632EE3-C5C8-4ED9-9CE4-27486B2B8295}.Debug|x86.ActiveCfg = Debug|Any CPU
- {A0632EE3-C5C8-4ED9-9CE4-27486B2B8295}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A0632EE3-C5C8-4ED9-9CE4-27486B2B8295}.Release|Any CPU.Build.0 = Release|Any CPU
- {A0632EE3-C5C8-4ED9-9CE4-27486B2B8295}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {A0632EE3-C5C8-4ED9-9CE4-27486B2B8295}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {A0632EE3-C5C8-4ED9-9CE4-27486B2B8295}.Release|x86.ActiveCfg = Release|Any CPU
- {70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Debug|Any CPU.ActiveCfg = Debug|x86
- {70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
- {70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Debug|Mixed Platforms.Build.0 = Debug|x86
- {70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Debug|x86.ActiveCfg = Debug|x86
- {70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Debug|x86.Build.0 = Debug|x86
- {70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Release|Any CPU.ActiveCfg = Release|x86
- {70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Release|Mixed Platforms.ActiveCfg = Release|x86
- {70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Release|Mixed Platforms.Build.0 = Release|x86
- {70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Release|x86.ActiveCfg = Release|x86
- {70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Release|x86.Build.0 = Release|x86
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Debug|Mixed Platforms.Deploy.0 = Debug|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Debug|x86.ActiveCfg = Debug|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Release|Any CPU.Build.0 = Release|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Release|Any CPU.Deploy.0 = Release|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Release|Mixed Platforms.Deploy.0 = Release|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Release|x86.ActiveCfg = Release|Any CPU
- {797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Debug|Any CPU.ActiveCfg = Debug|x86
- {797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
- {797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Debug|Mixed Platforms.Build.0 = Debug|x86
- {797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Debug|x86.ActiveCfg = Debug|x86
- {797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Debug|x86.Build.0 = Debug|x86
- {797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Release|Any CPU.ActiveCfg = Release|x86
- {797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Release|Mixed Platforms.ActiveCfg = Release|x86
- {797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Release|Mixed Platforms.Build.0 = Release|x86
- {797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Release|x86.ActiveCfg = Release|x86
- {797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Release|x86.Build.0 = Release|x86
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
diff --git a/SimpleOAuth.Net.nuspec b/SimpleOAuth.Net.nuspec
deleted file mode 100644
index cb69990..0000000
--- a/SimpleOAuth.Net.nuspec
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
- SimpleOAuth.Net
- 1.0.1
- Daniel McKenzie and Chris Benard
- Daniel McKenzie and Chris Benard
- https://github.com/cbenard/SimpleOAuth.Net/blob/master/LICENSE
- https://github.com/cbenard/SimpleOAuth.Net
- http://chrisbenard.net/downloads/oauthlogo.png
- false
- OAuth libraries come in all shapes and sizes, however in the .Net land, they only come in one - extra large. This library is made to rectify that. It's a small (~15kb) library, with no dependencies that lets you create WebRequest's and sign them to your hearts content.
- Initial NuGet release
- Copyright 2012
- oauth simpleoauth simpleoauth.net
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/SimpleOAuth.Tests/EncryptionMethodTests.cs b/SimpleOAuth.Tests/EncryptionMethodTests.cs
new file mode 100644
index 0000000..2eab7cc
--- /dev/null
+++ b/SimpleOAuth.Tests/EncryptionMethodTests.cs
@@ -0,0 +1,35 @@
+using SimpleOAuth.Internal;
+
+namespace SimpleOAuth.Tests;
+
+public class EncryptionMethodTests
+{
+ [Fact]
+ public void Plain_HasCorrectDescription()
+ {
+ var description = EncryptionMethod.Plain.GetDescription();
+ Assert.Equal("PLAINTEXT", description);
+ }
+
+ [Fact]
+ public void HMACSHA1_HasCorrectDescription()
+ {
+ var description = EncryptionMethod.HMACSHA1.GetDescription();
+ Assert.Equal("HMAC-SHA1", description);
+ }
+
+ [Fact]
+ public void HMACSHA1_HasSignatureType()
+ {
+ var signatureType = EncryptionMethod.HMACSHA1.GetSignatureType();
+ Assert.NotNull(signatureType);
+ Assert.Equal(typeof(Generators.HmacSha1Generator), signatureType);
+ }
+
+ [Fact]
+ public void Plain_HasNoSignatureType()
+ {
+ var signatureType = EncryptionMethod.Plain.GetSignatureType();
+ Assert.Null(signatureType);
+ }
+}
diff --git a/SimpleOAuth.Tests/GeneratorTests.cs b/SimpleOAuth.Tests/GeneratorTests.cs
new file mode 100644
index 0000000..4b7994e
--- /dev/null
+++ b/SimpleOAuth.Tests/GeneratorTests.cs
@@ -0,0 +1,114 @@
+using SimpleOAuth.Generators;
+
+namespace SimpleOAuth.Tests;
+
+public class NonceGeneratorTests
+{
+ [Fact]
+ public void Generate_ReturnsNonEmptyString()
+ {
+ var generator = new NonceGenerator();
+ var nonce = generator.Generate();
+ Assert.False(string.IsNullOrEmpty(nonce));
+ }
+
+ [Fact]
+ public void Generate_ReturnsUniqueValues()
+ {
+ var generator = new NonceGenerator();
+ var nonces = new HashSet();
+ for (int i = 0; i < 100; i++)
+ {
+ nonces.Add(generator.Generate());
+ }
+ Assert.Equal(100, nonces.Count);
+ }
+
+ [Fact]
+ public void Generate_ReturnsStringWithoutDashes()
+ {
+ var generator = new NonceGenerator();
+ var nonce = generator.Generate();
+ Assert.DoesNotContain("-", nonce);
+ }
+
+ [Fact]
+ public void Generate_Returns32CharacterString()
+ {
+ var generator = new NonceGenerator();
+ var nonce = generator.Generate();
+ // GUID without dashes = 32 hex chars
+ Assert.Equal(32, nonce.Length);
+ }
+}
+
+public class TimestampGeneratorTests
+{
+ [Fact]
+ public void Generate_ReturnsNonEmptyString()
+ {
+ var generator = new TimestampGenerator();
+ var timestamp = generator.Generate();
+ Assert.False(string.IsNullOrEmpty(timestamp));
+ }
+
+ [Fact]
+ public void Generate_ReturnsNumericString()
+ {
+ var generator = new TimestampGenerator();
+ var timestamp = generator.Generate();
+ Assert.True(long.TryParse(timestamp, out _));
+ }
+
+ [Fact]
+ public void Generate_ReturnsReasonableTimestamp()
+ {
+ var generator = new TimestampGenerator();
+ var timestamp = long.Parse(generator.Generate());
+
+ // Should be after 2020-01-01 (1577836800) and before 2100-01-01
+ Assert.True(timestamp > 1577836800);
+ Assert.True(timestamp < 4102444800);
+ }
+}
+
+public class HmacSha1GeneratorTests
+{
+ [Fact]
+ public void Generate_ProducesConsistentOutput()
+ {
+ var generator = new HmacSha1Generator();
+ var result1 = generator.Generate("base string", "key");
+ var result2 = generator.Generate("base string", "key");
+ Assert.Equal(result1, result2);
+ }
+
+ [Fact]
+ public void Generate_DifferentInputsProduceDifferentOutput()
+ {
+ var generator = new HmacSha1Generator();
+ var result1 = generator.Generate("base string 1", "key");
+ var result2 = generator.Generate("base string 2", "key");
+ Assert.NotEqual(result1, result2);
+ }
+
+ [Fact]
+ public void Generate_DifferentKeysProduceDifferentOutput()
+ {
+ var generator = new HmacSha1Generator();
+ var result1 = generator.Generate("base string", "key1");
+ var result2 = generator.Generate("base string", "key2");
+ Assert.NotEqual(result1, result2);
+ }
+
+ [Fact]
+ public void Generate_ReturnsBase64String()
+ {
+ var generator = new HmacSha1Generator();
+ var result = generator.Generate("test", "key");
+ // Should be valid base64
+ var bytes = Convert.FromBase64String(result);
+ // HMAC-SHA1 produces 20 bytes
+ Assert.Equal(20, bytes.Length);
+ }
+}
diff --git a/SimpleOAuth.Tests/OAuthRequestWrapperTests.cs b/SimpleOAuth.Tests/OAuthRequestWrapperTests.cs
new file mode 100644
index 0000000..41cc244
--- /dev/null
+++ b/SimpleOAuth.Tests/OAuthRequestWrapperTests.cs
@@ -0,0 +1,281 @@
+using System.Net;
+using System.Net.Http;
+
+namespace SimpleOAuth.Tests;
+
+public class OAuthRequestWrapperTests
+{
+ private readonly Tokens _testTokens = new Tokens
+ {
+ ConsumerKey = "dpf43f3p2l4k3l03",
+ ConsumerSecret = "kd94hf93k423kf44",
+ AccessToken = "nnch734d00sl2jdk",
+ AccessTokenSecret = "pfkkdhi9sl3r4s00"
+ };
+
+ #region WebRequest Tests
+
+ [Fact]
+ public void SignRequest_WebRequest_SetsAuthorizationHeader()
+ {
+ var request = WebRequest.Create("https://example.com/resource");
+ request.Method = "GET";
+
+ request.SignRequest(_testTokens)
+ .WithEncryption(EncryptionMethod.HMACSHA1)
+ .InHeader();
+
+ var authHeader = request.Headers["Authorization"];
+ Assert.NotNull(authHeader);
+ Assert.StartsWith("OAuth ", authHeader);
+ }
+
+ [Fact]
+ public void SignRequest_WebRequest_ContainsRequiredOAuthParameters()
+ {
+ var request = WebRequest.Create("https://example.com/resource");
+ request.Method = "GET";
+
+ request.SignRequest(_testTokens)
+ .WithEncryption(EncryptionMethod.HMACSHA1)
+ .InHeader();
+
+ var authHeader = request.Headers["Authorization"];
+ Assert.Contains("oauth_consumer_key", authHeader);
+ Assert.Contains("oauth_nonce", authHeader);
+ Assert.Contains("oauth_signature_method", authHeader);
+ Assert.Contains("oauth_timestamp", authHeader);
+ Assert.Contains("oauth_token", authHeader);
+ Assert.Contains("oauth_version", authHeader);
+ Assert.Contains("oauth_signature", authHeader);
+ }
+
+ [Fact]
+ public void SignRequest_WebRequest_WithCallback_IncludesCallback()
+ {
+ var request = WebRequest.Create("https://example.com/request_token");
+ request.Method = "POST";
+
+ request.SignRequest()
+ .WithTokens(_testTokens)
+ .WithCallback("oob")
+ .InHeader();
+
+ var authHeader = request.Headers["Authorization"];
+ Assert.Contains("oauth_callback", authHeader);
+ }
+
+ [Fact]
+ public void SignRequest_WebRequest_WithVerifier_IncludesVerifier()
+ {
+ var request = WebRequest.Create("https://example.com/access_token");
+ request.Method = "POST";
+
+ request.SignRequest(_testTokens)
+ .WithVerifier("hfdp7dh39dks9884")
+ .InHeader();
+
+ var authHeader = request.Headers["Authorization"];
+ Assert.Contains("oauth_verifier", authHeader);
+ }
+
+ [Fact]
+ public void SignRequest_WebRequest_WithVersion_UsesSpecifiedVersion()
+ {
+ var request = WebRequest.Create("https://example.com/resource");
+ request.Method = "GET";
+
+ request.SignRequest(_testTokens)
+ .WithVersion("1.0")
+ .InHeader();
+
+ var authHeader = request.Headers["Authorization"];
+ Assert.Contains("oauth_version", authHeader);
+ Assert.Contains("1.0", authHeader);
+ }
+
+ [Fact]
+ public void WithTokens_NullTokens_ThrowsArgumentException()
+ {
+ var request = WebRequest.Create("https://example.com/resource");
+ var wrapper = request.SignRequest();
+
+ Assert.Throws(() => wrapper.WithTokens(null));
+ }
+
+ [Fact]
+ public void SignRequest_WebRequest_WithPostParameters_IncludesInSignature()
+ {
+ var request = WebRequest.Create("https://example.com/resource");
+ request.Method = "POST";
+
+ request.SignRequest(_testTokens)
+ .WithPostParameters("status=hello")
+ .InHeader();
+
+ var authHeader = request.Headers["Authorization"];
+ Assert.NotNull(authHeader);
+ Assert.Contains("oauth_signature", authHeader);
+ }
+
+ [Fact]
+ public void SignRequest_WebRequest_WithPostParameters_SetsContentType()
+ {
+ var request = WebRequest.Create("https://example.com/resource");
+ request.Method = "POST";
+
+ request.SignRequest(_testTokens)
+ .WithPostParameters("status=hello")
+ .InHeader();
+
+ Assert.Equal("application/x-www-form-urlencoded", request.ContentType);
+ }
+
+ [Fact]
+ public void SignRequest_WebRequest_NoAccessToken_OmitsOAuthToken()
+ {
+ var consumerOnlyTokens = new Tokens
+ {
+ ConsumerKey = "key",
+ ConsumerSecret = "secret"
+ };
+
+ var request = WebRequest.Create("https://example.com/request_token");
+ request.Method = "POST";
+
+ request.SignRequest(consumerOnlyTokens).InHeader();
+
+ var authHeader = request.Headers["Authorization"];
+ Assert.DoesNotContain("oauth_token", authHeader);
+ }
+
+ [Fact]
+ public void SignRequest_WebRequest_WithQueryParams_ProducesDifferentSignature()
+ {
+ var request1 = WebRequest.Create("https://example.com/resource?page=1");
+ request1.Method = "GET";
+ request1.SignRequest(_testTokens).InHeader();
+ var header1 = request1.Headers["Authorization"];
+
+ var request2 = WebRequest.Create("https://example.com/resource?page=2");
+ request2.Method = "GET";
+ request2.SignRequest(_testTokens).InHeader();
+ var header2 = request2.Headers["Authorization"];
+
+ // The signatures should differ because the query parameters differ
+ // (nonce/timestamp also differ, but the point is both produce valid headers)
+ Assert.NotEqual(header1, header2);
+ }
+
+ #endregion
+
+ #region HttpRequestMessage Tests
+
+ [Fact]
+ public void SignRequest_HttpRequestMessage_SetsAuthorizationHeader()
+ {
+ var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/resource");
+
+ request.SignRequest(_testTokens)
+ .WithEncryption(EncryptionMethod.HMACSHA1)
+ .InHeader();
+
+ Assert.True(request.Headers.Contains("Authorization"));
+ var authHeader = request.Headers.GetValues("Authorization").First();
+ Assert.StartsWith("OAuth ", authHeader);
+ }
+
+ [Fact]
+ public void SignRequest_HttpRequestMessage_ContainsRequiredOAuthParameters()
+ {
+ var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/resource");
+
+ request.SignRequest(_testTokens)
+ .WithEncryption(EncryptionMethod.HMACSHA1)
+ .InHeader();
+
+ var authHeader = request.Headers.GetValues("Authorization").First();
+ Assert.Contains("oauth_consumer_key", authHeader);
+ Assert.Contains("oauth_nonce", authHeader);
+ Assert.Contains("oauth_signature_method", authHeader);
+ Assert.Contains("oauth_timestamp", authHeader);
+ Assert.Contains("oauth_token", authHeader);
+ Assert.Contains("oauth_version", authHeader);
+ Assert.Contains("oauth_signature", authHeader);
+ }
+
+ [Fact]
+ public void SignRequest_HttpRequestMessage_PostWithContent_SetsSignature()
+ {
+ var request = new HttpRequestMessage(HttpMethod.Post, "https://example.com/resource");
+ request.Content = new FormUrlEncodedContent(new[]
+ {
+ new KeyValuePair("status", "hello")
+ });
+
+ request.SignRequest(_testTokens)
+ .WithPostParameters("status=hello")
+ .InHeader();
+
+ var authHeader = request.Headers.GetValues("Authorization").First();
+ Assert.Contains("oauth_signature", authHeader);
+ }
+
+ [Fact]
+ public void SignRequest_HttpRequestMessage_WithCallback_IncludesCallback()
+ {
+ var request = new HttpRequestMessage(HttpMethod.Post, "https://example.com/request_token");
+
+ request.SignRequest(_testTokens)
+ .WithCallback("https://example.com/callback")
+ .InHeader();
+
+ var authHeader = request.Headers.GetValues("Authorization").First();
+ Assert.Contains("oauth_callback", authHeader);
+ }
+
+ #endregion
+
+ #region DefaultSigningMethod Tests
+
+ [Fact]
+ public void DefaultSigningMethod_IsHmacSha1()
+ {
+ Assert.Equal(EncryptionMethod.HMACSHA1, OAuthRequestWrapper.DefaultSigningMethod);
+ }
+
+ [Fact]
+ public void DefaultSigningMethod_CanBeChanged()
+ {
+ var originalDefault = OAuthRequestWrapper.DefaultSigningMethod;
+ try
+ {
+ OAuthRequestWrapper.DefaultSigningMethod = EncryptionMethod.Plain;
+ Assert.Equal(EncryptionMethod.Plain, OAuthRequestWrapper.DefaultSigningMethod);
+ }
+ finally
+ {
+ OAuthRequestWrapper.DefaultSigningMethod = originalDefault;
+ }
+ }
+
+ #endregion
+
+ #region Chaining API Tests
+
+ [Fact]
+ public void FluentApi_ReturnsSameWrapper()
+ {
+ var request = WebRequest.Create("https://example.com/resource");
+ var wrapper = request.SignRequest();
+
+ var result = wrapper
+ .WithTokens(_testTokens)
+ .WithEncryption(EncryptionMethod.HMACSHA1)
+ .WithVersion("1.0");
+
+ Assert.Same(wrapper, result);
+ }
+
+ #endregion
+}
diff --git a/SimpleOAuth.Tests/SimpleOAuth.Tests.csproj b/SimpleOAuth.Tests/SimpleOAuth.Tests.csproj
new file mode 100644
index 0000000..3adb1fd
--- /dev/null
+++ b/SimpleOAuth.Tests/SimpleOAuth.Tests.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+ $(NoWarn);SYSLIB0014
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SimpleOAuth.Tests/TokensTests.cs b/SimpleOAuth.Tests/TokensTests.cs
new file mode 100644
index 0000000..e168ee6
--- /dev/null
+++ b/SimpleOAuth.Tests/TokensTests.cs
@@ -0,0 +1,76 @@
+namespace SimpleOAuth.Tests;
+
+public class TokensTests
+{
+ [Fact]
+ public void MergeWith_UpdatesNonEmptyTokens()
+ {
+ var original = new Tokens
+ {
+ ConsumerKey = "key1",
+ ConsumerSecret = "secret1",
+ AccessToken = "token1",
+ AccessTokenSecret = "tokenSecret1"
+ };
+
+ var newTokens = new Tokens
+ {
+ AccessToken = "newToken",
+ AccessTokenSecret = "newTokenSecret"
+ };
+
+ original.MergeWith(newTokens);
+
+ Assert.Equal("key1", original.ConsumerKey);
+ Assert.Equal("secret1", original.ConsumerSecret);
+ Assert.Equal("newToken", original.AccessToken);
+ Assert.Equal("newTokenSecret", original.AccessTokenSecret);
+ }
+
+ [Fact]
+ public void MergeWith_DoesNotOverwriteWithEmpty()
+ {
+ var original = new Tokens
+ {
+ ConsumerKey = "key1",
+ ConsumerSecret = "secret1",
+ AccessToken = "token1",
+ AccessTokenSecret = "tokenSecret1"
+ };
+
+ var newTokens = new Tokens
+ {
+ ConsumerKey = "",
+ ConsumerSecret = null,
+ AccessToken = "newToken",
+ AccessTokenSecret = ""
+ };
+
+ original.MergeWith(newTokens);
+
+ Assert.Equal("key1", original.ConsumerKey);
+ Assert.Equal("secret1", original.ConsumerSecret);
+ Assert.Equal("newToken", original.AccessToken);
+ Assert.Equal("tokenSecret1", original.AccessTokenSecret);
+ }
+
+ [Fact]
+ public void MergeWith_AllNewTokens()
+ {
+ var original = new Tokens();
+ var newTokens = new Tokens
+ {
+ ConsumerKey = "key",
+ ConsumerSecret = "secret",
+ AccessToken = "token",
+ AccessTokenSecret = "tokenSecret"
+ };
+
+ original.MergeWith(newTokens);
+
+ Assert.Equal("key", original.ConsumerKey);
+ Assert.Equal("secret", original.ConsumerSecret);
+ Assert.Equal("token", original.AccessToken);
+ Assert.Equal("tokenSecret", original.AccessTokenSecret);
+ }
+}
diff --git a/SimpleOAuth.Tests/UrlHelperTests.cs b/SimpleOAuth.Tests/UrlHelperTests.cs
new file mode 100644
index 0000000..a18bec3
--- /dev/null
+++ b/SimpleOAuth.Tests/UrlHelperTests.cs
@@ -0,0 +1,79 @@
+using SimpleOAuth.Utilities;
+
+namespace SimpleOAuth.Tests;
+
+public class UrlHelperTests
+{
+ [Theory]
+ [InlineData("", "")]
+ [InlineData("hello", "hello")]
+ [InlineData("hello world", "hello%20world")]
+ [InlineData("test@example.com", "test%40example.com")]
+ [InlineData("100%", "100%25")]
+ [InlineData("a+b", "a%2Bb")]
+ [InlineData("foo=bar&baz=qux", "foo%3Dbar%26baz%3Dqux")]
+ public void Encode_EncodesCorrectly(string input, string expected)
+ {
+ var result = UrlHelper.Encode(input);
+ Assert.Equal(expected, result);
+ }
+
+ [Fact]
+ public void Encode_PreservesUnreservedCharacters()
+ {
+ // RFC 3986 unreserved characters: A-Z a-z 0-9 - . _ ~
+ var unreserved = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
+ var result = UrlHelper.Encode(unreserved);
+ Assert.Equal(unreserved, result);
+ }
+
+ [Fact]
+ public void Encode_NullReturnsEmpty()
+ {
+ var result = UrlHelper.Encode(null);
+ Assert.Equal(string.Empty, result);
+ }
+
+ [Theory]
+ [InlineData("hello%20world", "hello world")]
+ [InlineData("hello+world", "hello world")]
+ [InlineData("test%40example.com", "test@example.com")]
+ public void Decode_DecodesCorrectly(string input, string expected)
+ {
+ var result = UrlHelper.Decode(input);
+ Assert.Equal(expected, result);
+ }
+
+ [Fact]
+ public void ParseQueryString_ParsesSimpleQuery()
+ {
+ var result = UrlHelper.ParseQueryString("foo=bar&baz=qux");
+ Assert.Equal(2, result.Count);
+ Assert.Equal("bar", result["foo"]);
+ Assert.Equal("qux", result["baz"]);
+ }
+
+ [Fact]
+ public void ParseQueryString_HandlesLeadingQuestionMark()
+ {
+ var result = UrlHelper.ParseQueryString("?foo=bar&baz=qux");
+ Assert.Equal(2, result.Count);
+ Assert.Equal("bar", result["foo"]);
+ Assert.Equal("qux", result["baz"]);
+ }
+
+ [Fact]
+ public void ParseQueryString_HandlesEncodedValues()
+ {
+ var result = UrlHelper.ParseQueryString("name=hello+world&key=test%40value");
+ Assert.Equal("hello world", result["name"]);
+ Assert.Equal("test@value", result["key"]);
+ }
+
+ [Fact]
+ public void ParseQueryString_EmptyStringReturnsEmpty()
+ {
+ var result = UrlHelper.ParseQueryString("");
+ Assert.Empty(result);
+ }
+}
diff --git a/SimpleOAuth.WindowsPhone.sln b/SimpleOAuth.WindowsPhone.sln
deleted file mode 100644
index 87d0712..0000000
--- a/SimpleOAuth.WindowsPhone.sln
+++ /dev/null
@@ -1,28 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 11.00
-# Visual Studio 2010
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleOAuth.WP.Mango", "SimpleOAuth.WP.Mango\SimpleOAuth.WP.Mango.csproj", "{A0632EE3-C5C8-4ED9-9CE4-27486B2B8295}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleOAuthTester.WP.Mango", "SimpleOAuthTester.WP.Mango\SimpleOAuthTester.WP.Mango.csproj", "{F22A9EAE-F610-4260-89A8-4E5856AF33C4}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {A0632EE3-C5C8-4ED9-9CE4-27486B2B8295}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A0632EE3-C5C8-4ED9-9CE4-27486B2B8295}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A0632EE3-C5C8-4ED9-9CE4-27486B2B8295}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A0632EE3-C5C8-4ED9-9CE4-27486B2B8295}.Release|Any CPU.Build.0 = Release|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Release|Any CPU.Build.0 = Release|Any CPU
- {F22A9EAE-F610-4260-89A8-4E5856AF33C4}.Release|Any CPU.Deploy.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
diff --git a/SimpleOAuth.sln b/SimpleOAuth.sln
index 2648395..c892603 100644
--- a/SimpleOAuth.sln
+++ b/SimpleOAuth.sln
@@ -1,5 +1,5 @@
-Microsoft Visual Studio Solution File, Format Version 11.00
+Microsoft Visual Studio Solution File, Format Version 12.00
# Visual C# Express 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleOAuth", "SimpleOAuth\SimpleOAuth.csproj", "{7195A9BE-2949-4819-9BEB-7B5F366B7CBC}"
EndProject
@@ -7,14 +7,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleOAuthTwitter", "Simpl
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleOAuthTester", "SimpleOAuthTester\SimpleOAuthTester.csproj", "{70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleOAuth.Tests", "SimpleOAuth.Tests\SimpleOAuth.Tests.csproj", "{AA8F952A-0E2C-45F4-A276-6C68EA81A260}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|x86 = Debug|x86
+ Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|x86 = Release|x86
+ Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
@@ -22,31 +26,59 @@ Global
{7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Debug|x64.Build.0 = Debug|Any CPU
{7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Release|Any CPU.Build.0 = Release|Any CPU
{7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Release|x86.ActiveCfg = Release|Any CPU
+ {7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Release|x64.ActiveCfg = Release|Any CPU
+ {7195A9BE-2949-4819-9BEB-7B5F366B7CBC}.Release|x64.Build.0 = Release|Any CPU
{797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Debug|Any CPU.ActiveCfg = Debug|x86
{797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Debug|Mixed Platforms.Build.0 = Debug|x86
{797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Debug|x86.ActiveCfg = Debug|x86
{797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Debug|x86.Build.0 = Debug|x86
+ {797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Debug|x64.Build.0 = Debug|Any CPU
{797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Release|Any CPU.ActiveCfg = Release|x86
{797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Release|Mixed Platforms.ActiveCfg = Release|x86
{797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Release|Mixed Platforms.Build.0 = Release|x86
{797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Release|x86.ActiveCfg = Release|x86
{797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Release|x86.Build.0 = Release|x86
+ {797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Release|x64.ActiveCfg = Release|Any CPU
+ {797DB1AA-32A5-4EEE-B5BD-73667CEE112F}.Release|x64.Build.0 = Release|Any CPU
{70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Debug|Any CPU.ActiveCfg = Debug|x86
{70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Debug|Mixed Platforms.Build.0 = Debug|x86
{70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Debug|x86.ActiveCfg = Debug|x86
{70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Debug|x86.Build.0 = Debug|x86
+ {70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Debug|x64.Build.0 = Debug|Any CPU
{70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Release|Any CPU.ActiveCfg = Release|x86
{70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Release|Mixed Platforms.ActiveCfg = Release|x86
{70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Release|Mixed Platforms.Build.0 = Release|x86
{70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Release|x86.ActiveCfg = Release|x86
{70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Release|x86.Build.0 = Release|x86
+ {70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Release|x64.ActiveCfg = Release|Any CPU
+ {70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}.Release|x64.Build.0 = Release|Any CPU
+ {AA8F952A-0E2C-45F4-A276-6C68EA81A260}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AA8F952A-0E2C-45F4-A276-6C68EA81A260}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AA8F952A-0E2C-45F4-A276-6C68EA81A260}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {AA8F952A-0E2C-45F4-A276-6C68EA81A260}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {AA8F952A-0E2C-45F4-A276-6C68EA81A260}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {AA8F952A-0E2C-45F4-A276-6C68EA81A260}.Debug|x86.Build.0 = Debug|Any CPU
+ {AA8F952A-0E2C-45F4-A276-6C68EA81A260}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {AA8F952A-0E2C-45F4-A276-6C68EA81A260}.Debug|x64.Build.0 = Debug|Any CPU
+ {AA8F952A-0E2C-45F4-A276-6C68EA81A260}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AA8F952A-0E2C-45F4-A276-6C68EA81A260}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AA8F952A-0E2C-45F4-A276-6C68EA81A260}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {AA8F952A-0E2C-45F4-A276-6C68EA81A260}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {AA8F952A-0E2C-45F4-A276-6C68EA81A260}.Release|x86.ActiveCfg = Release|Any CPU
+ {AA8F952A-0E2C-45F4-A276-6C68EA81A260}.Release|x86.Build.0 = Release|Any CPU
+ {AA8F952A-0E2C-45F4-A276-6C68EA81A260}.Release|x64.ActiveCfg = Release|Any CPU
+ {AA8F952A-0E2C-45F4-A276-6C68EA81A260}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/SimpleOAuth/Extensions.cs b/SimpleOAuth/Extensions.cs
index f508be2..92916d4 100644
--- a/SimpleOAuth/Extensions.cs
+++ b/SimpleOAuth/Extensions.cs
@@ -1,12 +1,12 @@
-// Simple OAuth .Net
+// Simple OAuth .Net
// (c) 2012 Daniel McKenzie
// Simple OAuth .Net may be freely distributed under the MIT license.
using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Net;
using System.IO;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
using SimpleOAuth.Utilities;
namespace SimpleOAuth
@@ -16,6 +16,7 @@ namespace SimpleOAuth
///
public static class Extensions
{
+ #region WebRequest Extensions
///
/// Begin signing this object with OAuth.
@@ -38,7 +39,6 @@ public static OAuthRequestWrapper SignRequest(this WebRequest request, Tokens wi
return new OAuthRequestWrapper(request) { RequestTokens = withTokens };
}
-#if !SILVERLIGHT
///
/// For the Request and Access Token stages, makes the request and parses out the OAuth tokens from
/// the server.
@@ -46,19 +46,6 @@ public static OAuthRequestWrapper SignRequest(this WebRequest request, Tokens wi
/// The request that needs to be signed with OAuth.
/// A object containing the Access Token and Access Secret provided by the OAuth server.
/// You typically call this when making a request to get the users access tokens and combine this with the function.
- ///
- ///
- /// var request = WebRequest.Create("https://api.twitter.com/oauth/request_token") { Method = "POST" };
- /// request.SignRequest(RequestTokens)
- /// .WithCallback("oob")
- /// .InHeader();
- ///
- /// var accessTokens = request.GetOAuthTokens();
- /// RequestTokens.MergeWith(accessTokens);
- ///
- /// In the above example, the is created, and signed with a specific set of . A call to
- /// is made and then merged with the original Request Tokens.
- ///
/// Thrown when the encounters an error.
public static Tokens GetOAuthTokens(this WebRequest request)
{
@@ -79,97 +66,116 @@ public static Tokens GetOAuthTokens(this WebRequest request)
newTokens.AccessToken = dataValues["oauth_token"];
newTokens.AccessTokenSecret = dataValues["oauth_token_secret"];
-
}
return newTokens;
}
-#endif
///
- /// For the Request and Access Token stages, makes the request and parses out the OAuth tokens from
+ /// For the Request and Access Token stages, makes the request asynchronously and parses out the OAuth tokens from
/// the server.
///
/// The request that needs to be signed with OAuth.
/// A object containing the Access Token and Access Secret provided by the OAuth server.
/// You typically call this when making a request to get the users access tokens and combine this with the function.
- ///
- ///
- /// var request = WebRequest.Create("https://api.twitter.com/oauth/request_token") { Method = "POST" };
- /// request.SignRequest(RequestTokens)
- /// .WithCallback("oob")
- /// .InHeader();
- ///
- /// var accessTokens = request.GetOAuthTokens();
- /// RequestTokens.MergeWith(accessTokens);
- ///
- /// In the above example, the is created, and signed with a specific set of . A call to
- /// is made and then merged with the original Request Tokens.
- ///
/// Thrown when the encounters an error.
- public static void GetOAuthTokensAsync(this WebRequest request, Action callback)
+ public static async Task GetOAuthTokensAsync(this WebRequest request)
{
var newTokens = new Tokens();
- var output = string.Empty;
-
- WebResponse response = null;
- StreamReader responseStreamReader = null;
- Exception thrownException = null;
+ using (var response = await request.GetResponseAsync())
+ using (var reader = new StreamReader(response.GetResponseStream()))
+ {
+ var output = await reader.ReadToEndAsync();
- request.BeginGetResponse((responseResult) =>
+ if (!String.IsNullOrEmpty(output))
{
- try
+ var dataValues = UrlHelper.ParseQueryString(output);
+
+ if (!dataValues.ContainsKey("oauth_token")
+ || !dataValues.ContainsKey("oauth_token_secret"))
{
- try
- {
- response = request.EndGetResponse(responseResult);
-
- responseStreamReader = new StreamReader(response.GetResponseStream());
-
- output = responseStreamReader.ReadToEnd();
-
- if (!String.IsNullOrEmpty(output))
- {
- var dataValues = UrlHelper.ParseQueryString(output);
-
- if (!dataValues.ContainsKey("oauth_token")
- || !dataValues.ContainsKey("oauth_token_secret"))
- {
- var ex = new Exception("Response did not contain oauth_token and oauth_token_secret. Response is contained in Data of exception.");
- ex.Data.Add("ResponseText", output);
- ex.Data.Add("RequestUri", request.RequestUri);
- ex.Data.Add("RequestMethod", request.Method);
- ex.Data.Add("RequestHeaders", request.Headers);
- ex.Data.Add("ResponseHeaders", response.Headers);
- throw ex;
- }
- newTokens.AccessToken = dataValues["oauth_token"];
- newTokens.AccessTokenSecret = dataValues["oauth_token_secret"];
- }
- }
- catch (Exception ex)
- {
- thrownException = ex;
- }
-
- if (thrownException != null)
- {
- callback(null, thrownException);
- }
- else
- {
- callback(newTokens, null);
- }
+ var ex = new InvalidOperationException(
+ "Response did not contain oauth_token and oauth_token_secret.");
+ ex.Data.Add("ResponseText", output);
+ ex.Data.Add("RequestUri", request.RequestUri);
+ ex.Data.Add("RequestMethod", request.Method);
+ throw ex;
}
- finally
+
+ newTokens.AccessToken = dataValues["oauth_token"];
+ newTokens.AccessTokenSecret = dataValues["oauth_token_secret"];
+ }
+ }
+
+ return newTokens;
+ }
+
+ #endregion
+
+ #region HttpRequestMessage Extensions
+
+ ///
+ /// Begin signing this with OAuth.
+ ///
+ /// The HTTP request message that needs to be signed with OAuth.
+ /// An used to provide the required parameters for OAuth signing.
+ public static OAuthRequestWrapper SignRequest(this HttpRequestMessage request)
+ {
+ return new OAuthRequestWrapper(request);
+ }
+
+ ///
+ /// Begin signing this with OAuth using the tokens provided.
+ ///
+ /// The HTTP request message that needs to be signed with OAuth.
+ /// The to use to sign the request with.
+ /// An used to provide the required parameters for OAuth signing.
+ public static OAuthRequestWrapper SignRequest(this HttpRequestMessage request, Tokens withTokens)
+ {
+ return new OAuthRequestWrapper(request) { RequestTokens = withTokens };
+ }
+
+ ///
+ /// Sends the signed request using the provided and parses out the OAuth tokens
+ /// from the server response.
+ ///
+ /// The signed HTTP request message.
+ /// The to send the request with.
+ /// A object containing the Access Token and Access Secret provided by the OAuth server.
+ /// Thrown when the request encounters an error.
+ public static async Task GetOAuthTokensAsync(this HttpRequestMessage request, HttpClient client)
+ {
+ var newTokens = new Tokens();
+
+ using (var response = await client.SendAsync(request))
+ {
+ response.EnsureSuccessStatusCode();
+ var output = await response.Content.ReadAsStringAsync();
+
+ if (!String.IsNullOrEmpty(output))
+ {
+ var dataValues = UrlHelper.ParseQueryString(output);
+
+ if (!dataValues.ContainsKey("oauth_token")
+ || !dataValues.ContainsKey("oauth_token_secret"))
{
- try { if (responseStreamReader != null) responseStreamReader.Dispose(); }
- catch { }
- try { if (response != null) ((IDisposable)response).Dispose(); }
- catch { }
+ var ex = new InvalidOperationException(
+ "Response did not contain oauth_token and oauth_token_secret.");
+ ex.Data.Add("ResponseText", output);
+ ex.Data.Add("RequestUri", request.RequestUri);
+ ex.Data.Add("RequestMethod", request.Method);
+ throw ex;
}
- }, null);
+
+ newTokens.AccessToken = dataValues["oauth_token"];
+ newTokens.AccessTokenSecret = dataValues["oauth_token_secret"];
+ }
+ }
+
+ return newTokens;
}
+
+ #endregion
}
}
diff --git a/SimpleOAuth/OAuthRequestWrapper.cs b/SimpleOAuth/OAuthRequestWrapper.cs
index 34b6aa7..d0c0441 100644
--- a/SimpleOAuth/OAuthRequestWrapper.cs
+++ b/SimpleOAuth/OAuthRequestWrapper.cs
@@ -1,4 +1,4 @@
-// Simple OAuth .Net
+// Simple OAuth .Net
// (c) 2012 Daniel McKenzie
// Simple OAuth .Net may be freely distributed under the MIT license.
@@ -6,16 +6,15 @@
using System.Collections.Generic;
using System.Text;
using System.Net;
+using System.Net.Http;
using SimpleOAuth.Generators;
using SimpleOAuth.Utilities;
using SimpleOAuth.Internal;
-using System.ComponentModel;
-using System.IO;
namespace SimpleOAuth
{
///
- /// Contains a and does all the necessary work in order to sign it
+ /// Contains a request and does all the necessary work in order to sign it
/// as a valid OAuth request before it gets sent.
///
public sealed class OAuthRequestWrapper
@@ -49,6 +48,7 @@ public static EncryptionMethod DefaultSigningMethod
#region " Properties "
private WebRequest ContainedRequest { get; set; }
+ private HttpRequestMessage ContainedHttpRequest { get; set; }
///
/// The consumer and access keys (if required) to sign the OAuth request.
@@ -63,7 +63,7 @@ public static EncryptionMethod DefaultSigningMethod
public EncryptionMethod SigningMethod { get; set; }
///
- /// The parameters to include in the OAuth base signature string when doing a POST request.
+ /// The parameters to include in the OAuth base signature string when doing a POST request.
///
/// Typically, this would be provided with .
public string PostParameters { get; set; }
@@ -72,12 +72,12 @@ public static EncryptionMethod DefaultSigningMethod
///
/// The OAuth version to use, by default it is 1.0.
///
- public string OAuthVersion {
- get {
- return _oauthVersion;
- }
- set {
- _oauthVersion = value;
+ public string OAuthVersion {
+ get {
+ return _oauthVersion;
+ }
+ set {
+ _oauthVersion = value;
}
}
@@ -99,7 +99,7 @@ private Dictionary AuthorizationHeader
#region " Constructor "
///
- /// There is only one constructor, and the OAuthRequestWrapper can only be instantiated internally to the library.
+ /// Creates a wrapper around a for OAuth signing.
///
internal OAuthRequestWrapper(WebRequest toContain)
{
@@ -107,6 +107,15 @@ internal OAuthRequestWrapper(WebRequest toContain)
SigningMethod = OAuthRequestWrapper.DefaultSigningMethod;
}
+ ///
+ /// Creates a wrapper around an for OAuth signing.
+ ///
+ internal OAuthRequestWrapper(HttpRequestMessage toContain)
+ {
+ ContainedHttpRequest = toContain;
+ SigningMethod = OAuthRequestWrapper.DefaultSigningMethod;
+ }
+
#endregion
#region " Chaining Methods "
@@ -177,22 +186,22 @@ public OAuthRequestWrapper WithVersion(string version)
}
///
- /// Provide the POST request to generate a valid OAuth signature.
+ /// Provide the POST parameters to generate a valid OAuth signature.
///
/// The POST parameters that will be sent.
/// Itself to chain.
/// The OAuth standard requires POST parameters sent in application/x-www-form-urlencoded
- /// requests to be included in the OAuth base string to create a valid signature. If it is not provided,
- /// then the request will fail. If this is set, then the of the
- /// will be set to 'application/x-www-form-urlencoded'
- /// if it is not already set.
+ /// requests to be included in the OAuth base string to create a valid signature.
public OAuthRequestWrapper WithPostParameters(string parameters)
{
PostParameters = parameters;
- if (String.IsNullOrEmpty(ContainedRequest.ContentType))
+ if (ContainedRequest != null)
{
- ContainedRequest.ContentType = FormUrlEncodedMimeType;
+ if (String.IsNullOrEmpty(ContainedRequest.ContentType))
+ {
+ ContainedRequest.ContentType = FormUrlEncodedMimeType;
+ }
}
return this;
@@ -220,7 +229,16 @@ public void InHeader()
builder.AppendFormat("{0}=\"{1}\"", UrlHelper.Encode(pair.Key), UrlHelper.Encode(pair.Value));
}
- ContainedRequest.Headers["Authorization"] = builder.ToString();
+ var headerValue = builder.ToString();
+
+ if (ContainedRequest != null)
+ {
+ ContainedRequest.Headers["Authorization"] = headerValue;
+ }
+ else if (ContainedHttpRequest != null)
+ {
+ ContainedHttpRequest.Headers.TryAddWithoutValidation("Authorization", headerValue);
+ }
}
#endregion
@@ -237,20 +255,46 @@ private void createAuthorization()
AuthorizationHeader.Add("oauth_version", OAuthVersion);
}
- private void createSignature()
+ private string GetRequestMethod()
+ {
+ if (ContainedRequest != null)
+ return ContainedRequest.Method;
+ return ContainedHttpRequest.Method.Method;
+ }
+
+ private Uri GetRequestUri()
{
+ if (ContainedRequest != null)
+ return ContainedRequest.RequestUri;
+ return ContainedHttpRequest.RequestUri;
+ }
+
+ private string GetContentType()
+ {
+ if (ContainedRequest != null)
+ return ContainedRequest.ContentType;
+ if (ContainedHttpRequest?.Content != null)
+ return ContainedHttpRequest.Content.Headers.ContentType?.MediaType;
+ return null;
+ }
- var method = ContainedRequest.Method;
- var baseUrl = String.Format("{0}://{1}{2}", ContainedRequest.RequestUri.Scheme, ContainedRequest.RequestUri.Host, ContainedRequest.RequestUri.AbsolutePath);
+ private void createSignature()
+ {
+ var method = GetRequestMethod();
+ var requestUri = GetRequestUri();
+ var baseUrl = String.Format("{0}://{1}{2}", requestUri.Scheme, requestUri.Host, requestUri.AbsolutePath);
var parameters = new SortedDictionary(AuthorizationHeader);
- var queryParams = UrlHelper.ParseQueryString(ContainedRequest.RequestUri.Query);
+ var queryParams = UrlHelper.ParseQueryString(requestUri.Query);
foreach (var pair in queryParams)
{
parameters.Add(pair.Key, pair.Value);
}
- if (method.Equals("POST") && !String.IsNullOrEmpty(ContainedRequest.ContentType) && ContainedRequest.ContentType.Equals(FormUrlEncodedMimeType))
+ var contentType = GetContentType();
+ if (method.Equals("POST", StringComparison.OrdinalIgnoreCase)
+ && !String.IsNullOrEmpty(contentType)
+ && contentType.Equals(FormUrlEncodedMimeType, StringComparison.OrdinalIgnoreCase))
{
if (!String.IsNullOrEmpty(PostParameters))
{
@@ -274,7 +318,7 @@ private void createSignature()
}
// percent encode everything
- var encodedParams = String.Format("{0}&{1}&{2}", ContainedRequest.Method.ToUpper(),
+ var encodedParams = String.Format("{0}&{1}&{2}", method.ToUpper(),
UrlHelper.Encode(baseUrl), UrlHelper.Encode(paramString.ToString()));
// key
@@ -282,7 +326,7 @@ private void createSignature()
UrlHelper.Encode(RequestTokens.AccessTokenSecret));
// signature time!
- string signature = SignatureMethod.CreateSignature(this.SigningMethod, encodedParams.ToString(), key);
+ string signature = SignatureMethod.CreateSignature(this.SigningMethod, encodedParams, key);
AuthorizationHeader.Add("oauth_signature", signature);
}
diff --git a/SimpleOAuth/Properties/AssemblyInfo.cs b/SimpleOAuth/Properties/AssemblyInfo.cs
deleted file mode 100644
index abe2948..0000000
--- a/SimpleOAuth/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("SimpleOAuth")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("SimpleOAuth")]
-[assembly: AssemblyCopyright("Copyright © 2012")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("4cc478ed-47cb-4c0d-878d-334abfa647a9")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/SimpleOAuth/SimpleOAuth.csproj b/SimpleOAuth/SimpleOAuth.csproj
index 1efcaab..373c290 100644
--- a/SimpleOAuth/SimpleOAuth.csproj
+++ b/SimpleOAuth/SimpleOAuth.csproj
@@ -1,67 +1,25 @@
-
-
+
+
- Debug
- AnyCPU
- 8.0.30703
- 2.0
- {7195A9BE-2949-4819-9BEB-7B5F366B7CBC}
- Library
- Properties
+ net10.0
SimpleOAuth
SimpleOAuth
- v3.5
- 512
-
+ true
+ $(NoWarn);CS1591
+
+
+ SimpleOAuth.Net
+ 2.0.0
+ Daniel McKenzie and Chris Benard
+ OAuth libraries come in all shapes and sizes, however in the .Net land, they only come in one - extra large. This library is made to rectify that. It's a small library, with no dependencies that lets you sign HTTP requests to your hearts content.
+ Copyright 2012-2026
+ oauth simpleoauth simpleoauth.net
+ MIT
+ https://github.com/cbenard/SimpleOAuth.Net
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
- bin\Release\SimpleOAuth.xml
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
- bin\Release\SimpleOAuth.xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
-
-
-
\ No newline at end of file
+
+
diff --git a/SimpleOAuthTester/Program.cs b/SimpleOAuthTester/Program.cs
index bbfe7f8..a7d9c08 100644
--- a/SimpleOAuthTester/Program.cs
+++ b/SimpleOAuthTester/Program.cs
@@ -1,10 +1,7 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using System.Net;
using SimpleOAuth;
-using System.Diagnostics;
+using SimpleOAuth.Utilities;
using System.IO;
namespace SimpleOAuthTester
diff --git a/SimpleOAuthTester/Properties/AssemblyInfo.cs b/SimpleOAuthTester/Properties/AssemblyInfo.cs
deleted file mode 100644
index 40efc7f..0000000
--- a/SimpleOAuthTester/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("SimpleOAuthTester")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("SimpleOAuthTester")]
-[assembly: AssemblyCopyright("Copyright © 2012")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("ced799eb-3e91-4b87-83ee-06cf96177868")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/SimpleOAuthTester/SimpleOAuthTester.csproj b/SimpleOAuthTester/SimpleOAuthTester.csproj
index 4271b5a..82580f6 100644
--- a/SimpleOAuthTester/SimpleOAuthTester.csproj
+++ b/SimpleOAuthTester/SimpleOAuthTester.csproj
@@ -1,64 +1,15 @@
-
-
+
+
- Debug
- x86
- 8.0.30703
- 2.0
- {70CD80CC-5FED-47CD-B8A5-AC7E00276EFA}
Exe
- Properties
+ net10.0
SimpleOAuthTester
SimpleOAuthTester
- v4.0
- Client
- 512
+ $(NoWarn);SYSLIB0014
-
- x86
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- x86
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
- {7195A9BE-2949-4819-9BEB-7B5F366B7CBC}
- SimpleOAuth
-
+
-
-
-
\ No newline at end of file
+
+
diff --git a/SimpleOAuthTester/UrlHelper.cs b/SimpleOAuthTester/UrlHelper.cs
deleted file mode 100644
index d1b62df..0000000
--- a/SimpleOAuthTester/UrlHelper.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Collections.Specialized;
-
-namespace SimpleOAuthTester
-{
- ///
- /// URL encoding class. Note: use at your own risk.
- /// Written by: Ian Hopkins (http://www.lucidhelix.com)
- /// Date: 2008-Dec-23
- /// (Ported to C# by t3rse (http://www.t3rse.com))
- /// Source: http://stackoverflow.com/questions/14731/urlencode-through-a-console-application
- ///
- public class UrlHelper
- {
- public static string Encode(string str)
- {
- var charClass = String.Format("0-9a-zA-Z{0}", Regex.Escape("-_.!~*'()"));
- return Regex.Replace(str,
- String.Format("[^{0}]", charClass),
- new MatchEvaluator(EncodeEvaluator));
- }
-
- public static string EncodeEvaluator(Match match)
- {
- return (match.Value == " ") ? "+" : String.Format("%{0:X2}", Convert.ToInt32(match.Value[0]));
- }
-
- public static string DecodeEvaluator(Match match)
- {
- return Convert.ToChar(int.Parse(match.Value.Substring(1), System.Globalization.NumberStyles.HexNumber)).ToString();
- }
-
- public static string Decode(string str)
- {
- return Regex.Replace(str.Replace('+', ' '), "%[0-9a-zA-Z][0-9a-zA-Z]", new MatchEvaluator(DecodeEvaluator));
- }
-
- public static Dictionary ParseQueryString(string query)
- {
- var collection = new Dictionary();
- var queryParts = query.Split(new char[] { '&' }, StringSplitOptions.RemoveEmptyEntries);
- foreach (var segment in queryParts)
- {
- var segmentParts = segment.Split('=');
- collection.Add(segmentParts[0].Trim(new char[] { '?', ' ' }), UrlHelper.Decode(segmentParts[1].Trim()));
- }
-
- return collection;
- }
- }
-}
diff --git a/SimpleOAuthTwitter/Program.cs b/SimpleOAuthTwitter/Program.cs
index 89cc5ea..133add6 100644
--- a/SimpleOAuthTwitter/Program.cs
+++ b/SimpleOAuthTwitter/Program.cs
@@ -1,7 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using System.Net;
using SimpleOAuth;
using System.Diagnostics;
diff --git a/SimpleOAuthTwitter/Properties/AssemblyInfo.cs b/SimpleOAuthTwitter/Properties/AssemblyInfo.cs
deleted file mode 100644
index fe54bef..0000000
--- a/SimpleOAuthTwitter/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("SimpleOAuthTwitter")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("SimpleOAuthTwitter")]
-[assembly: AssemblyCopyright("Copyright © 2012")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("910d5b91-8a64-44b5-8877-5a369d1873f9")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/SimpleOAuthTwitter/SimpleOAuthTwitter.csproj b/SimpleOAuthTwitter/SimpleOAuthTwitter.csproj
index ba9547e..3c19561 100644
--- a/SimpleOAuthTwitter/SimpleOAuthTwitter.csproj
+++ b/SimpleOAuthTwitter/SimpleOAuthTwitter.csproj
@@ -1,67 +1,15 @@
-
-
+
+
- Debug
- x86
- 8.0.30703
- 2.0
- {797DB1AA-32A5-4EEE-B5BD-73667CEE112F}
Exe
- Properties
+ net10.0
SimpleOAuthTwitter
SimpleOAuthTwitter
- v4.0
- Client
- 512
+ $(NoWarn);SYSLIB0014
-
- x86
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- x86
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {7195A9BE-2949-4819-9BEB-7B5F366B7CBC}
- SimpleOAuth
-
-
+
-
+
-
-
-
\ No newline at end of file
+
+
diff --git a/SimpleOAuthTwitter/UrlHelper.cs b/SimpleOAuthTwitter/UrlHelper.cs
deleted file mode 100644
index 4c8761f..0000000
--- a/SimpleOAuthTwitter/UrlHelper.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Collections.Specialized;
-
-namespace SimpleOAuthTwitter
-{
- ///
- /// URL encoding class. Note: use at your own risk.
- /// Written by: Ian Hopkins (http://www.lucidhelix.com)
- /// Date: 2008-Dec-23
- /// (Ported to C# by t3rse (http://www.t3rse.com))
- /// Source: http://stackoverflow.com/questions/14731/urlencode-through-a-console-application
- ///
- public class UrlHelper
- {
- public static string Encode(string str)
- {
- var charClass = String.Format("0-9a-zA-Z{0}", Regex.Escape("-_.~"));
- return Regex.Replace(str,
- String.Format("[^{0}]", charClass),
- new MatchEvaluator(EncodeEvaluator));
- }
-
- public static string EncodeEvaluator(Match match)
- {
- return String.Format("%{0:X2}", Convert.ToInt32(match.Value[0]));
- }
-
- public static string DecodeEvaluator(Match match)
- {
- return Convert.ToChar(int.Parse(match.Value.Substring(1), System.Globalization.NumberStyles.HexNumber)).ToString();
- }
-
- public static string Decode(string str)
- {
- return Regex.Replace(str.Replace('+', ' '), "%[0-9a-zA-Z][0-9a-zA-Z]", new MatchEvaluator(DecodeEvaluator));
- }
-
- public static Dictionary ParseQueryString(string query)
- {
- var collection = new Dictionary();
- var queryParts = query.Split(new char[] { '&' }, StringSplitOptions.RemoveEmptyEntries);
- foreach (var segment in queryParts)
- {
- var segmentParts = segment.Split('=');
- collection.Add(segmentParts[0].Trim(new char[] { '?', ' ' }), UrlHelper.Decode(segmentParts[1].Trim()));
- }
-
- return collection;
- }
- }
-}
diff --git a/SimpleOAuthTwitter/app.config b/SimpleOAuthTwitter/app.config
deleted file mode 100644
index 8577e93..0000000
--- a/SimpleOAuthTwitter/app.config
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file