summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2008-05-26 23:15:21 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2008-05-26 23:15:21 -0700
commit24bcd4604eef4e77c0db3408df65ea3f7ff33e0e (patch)
tree2c7fddddd0351473966ecaa27cef9ec1836ab072
parentea26f06c67848867d693c178335dad579e987154 (diff)
downloadDotNetOpenAuth-24bcd4604eef4e77c0db3408df65ea3f7ff33e0e.zip
DotNetOpenAuth-24bcd4604eef4e77c0db3408df65ea3f7ff33e0e.tar.gz
DotNetOpenAuth-24bcd4604eef4e77c0db3408df65ea3f7ff33e0e.tar.bz2
Breaking changes! OpenID indirect messages larger than 2KB are now sent via form POST instead of GET.
Refactored public API to allow for form POST responses. This scenario is not being tested.
-rw-r--r--samples/RelyingPartyCustomStore/login.aspx.cs6
-rw-r--r--src/DotNetOpenId.Test/EndToEndTesting.cs12
-rw-r--r--src/DotNetOpenId.Test/Extensions/AttributeExchangeTests.cs2
-rw-r--r--src/DotNetOpenId.Test/Extensions/ExtensionTestBase.cs7
-rw-r--r--src/DotNetOpenId.Test/Hosting/AspNetHost.cs2
-rw-r--r--src/DotNetOpenId.Test/Provider/IAuthenticationRequestTest.cs4
-rw-r--r--src/DotNetOpenId.Test/RelyingParty/AuthenticationResponseTests.cs8
-rw-r--r--src/DotNetOpenId.Test/RelyingParty/OpenIdRelyingPartyTest.cs21
-rw-r--r--src/DotNetOpenId.Test/TestSupport.cs13
-rw-r--r--src/DotNetOpenId.Test/UtilTest.cs10
-rw-r--r--src/DotNetOpenId.sln4
-rw-r--r--src/DotNetOpenId/DotNetOpenId.csproj10
-rw-r--r--src/DotNetOpenId/IEncodable.cs (renamed from src/DotNetOpenId/Provider/IEncodable.cs)18
-rw-r--r--src/DotNetOpenId/IResponse.cs (renamed from src/DotNetOpenId/Provider/IResponse.cs)4
-rw-r--r--src/DotNetOpenId/MessageEncoder.cs106
-rw-r--r--src/DotNetOpenId/OpenIdException.cs4
-rw-r--r--src/DotNetOpenId/Provider/EncodableResponse.cs4
-rw-r--r--src/DotNetOpenId/Provider/Encoder.cs88
-rw-r--r--src/DotNetOpenId/Provider/OpenIdProvider.cs11
-rw-r--r--src/DotNetOpenId/Provider/SigningMessageEncoder.cs41
-rw-r--r--src/DotNetOpenId/RelyingParty/AuthenticationRequest.cs37
-rw-r--r--src/DotNetOpenId/RelyingParty/AuthenticationResponse.cs7
-rw-r--r--src/DotNetOpenId/RelyingParty/DirectRequest.cs9
-rw-r--r--src/DotNetOpenId/RelyingParty/IAuthenticationRequest.cs17
-rw-r--r--src/DotNetOpenId/RelyingParty/IndirectMessageRequest.cs25
-rw-r--r--src/DotNetOpenId/RelyingParty/OpenIdRelyingParty.cs25
-rw-r--r--src/DotNetOpenId/RelyingParty/OpenIdTextBox.cs2
-rw-r--r--src/DotNetOpenId/Response.cs (renamed from src/DotNetOpenId/Provider/Response.cs)20
-rw-r--r--src/DotNetOpenId/Util.cs3
29 files changed, 328 insertions, 192 deletions
diff --git a/samples/RelyingPartyCustomStore/login.aspx.cs b/samples/RelyingPartyCustomStore/login.aspx.cs
index 23cb7f7..f7b1088 100644
--- a/samples/RelyingPartyCustomStore/login.aspx.cs
+++ b/samples/RelyingPartyCustomStore/login.aspx.cs
@@ -8,7 +8,8 @@ public partial class login : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e) {
openIdBox.Focus();
- OpenIdRelyingParty rp = new OpenIdRelyingParty(CustomStore.Instance, Request.Url);
+ OpenIdRelyingParty rp = new OpenIdRelyingParty(CustomStore.Instance, Request.Url,
+ Request.HttpMethod == "GET" ? Request.QueryString : Request.Form);
if (rp.Response != null) {
switch (rp.Response.Status) {
case AuthenticationStatus.Authenticated:
@@ -25,7 +26,8 @@ public partial class login : System.Web.UI.Page {
}
protected void loginButton_Click(object sender, EventArgs e) {
- OpenIdRelyingParty rp = new OpenIdRelyingParty(CustomStore.Instance, Request.Url);
+ OpenIdRelyingParty rp = new OpenIdRelyingParty(CustomStore.Instance, Request.Url,
+ Request.HttpMethod == "GET" ? Request.QueryString : Request.Form);
rp.CreateRequest(openIdBox.Text).RedirectToProvider();
}
}
diff --git a/src/DotNetOpenId.Test/EndToEndTesting.cs b/src/DotNetOpenId.Test/EndToEndTesting.cs
index 8c2df0f..de1ed2a 100644
--- a/src/DotNetOpenId.Test/EndToEndTesting.cs
+++ b/src/DotNetOpenId.Test/EndToEndTesting.cs
@@ -36,7 +36,7 @@ namespace DotNetOpenId.Test {
Uri redirectToProviderUrl;
var returnTo = TestSupport.GetFullUrl(TestSupport.ConsumerPage);
var realm = new Realm(TestSupport.GetFullUrl(TestSupport.ConsumerPage).AbsoluteUri);
- var consumer = new OpenIdRelyingParty(store, null);
+ var consumer = new OpenIdRelyingParty(store, null, null);
Assert.IsNull(consumer.Response);
var request = consumer.CreateRequest(identityUrl, realm, returnTo);
Protocol protocol = Protocol.Lookup(request.ProviderVersion);
@@ -49,11 +49,11 @@ namespace DotNetOpenId.Test {
request.Mode = requestMode;
// Verify the redirect URL
- Assert.IsNotNull(request.RedirectToProviderUrl);
- var consumerToProviderQuery = HttpUtility.ParseQueryString(request.RedirectToProviderUrl.Query);
+ Assert.IsNotNull(request.RedirectingResponse);
+ var consumerToProviderQuery = HttpUtility.ParseQueryString(request.RedirectingResponse.ExtractUrl().Query);
Assert.IsTrue(consumerToProviderQuery[protocol.openid.return_to].StartsWith(returnTo.AbsoluteUri, StringComparison.Ordinal));
Assert.AreEqual(realm.ToString(), consumerToProviderQuery[protocol.openid.Realm]);
- redirectToProviderUrl = request.RedirectToProviderUrl;
+ redirectToProviderUrl = request.RedirectingResponse.ExtractUrl();
HttpWebRequest providerRequest = (HttpWebRequest)WebRequest.Create(redirectToProviderUrl);
providerRequest.AllowAutoRedirect = false;
@@ -72,7 +72,7 @@ namespace DotNetOpenId.Test {
}
throw;
}
- consumer = new OpenIdRelyingParty(store, redirectUrl);
+ consumer = new OpenIdRelyingParty(store, redirectUrl, HttpUtility.ParseQueryString(redirectUrl.Query));
Assert.AreEqual(expectedResult, consumer.Response.Status);
Assert.AreEqual(identityUrl, consumer.Response.ClaimedIdentifier);
@@ -83,7 +83,7 @@ namespace DotNetOpenId.Test {
// the consumer, and tries the same query to the consumer in an
// attempt to spoof the identity of the authenticating user.
try {
- var replayAttackConsumer = new OpenIdRelyingParty(store, redirectUrl);
+ var replayAttackConsumer = new OpenIdRelyingParty(store, redirectUrl, HttpUtility.ParseQueryString(redirectUrl.Query));
Assert.AreNotEqual(AuthenticationStatus.Authenticated, replayAttackConsumer.Response.Status, "Replay attack");
} catch (OpenIdException) { // nonce already used
// another way to pass
diff --git a/src/DotNetOpenId.Test/Extensions/AttributeExchangeTests.cs b/src/DotNetOpenId.Test/Extensions/AttributeExchangeTests.cs
index 5f4fd26..62efbc2 100644
--- a/src/DotNetOpenId.Test/Extensions/AttributeExchangeTests.cs
+++ b/src/DotNetOpenId.Test/Extensions/AttributeExchangeTests.cs
@@ -95,7 +95,7 @@ namespace DotNetOpenId.Test.Extensions {
var identityUrl = TestSupport.GetIdentityUrl(TestSupport.Scenarios.ExtensionFullCooperation, Version);
var returnTo = TestSupport.GetFullUrl(TestSupport.ConsumerPage);
var realm = new Realm(TestSupport.GetFullUrl(TestSupport.ConsumerPage).AbsoluteUri);
- var consumer = new OpenIdRelyingParty(AppStore, null);
+ var consumer = new OpenIdRelyingParty(AppStore, null, null);
var request = consumer.CreateRequest(identityUrl, realm, returnTo);
request.AddExtension(new FetchRequest());
request.AddExtension(new StoreRequest());
diff --git a/src/DotNetOpenId.Test/Extensions/ExtensionTestBase.cs b/src/DotNetOpenId.Test/Extensions/ExtensionTestBase.cs
index ea3379c..825ef58 100644
--- a/src/DotNetOpenId.Test/Extensions/ExtensionTestBase.cs
+++ b/src/DotNetOpenId.Test/Extensions/ExtensionTestBase.cs
@@ -8,6 +8,7 @@ using System.Net;
using DotNetOpenId.Extensions;
using System.IO;
using System.Diagnostics;
+using System.Web;
namespace DotNetOpenId.Test.Extensions {
public class ExtensionTestBase {
@@ -24,12 +25,12 @@ namespace DotNetOpenId.Test.Extensions {
Debug.Assert(identityUrl != null);
var returnTo = TestSupport.GetFullUrl(TestSupport.ConsumerPage);
var realm = new Realm(TestSupport.GetFullUrl(TestSupport.ConsumerPage).AbsoluteUri);
- var consumer = new OpenIdRelyingParty(AppStore, null);
+ var consumer = new OpenIdRelyingParty(AppStore, null, null);
var request = consumer.CreateRequest(identityUrl, realm, returnTo);
if (extension != null)
request.AddExtension(extension);
- HttpWebRequest providerRequest = (HttpWebRequest)WebRequest.Create(request.RedirectToProviderUrl);
+ HttpWebRequest providerRequest = (HttpWebRequest)WebRequest.Create(request.RedirectingResponse.ExtractUrl());
providerRequest.AllowAutoRedirect = false;
Uri redirectUrl;
try {
@@ -46,7 +47,7 @@ namespace DotNetOpenId.Test.Extensions {
}
throw;
}
- consumer = new OpenIdRelyingParty(AppStore, redirectUrl);
+ consumer = new OpenIdRelyingParty(AppStore, redirectUrl, HttpUtility.ParseQueryString(redirectUrl.Query));
Assert.AreEqual(AuthenticationStatus.Authenticated, consumer.Response.Status);
Assert.AreEqual(identityUrl, consumer.Response.ClaimedIdentifier);
return consumer.Response.GetExtension<T>();
diff --git a/src/DotNetOpenId.Test/Hosting/AspNetHost.cs b/src/DotNetOpenId.Test/Hosting/AspNetHost.cs
index 4340404..844c040 100644
--- a/src/DotNetOpenId.Test/Hosting/AspNetHost.cs
+++ b/src/DotNetOpenId.Test/Hosting/AspNetHost.cs
@@ -21,7 +21,7 @@ namespace DotNetOpenId.Test.Hosting {
httpHost = HttpHost.CreateHost(this);
if (!UntrustedWebRequest.WhitelistHosts.Contains("localhost"))
UntrustedWebRequest.WhitelistHosts.Add("localhost");
- DotNetOpenId.Provider.SigningEncoder.Signing += (s, e) => {
+ DotNetOpenId.Provider.SigningMessageEncoder.Signing += (s, e) => {
if (MessageInterceptor != null) MessageInterceptor.OnSigningMessage(e.Message);
};
}
diff --git a/src/DotNetOpenId.Test/Provider/IAuthenticationRequestTest.cs b/src/DotNetOpenId.Test/Provider/IAuthenticationRequestTest.cs
index 42a572f..3ea7d9c 100644
--- a/src/DotNetOpenId.Test/Provider/IAuthenticationRequestTest.cs
+++ b/src/DotNetOpenId.Test/Provider/IAuthenticationRequestTest.cs
@@ -20,9 +20,9 @@ namespace DotNetOpenId.Test.Provider {
Uri returnTo;
Realm realm;
getUnverifiableRP(out returnTo, out realm);
- var consumer = new OpenIdRelyingParty(new ApplicationMemoryStore(), null);
+ var consumer = new OpenIdRelyingParty(new ApplicationMemoryStore(), null, null);
var request = consumer.CreateRequest(TestSupport.GetIdentityUrl(TestSupport.Scenarios.AutoApproval, ProtocolVersion.V20), realm, returnTo);
- WebRequest.Create(request.RedirectToProviderUrl).GetResponse(); // the OP should return 500, causing exception here.
+ WebRequest.Create(request.RedirectingResponse.ExtractUrl()).GetResponse(); // the OP should return 500, causing exception here.
}
static void getUnverifiableRP(out Uri returnTo, out Realm realm) {
diff --git a/src/DotNetOpenId.Test/RelyingParty/AuthenticationResponseTests.cs b/src/DotNetOpenId.Test/RelyingParty/AuthenticationResponseTests.cs
index a3e2e28..7dcb792 100644
--- a/src/DotNetOpenId.Test/RelyingParty/AuthenticationResponseTests.cs
+++ b/src/DotNetOpenId.Test/RelyingParty/AuthenticationResponseTests.cs
@@ -35,10 +35,10 @@ namespace DotNetOpenId.Test.RelyingParty {
Uri getPositiveAssertion(ProtocolVersion version) {
try {
- OpenIdRelyingParty rp = new OpenIdRelyingParty(store, null);
+ OpenIdRelyingParty rp = new OpenIdRelyingParty(store, null, null);
Identifier id = TestSupport.GetIdentityUrl(TestSupport.Scenarios.AutoApproval, version);
var request = rp.CreateRequest(id, realm, returnTo);
- HttpWebRequest providerRequest = (HttpWebRequest)WebRequest.Create(request.RedirectToProviderUrl);
+ HttpWebRequest providerRequest = (HttpWebRequest)WebRequest.Create(request.RedirectingResponse.ExtractUrl());
providerRequest.AllowAutoRedirect = false;
Uri redirectUrl;
try {
@@ -106,7 +106,7 @@ namespace DotNetOpenId.Test.RelyingParty {
// which should cause a failure because the return_to argument
// says that parameter is supposed to be there.
removeQueryParameter(ref assertion, returnToRemovableParameter);
- var response = new OpenIdRelyingParty(store, assertion).Response;
+ var response = new OpenIdRelyingParty(store, assertion, HttpUtility.ParseQueryString(assertion.Query)).Response;
Assert.AreEqual(AuthenticationStatus.Failed, response.Status);
Assert.IsNotNull(response.Exception);
}
@@ -140,7 +140,7 @@ namespace DotNetOpenId.Test.RelyingParty {
resign(ref assertion); // resign changed URL to simulate a contrived OP for breaking into RPs.
// (triggers exception) "... you're in trouble up to your ears."
- var response = new OpenIdRelyingParty(store, assertion).Response;
+ var response = new OpenIdRelyingParty(store, assertion, HttpUtility.ParseQueryString(assertion.Query)).Response;
Assert.AreEqual(AuthenticationStatus.Failed, response.Status);
Assert.IsNotNull(response.Exception);
}
diff --git a/src/DotNetOpenId.Test/RelyingParty/OpenIdRelyingPartyTest.cs b/src/DotNetOpenId.Test/RelyingParty/OpenIdRelyingPartyTest.cs
index 63d6f8e..ddef8a3 100644
--- a/src/DotNetOpenId.Test/RelyingParty/OpenIdRelyingPartyTest.cs
+++ b/src/DotNetOpenId.Test/RelyingParty/OpenIdRelyingPartyTest.cs
@@ -3,6 +3,7 @@ using DotNetOpenId.RelyingParty;
using NUnit.Framework;
using ProviderMemoryStore = DotNetOpenId.AssociationMemoryStore<DotNetOpenId.AssociationRelyingPartyType>;
using System.Web;
+using System.Collections.Specialized;
namespace DotNetOpenId.Test.RelyingParty {
[TestFixture]
@@ -28,25 +29,25 @@ namespace DotNetOpenId.Test.RelyingParty {
[Test]
public void CtorWithNullRequestUri() {
- new OpenIdRelyingParty(store, null);
+ new OpenIdRelyingParty(store, null, null);
}
[Test]
public void CtorWithNullStore() {
- var consumer = new OpenIdRelyingParty(null, simpleNonOpenIdRequest);
+ var consumer = new OpenIdRelyingParty(null, simpleNonOpenIdRequest, new NameValueCollection());
}
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void CreateRequestWithoutContext1() {
- var consumer = new OpenIdRelyingParty(store, simpleNonOpenIdRequest);
+ var consumer = new OpenIdRelyingParty(store, simpleNonOpenIdRequest, new NameValueCollection());
consumer.CreateRequest(simpleOpenId);
}
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void CreateRequestWithoutContext2() {
- var consumer = new OpenIdRelyingParty(store, simpleNonOpenIdRequest);
+ var consumer = new OpenIdRelyingParty(store, simpleNonOpenIdRequest, new NameValueCollection());
consumer.CreateRequest(simpleOpenId, realm);
}
@@ -54,7 +55,7 @@ namespace DotNetOpenId.Test.RelyingParty {
public void AssociationCreationWithStore() {
var providerStore = new ProviderMemoryStore();
- OpenIdRelyingParty rp = new OpenIdRelyingParty(new ApplicationMemoryStore(), null);
+ OpenIdRelyingParty rp = new OpenIdRelyingParty(new ApplicationMemoryStore(), null, null);
var idUrl = TestSupport.GetIdentityUrl(TestSupport.Scenarios.AutoApproval, ProtocolVersion.V20);
DotNetOpenId.RelyingParty.IAuthenticationRequest req;
@@ -72,7 +73,7 @@ namespace DotNetOpenId.Test.RelyingParty {
public void NoAssociationRequestWithoutStore() {
var providerStore = new ProviderMemoryStore();
- OpenIdRelyingParty rp = new OpenIdRelyingParty(null, null);
+ OpenIdRelyingParty rp = new OpenIdRelyingParty(null, null, null);
var idUrl = TestSupport.GetIdentityUrl(TestSupport.Scenarios.AutoApproval, ProtocolVersion.V20);
DotNetOpenId.RelyingParty.IAuthenticationRequest req;
@@ -113,10 +114,10 @@ namespace DotNetOpenId.Test.RelyingParty {
private static void testExplicitPortOnRealmAndReturnTo(Uri returnTo, Realm realm) {
var identityUrl = TestSupport.GetIdentityUrl(TestSupport.Scenarios.AutoApproval, ProtocolVersion.V20);
- var consumer = new OpenIdRelyingParty(null, null);
+ var consumer = new OpenIdRelyingParty(null, null, null);
var request = consumer.CreateRequest(identityUrl, realm, returnTo);
Protocol protocol = Protocol.Lookup(request.ProviderVersion);
- var nvc = HttpUtility.ParseQueryString(request.RedirectToProviderUrl.Query);
+ var nvc = HttpUtility.ParseQueryString(request.RedirectingResponse.ExtractUrl().Query);
string realmString = nvc[protocol.openid.Realm];
string returnToString = nvc[protocol.openid.return_to];
bool realmPortExplicitlyGiven = realmString.Contains(":80");
@@ -133,11 +134,11 @@ namespace DotNetOpenId.Test.RelyingParty {
public void ReturnToUrlEncodingTest() {
Uri origin = TestSupport.GetFullUrl(TestSupport.ConsumerPage);
var identityUrl = TestSupport.GetIdentityUrl(TestSupport.Scenarios.AutoApproval, ProtocolVersion.V20);
- var consumer = new OpenIdRelyingParty(null, null);
+ var consumer = new OpenIdRelyingParty(null, null, null);
var request = consumer.CreateRequest(identityUrl, origin, origin);
Protocol protocol = Protocol.Lookup(request.ProviderVersion);
request.AddCallbackArguments("a+b", "c+d");
- var requestArgs = HttpUtility.ParseQueryString(request.RedirectToProviderUrl.Query);
+ var requestArgs = HttpUtility.ParseQueryString(request.RedirectingResponse.ExtractUrl().Query);
var returnToArgs = HttpUtility.ParseQueryString(requestArgs[protocol.openid.return_to]);
Assert.AreEqual("c+d", returnToArgs["a+b"]);
}
diff --git a/src/DotNetOpenId.Test/TestSupport.cs b/src/DotNetOpenId.Test/TestSupport.cs
index d45aad3..be17494 100644
--- a/src/DotNetOpenId.Test/TestSupport.cs
+++ b/src/DotNetOpenId.Test/TestSupport.cs
@@ -93,3 +93,16 @@ public class TestSupport {
nvc[protocol.openid.sig] = Convert.ToBase64String(assoc.Sign(subsetDictionary, signed));
}
}
+
+static class TestExtensions {
+ /// <summary>
+ /// Gets a URL that can be requested to send an indirect message.
+ /// </summary>
+ public static Uri ExtractUrl(this IResponse message) {
+ DotNetOpenId.Response response = (DotNetOpenId.Response)message;
+ IEncodable encodable = response.EncodableMessage;
+ UriBuilder builder = new UriBuilder(encodable.RedirectUrl);
+ UriUtil.AppendQueryArgs(builder, encodable.EncodedFields);
+ return builder.Uri;
+ }
+}
diff --git a/src/DotNetOpenId.Test/UtilTest.cs b/src/DotNetOpenId.Test/UtilTest.cs
index 2f995c5..f6a3dbe 100644
--- a/src/DotNetOpenId.Test/UtilTest.cs
+++ b/src/DotNetOpenId.Test/UtilTest.cs
@@ -24,5 +24,15 @@ namespace DotNetOpenId.Test {
IDictionary<string, string> dict = Util.NameValueCollectionToDictionary(nvc);
Assert.IsTrue(dict["a"] == "b");
}
+
+ [Test]
+ public void NameValueCollectionToDictionaryNull() {
+ Assert.IsNull(Util.NameValueCollectionToDictionary(null));
+ }
+
+ [Test]
+ public void DictionaryToNameValueCollectionNull() {
+ Assert.IsNull(Util.DictionaryToNameValueCollection(null));
+ }
}
}
diff --git a/src/DotNetOpenId.sln b/src/DotNetOpenId.sln
index c36d9d9..8cd73a7 100644
--- a/src/DotNetOpenId.sln
+++ b/src/DotNetOpenId.sln
@@ -3,10 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{14D00F8C-C7CB-49B3-A668-7D0C5A4A6D9F}"
ProjectSection(SolutionItems) = preProject
- ..\doc\openid-attribute-exchange-1_0.html = ..\doc\openid-attribute-exchange-1_0.html
- ..\doc\openid-authentication-1_1.html = ..\doc\openid-authentication-1_1.html
- ..\doc\openid-authentication-2_0.html = ..\doc\openid-authentication-2_0.html
- ..\doc\openid-simple-registration-extension-1_0.html = ..\doc\openid-simple-registration-extension-1_0.html
..\doc\README.html = ..\doc\README.html
..\doc\WebFarms.htm = ..\doc\WebFarms.htm
EndProjectSection
diff --git a/src/DotNetOpenId/DotNetOpenId.csproj b/src/DotNetOpenId/DotNetOpenId.csproj
index b4fea45..a908853 100644
--- a/src/DotNetOpenId/DotNetOpenId.csproj
+++ b/src/DotNetOpenId/DotNetOpenId.csproj
@@ -55,6 +55,8 @@
<Compile Include="Association.cs" />
<Compile Include="AssociationMemoryStore.cs" />
<Compile Include="Associations.cs" />
+ <Compile Include="Provider\SigningMessageEncoder.cs" />
+ <Compile Include="RelyingParty\IndirectMessageRequest.cs" />
<Compile Include="ExtensionArgumentsManager.cs" />
<Compile Include="Extensions\AliasManager.cs" />
<Compile Include="Extensions\AttributeExchange\Constants.cs" />
@@ -74,6 +76,7 @@
<Compile Include="Extensions\IExtension.cs" />
<Compile Include="HmacSha256Association.cs" />
<Compile Include="Identifier.cs" />
+ <Compile Include="IResponse.cs" />
<Compile Include="Protocol.cs" />
<Compile Include="Provider\AssertionMessage.cs" />
<Compile Include="Provider\RelyingPartyReceivingEndpoint.cs" />
@@ -105,16 +108,15 @@
<Compile Include="OpenIdException.cs" />
<Compile Include="ProtocolMessages.cs" />
<Compile Include="Provider\EncodableResponse.cs" />
- <Compile Include="Provider\Encoder.cs" />
+ <Compile Include="MessageEncoder.cs" />
<Compile Include="Provider\FaultyRequest.cs" />
<Compile Include="Provider\IAuthenticationRequest.cs" />
<Compile Include="Provider\IdentityEndpoint.cs" />
<Compile Include="Provider\IRequest.cs" />
- <Compile Include="Provider\IResponse.cs" />
<Compile Include="Provider\OpenIdProvider.cs" />
<Compile Include="Provider\ProviderSession.cs" />
<Compile Include="Provider\Request.cs" />
- <Compile Include="Provider\Response.cs" />
+ <Compile Include="Response.cs" />
<Compile Include="Provider\ProviderEndpoint.cs" />
<Compile Include="Extensions\SimpleRegistration\Gender.cs" />
<Compile Include="Provider\AssociatedRequest.cs" />
@@ -139,7 +141,7 @@
<Compile Include="Provider\AssociateRequest.cs" />
<Compile Include="Provider\CheckAuthRequest.cs" />
<Compile Include="Provider\CheckIdRequest.cs" />
- <Compile Include="Provider\IEncodable.cs" />
+ <Compile Include="IEncodable.cs" />
<Compile Include="Provider\Signatory.cs" />
<Compile Include="XrdsPublisher.cs" />
<Compile Include="Strings.Designer.cs">
diff --git a/src/DotNetOpenId/Provider/IEncodable.cs b/src/DotNetOpenId/IEncodable.cs
index 3a7a60d..ef5ab60 100644
--- a/src/DotNetOpenId/Provider/IEncodable.cs
+++ b/src/DotNetOpenId/IEncodable.cs
@@ -2,21 +2,21 @@ using System;
using System.Collections.Generic;
using System.Text;
-namespace DotNetOpenId.Provider {
+namespace DotNetOpenId {
internal enum EncodingType {
None,
/// <summary>
- /// Response data to be sent to the consumer web site by telling the
- /// browser to redirect back to the consumer web site with a querystring
- /// that contains our data.
+ /// Data to be sent to the OP or RP site by telling the user agent to
+ /// redirect GET or form POST to a special URL with a payload of arguments.
/// </summary>
- RedirectBrowserUrl,
+ IndirectMessage,
/// <summary>
- /// Response data to be sent directly to the consumer site,
- /// in response to a direct request initiated by the consumer site
- /// (not the client browser).
+ /// Provider response data to be sent directly to the Relying Party site,
+ /// in response to a direct request initiated by the RP
+ /// (not indirect via the user agent).
+ /// Key-Value Form encoding will be used.
/// </summary>
- ResponseBody
+ DirectResponse
}
/// <remarks>
diff --git a/src/DotNetOpenId/Provider/IResponse.cs b/src/DotNetOpenId/IResponse.cs
index 29a70ce..e023383 100644
--- a/src/DotNetOpenId/Provider/IResponse.cs
+++ b/src/DotNetOpenId/IResponse.cs
@@ -4,7 +4,7 @@ using System.Text;
using System.Net;
using System.Collections.Specialized;
-namespace DotNetOpenId.Provider {
+namespace DotNetOpenId {
/// <summary>
/// Represents a Provider's response to an OpenId authentication request.
/// </summary>
@@ -26,7 +26,7 @@ namespace DotNetOpenId.Provider {
/// Sends the response to the browser.
/// </summary>
/// <remarks>
- /// This requires an ASP.NET hosted web site.
+ /// This requires an ASP.NET HttpContext.
/// </remarks>
void Send();
}
diff --git a/src/DotNetOpenId/MessageEncoder.cs b/src/DotNetOpenId/MessageEncoder.cs
new file mode 100644
index 0000000..3d5f840
--- /dev/null
+++ b/src/DotNetOpenId/MessageEncoder.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Collections.Specialized;
+using System.Text;
+using System.Net;
+using System.Diagnostics;
+using DotNetOpenId.Provider;
+using System.IO;
+using System.Web;
+
+namespace DotNetOpenId {
+ /// <summary>
+ /// Encodes <see cref="IEncodable"/> messages into <see cref="Response"/> instances
+ /// that can be interpreted by the host web site.
+ /// </summary>
+ internal class MessageEncoder {
+ /// <summary>
+ /// The maximum allowable size for a 301 Redirect response before we send
+ /// a 200 OK response with a scripted form POST with the parameters instead
+ /// in order to ensure successfully sending a large payload to another server
+ /// that might have a maximum allowable size restriction on its GET request.
+ /// </summary>
+ internal static int GetToPostThreshold = 2 * 1024; // 2KB, recommended by OpenID group
+ // We are intentionally using " instead of the html single quote ' below because
+ // the HtmlEncode'd values that we inject will only escape the double quote, so
+ // only the double-quote used around these values is safe.
+ const string FormPostFormat = @"
+<html>
+<body onload=""var btn = document.getElementById('submit_button'); btn.disabled = true; btn.value = 'Login in progress'; document.getElementById('openid_message').submit()"">
+<form id=""openid_message"" action=""{0}"" method=""post"" accept-charset=""UTF-8"" enctype=""application/x-www-form-urlencoded"" onSubmit=""var btn = document.getElementById('submit_button'); btn.disabled = true; btn.value = 'Login in progress'; return true;"">
+{1}
+ <input id=""submit_button"" type=""submit"" value=""Continue"" />
+</form>
+</body>
+</html>
+";
+ /// <summary>
+ /// Encodes messages into <see cref="Response"/> instances.
+ /// </summary>
+ public virtual Response Encode(IEncodable message) {
+ EncodingType encode_as = message.EncodingType;
+ Response wr;
+
+ WebHeaderCollection headers = new WebHeaderCollection();
+ switch (encode_as) {
+ case EncodingType.DirectResponse:
+ HttpStatusCode code = (message is Exception) ?
+ HttpStatusCode.BadRequest : HttpStatusCode.OK;
+ wr = new Response(code, headers, ProtocolMessages.KeyValueForm.GetBytes(message.EncodedFields), message);
+ break;
+ case EncodingType.IndirectMessage:
+ // TODO: either redirect or do a form POST depending on payload size.
+ Debug.Assert(message.RedirectUrl != null);
+ if (getSizeOfPayload(message) <= GetToPostThreshold)
+ wr = Create301RedirectResponse(message);
+ else
+ wr = CreateFormPostResponse(message);
+ break;
+ default:
+ if (TraceUtil.Switch.TraceError) {
+ Trace.TraceError("Cannot encode response: {0}", message);
+ }
+ wr = new Response(HttpStatusCode.BadRequest, headers, new byte[0], message);
+ break;
+ }
+ return wr;
+ }
+
+ static int getSizeOfPayload(IEncodable message) {
+ Debug.Assert(message != null);
+ int size = 0;
+ foreach (var field in message.EncodedFields) {
+ size += field.Key.Length;
+ size += field.Value.Length;
+ }
+ return size;
+ }
+ protected virtual Response Create301RedirectResponse(IEncodable message) {
+ WebHeaderCollection headers = new WebHeaderCollection();
+ UriBuilder builder = new UriBuilder(message.RedirectUrl);
+ UriUtil.AppendQueryArgs(builder, message.EncodedFields);
+ headers.Add(HttpResponseHeader.Location, builder.Uri.AbsoluteUri);
+ return new Response(HttpStatusCode.Redirect, headers, new byte[0], message);
+ }
+ protected virtual Response CreateFormPostResponse(IEncodable message) {
+ WebHeaderCollection headers = new WebHeaderCollection();
+ MemoryStream body = new MemoryStream();
+ StreamWriter bodyWriter = new StreamWriter(body);
+ StringBuilder hiddenFields = new StringBuilder();
+ foreach (var field in message.EncodedFields) {
+ hiddenFields.AppendFormat("\t<input type=\"hidden\" name=\"{0}\" value=\"{1}\" />\r\n",
+ HttpUtility.HtmlEncode(field.Key), HttpUtility.HtmlEncode(field.Value));
+ }
+ bodyWriter.WriteLine(FormPostFormat,
+ HttpUtility.HtmlEncode(message.RedirectUrl.AbsoluteUri), hiddenFields);
+ bodyWriter.Flush();
+ return new Response(HttpStatusCode.OK, headers, body.ToArray(), message);
+ }
+ }
+
+ internal class EncodeEventArgs : EventArgs {
+ public EncodeEventArgs(IEncodable encodable) {
+ Message = encodable;
+ }
+ public IEncodable Message { get; private set; }
+ }
+}
diff --git a/src/DotNetOpenId/OpenIdException.cs b/src/DotNetOpenId/OpenIdException.cs
index b2647bb..b523cff 100644
--- a/src/DotNetOpenId/OpenIdException.cs
+++ b/src/DotNetOpenId/OpenIdException.cs
@@ -83,14 +83,14 @@ namespace DotNetOpenId {
get {
Debug.Assert(Query != null, "An OpenId exception should always be provided with the query if it is to be encoded for transmittal to the RP.");
if (HasReturnTo)
- return EncodingType.RedirectBrowserUrl;
+ return EncodingType.IndirectMessage;
if (Query != null) {
string mode = Util.GetOptionalArg(Query, Protocol.openid.mode);
if (mode != null)
if (mode != Protocol.Args.Mode.checkid_setup &&
mode != Protocol.Args.Mode.checkid_immediate)
- return EncodingType.ResponseBody;
+ return EncodingType.DirectResponse;
}
// Notes from the original port
diff --git a/src/DotNetOpenId/Provider/EncodableResponse.cs b/src/DotNetOpenId/Provider/EncodableResponse.cs
index c7ebb4e..47bc1cd 100644
--- a/src/DotNetOpenId/Provider/EncodableResponse.cs
+++ b/src/DotNetOpenId/Provider/EncodableResponse.cs
@@ -45,7 +45,7 @@ namespace DotNetOpenId.Provider {
#region IEncodable Members
public EncodingType EncodingType {
- get { return RedirectUrl != null ? EncodingType.RedirectBrowserUrl : EncodingType.ResponseBody; }
+ get { return RedirectUrl != null ? EncodingType.IndirectMessage : EncodingType.DirectResponse; }
}
public IDictionary<string, string> EncodedFields {
@@ -53,7 +53,7 @@ namespace DotNetOpenId.Provider {
var nvc = new Dictionary<string, string>();
foreach (var pair in Fields) {
- if (EncodingType == EncodingType.RedirectBrowserUrl) {
+ if (EncodingType == EncodingType.IndirectMessage) {
nvc.Add(Protocol.openid.Prefix + pair.Key, pair.Value);
} else {
nvc.Add(pair.Key, pair.Value);
diff --git a/src/DotNetOpenId/Provider/Encoder.cs b/src/DotNetOpenId/Provider/Encoder.cs
deleted file mode 100644
index 57a5435..0000000
--- a/src/DotNetOpenId/Provider/Encoder.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-using System;
-using System.Collections.Specialized;
-using System.Text;
-using System.Net;
-using System.Diagnostics;
-
-namespace DotNetOpenId.Provider {
- /// <summary>
- /// Encodes responses in to <see cref="WebResponse"/>.
- /// </summary>
- internal class Encoder {
- /// <summary>
- /// Encodes responses in to WebResponses.
- /// </summary>
- public virtual Response Encode(IEncodable response) {
- EncodingType encode_as = response.EncodingType;
- Response wr;
-
- switch (encode_as) {
- case EncodingType.ResponseBody:
- HttpStatusCode code = (response is Exception) ?
- HttpStatusCode.BadRequest : HttpStatusCode.OK;
- wr = new Response(code, null, ProtocolMessages.KeyValueForm.GetBytes(response.EncodedFields));
- break;
- case EncodingType.RedirectBrowserUrl:
- Debug.Assert(response.RedirectUrl != null);
- WebHeaderCollection headers = new WebHeaderCollection();
-
- UriBuilder builder = new UriBuilder(response.RedirectUrl);
- UriUtil.AppendQueryArgs(builder, response.EncodedFields);
- headers.Add(HttpResponseHeader.Location, builder.Uri.AbsoluteUri);
-
- wr = new Response(HttpStatusCode.Redirect, headers, new byte[0]);
- break;
- default:
- if (TraceUtil.Switch.TraceError) {
- Trace.TraceError("Cannot encode response: {0}", response);
- }
- wr = new Response(HttpStatusCode.BadRequest, null, new byte[0]);
- break;
- }
- return wr;
- }
- }
-
- /// <summary>
- /// Encodes responses in to <see cref="WebResponse"/>, signing them when required.
- /// </summary>
- internal class SigningEncoder : Encoder {
- Signatory signatory;
-
- public SigningEncoder(Signatory signatory) {
- if (signatory == null)
- throw new ArgumentNullException("signatory", "Must have a store to sign this request");
-
- this.signatory = signatory;
- }
-
- public override Response Encode(IEncodable encodable) {
- OnSigning(encodable);
- var response = encodable as EncodableResponse;
- if (response != null) {
- if (response.NeedsSigning) {
- Debug.Assert(!response.Fields.ContainsKey(encodable.Protocol.openidnp.sig));
- signatory.Sign(response);
- }
- }
- return base.Encode(encodable);
- }
-
- /// <summary>
- /// Used for testing. Allows interception and modification of messages
- /// that are about to be returned to the RP.
- /// </summary>
- public static event EventHandler<EncodeEventArgs> Signing;
- protected virtual void OnSigning(IEncodable encodable) {
- if (Signing != null)
- Signing(this, new EncodeEventArgs(encodable));
- }
- }
-
- internal class EncodeEventArgs : EventArgs {
- public EncodeEventArgs(IEncodable encodable) {
- Message = encodable;
- }
- public IEncodable Message { get; private set;}
- }
-}
diff --git a/src/DotNetOpenId/Provider/OpenIdProvider.cs b/src/DotNetOpenId/Provider/OpenIdProvider.cs
index f227f0c..e0b2afd 100644
--- a/src/DotNetOpenId/Provider/OpenIdProvider.cs
+++ b/src/DotNetOpenId/Provider/OpenIdProvider.cs
@@ -15,7 +15,7 @@ namespace DotNetOpenId.Provider {
[DebuggerDisplay("Endpoint: {Endpoint}, OpenId Request: {Query.ContainsKey(\"openid.mode\")}")]
public class OpenIdProvider {
internal Signatory Signatory { get; private set; }
- internal Encoder Encoder;
+ internal MessageEncoder Encoder;
/// <summary>
/// The incoming request's Url.
/// </summary>
@@ -55,8 +55,11 @@ namespace DotNetOpenId.Provider {
/// The Internet-facing URL that responds to OpenID requests.
/// </param>
/// <param name="requestUrl">The incoming request URL.</param>
- /// <param name="query">The name/value pairs that came in on the
- /// QueryString of a GET request or in the entity of a POST request.</param>
+ /// <param name="query">
+ /// The name/value pairs that came in on the
+ /// QueryString of a GET request or in the entity of a POST request.
+ /// For example: (Request.HttpMethod == "GET" ? Request.QueryString : Request.Form).
+ /// </param>
public OpenIdProvider(IProviderAssociationStore store, Uri providerEndpoint, Uri requestUrl, NameValueCollection query)
: this(store, providerEndpoint, requestUrl, Util.NameValueCollectionToDictionary(query)) { }
OpenIdProvider(IProviderAssociationStore store, Uri providerEndpoint, Uri requestUrl, IDictionary<string, string> query) {
@@ -68,7 +71,7 @@ namespace DotNetOpenId.Provider {
RequestUrl = requestUrl;
Query = query;
Signatory = new Signatory(store);
- Encoder = new SigningEncoder(Signatory);
+ Encoder = new SigningMessageEncoder(Signatory);
store.ClearExpiredAssociations(); // every so often we should do this.
}
diff --git a/src/DotNetOpenId/Provider/SigningMessageEncoder.cs b/src/DotNetOpenId/Provider/SigningMessageEncoder.cs
new file mode 100644
index 0000000..ea5a522
--- /dev/null
+++ b/src/DotNetOpenId/Provider/SigningMessageEncoder.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Diagnostics;
+
+namespace DotNetOpenId.Provider {
+ /// <summary>
+ /// Encodes responses in to <see cref="Response"/>, signing them when required.
+ /// </summary>
+ internal class SigningMessageEncoder : MessageEncoder {
+ Signatory signatory;
+
+ public SigningMessageEncoder(Signatory signatory) {
+ if (signatory == null)
+ throw new ArgumentNullException("signatory", "Must have a store to sign this request");
+
+ this.signatory = signatory;
+ }
+
+ public override Response Encode(IEncodable encodable) {
+ OnSigning(encodable);
+ var response = encodable as EncodableResponse;
+ if (response != null) {
+ if (response.NeedsSigning) {
+ Debug.Assert(!response.Fields.ContainsKey(encodable.Protocol.openidnp.sig));
+ signatory.Sign(response);
+ }
+ }
+ return base.Encode(encodable);
+ }
+
+ /// <summary>
+ /// Used for testing. Allows interception and modification of messages
+ /// that are about to be returned to the RP.
+ /// </summary>
+ public static event EventHandler<EncodeEventArgs> Signing;
+ protected virtual void OnSigning(IEncodable encodable) {
+ if (Signing != null)
+ Signing(this, new EncodeEventArgs(encodable));
+ }
+ }
+
+}
diff --git a/src/DotNetOpenId/RelyingParty/AuthenticationRequest.cs b/src/DotNetOpenId/RelyingParty/AuthenticationRequest.cs
index 37ec80f..cc9c3ae 100644
--- a/src/DotNetOpenId/RelyingParty/AuthenticationRequest.cs
+++ b/src/DotNetOpenId/RelyingParty/AuthenticationRequest.cs
@@ -30,15 +30,18 @@ namespace DotNetOpenId.RelyingParty {
class AuthenticationRequest : IAuthenticationRequest {
Association assoc;
ServiceEndpoint endpoint;
+ MessageEncoder encoder;
Protocol protocol { get { return endpoint.Protocol; } }
AuthenticationRequest(string token, Association assoc, ServiceEndpoint endpoint,
- Realm realm, Uri returnToUrl) {
+ Realm realm, Uri returnToUrl, MessageEncoder encoder) {
if (endpoint == null) throw new ArgumentNullException("endpoint");
if (realm == null) throw new ArgumentNullException("realm");
if (returnToUrl == null) throw new ArgumentNullException("returnToUrl");
+ if (encoder == null) throw new ArgumentNullException("encoder");
this.assoc = assoc;
this.endpoint = endpoint;
+ this.encoder = encoder;
Realm = realm;
ReturnToUrl = returnToUrl;
@@ -49,7 +52,7 @@ namespace DotNetOpenId.RelyingParty {
AddCallbackArguments(DotNetOpenId.RelyingParty.Token.TokenKey, token);
}
internal static AuthenticationRequest Create(Identifier userSuppliedIdentifier,
- Realm realm, Uri returnToUrl, IRelyingPartyApplicationStore store) {
+ Realm realm, Uri returnToUrl, IRelyingPartyApplicationStore store, MessageEncoder encoder) {
if (userSuppliedIdentifier == null) throw new ArgumentNullException("userSuppliedIdentifier");
if (realm == null) throw new ArgumentNullException("realm");
@@ -92,7 +95,7 @@ namespace DotNetOpenId.RelyingParty {
return new AuthenticationRequest(
new Token(endpoint).Serialize(store),
store != null ? getAssociation(endpoint, store) : null,
- endpoint, realm, returnToUrl);
+ endpoint, realm, returnToUrl, encoder);
}
static Association getAssociation(ServiceEndpoint provider, IRelyingPartyApplicationStore store) {
if (provider == null) throw new ArgumentNullException("provider");
@@ -147,7 +150,7 @@ namespace DotNetOpenId.RelyingParty {
/// Gets the URL the user agent should be redirected to to begin the
/// OpenID authentication process.
/// </summary>
- public Uri RedirectToProviderUrl {
+ public IResponse RedirectingResponse {
get {
UriBuilder returnToBuilder = new UriBuilder(ReturnToUrl);
UriUtil.AppendQueryArgs(returnToBuilder, this.ReturnToArgs);
@@ -168,16 +171,17 @@ namespace DotNetOpenId.RelyingParty {
if (this.assoc != null)
qsArgs.Add(protocol.openid.assoc_handle, this.assoc.Handle);
- UriBuilder redir = new UriBuilder(this.endpoint.ProviderEndpoint);
+ // Add on extension arguments
+ foreach(var pair in OutgoingExtensions.GetArgumentsToSend(true))
+ qsArgs.Add(pair.Key, pair.Value);
- UriUtil.AppendQueryArgs(redir, qsArgs);
- UriUtil.AppendQueryArgs(redir, OutgoingExtensions.GetArgumentsToSend(true));
-
- return redir.Uri;
+ var request = new IndirectMessageRequest(this.endpoint.ProviderEndpoint, qsArgs);
+ return this.encoder.Encode(request);
}
}
public void AddExtension(DotNetOpenId.Extensions.IExtensionRequest extension) {
+ if (extension == null) throw new ArgumentNullException("extension");
OutgoingExtensions.AddExtensionArguments(extension.TypeUri, extension.Serialize(this));
}
@@ -204,26 +208,15 @@ namespace DotNetOpenId.RelyingParty {
/// <summary>
/// Redirects the user agent to the provider for authentication.
+ /// Execution of the current page terminates after this call.
/// </summary>
/// <remarks>
/// This method requires an ASP.NET HttpContext.
/// </remarks>
public void RedirectToProvider() {
- RedirectToProvider(false);
- }
- /// <summary>
- /// Redirects the user agent to the provider for authentication.
- /// </summary>
- /// <param name="endResponse">
- /// Whether execution of this response should cease after this call.
- /// </param>
- /// <remarks>
- /// This method requires an ASP.NET HttpContext.
- /// </remarks>
- public void RedirectToProvider(bool endResponse) {
if (HttpContext.Current == null || HttpContext.Current.Response == null)
throw new InvalidOperationException(Strings.CurrentHttpContextRequired);
- HttpContext.Current.Response.Redirect(RedirectToProviderUrl.AbsoluteUri, endResponse);
+ RedirectingResponse.Send();
}
}
}
diff --git a/src/DotNetOpenId/RelyingParty/AuthenticationResponse.cs b/src/DotNetOpenId/RelyingParty/AuthenticationResponse.cs
index 978d521..127e90c 100644
--- a/src/DotNetOpenId/RelyingParty/AuthenticationResponse.cs
+++ b/src/DotNetOpenId/RelyingParty/AuthenticationResponse.cs
@@ -111,7 +111,12 @@ namespace DotNetOpenId.RelyingParty {
if (query == null) throw new ArgumentNullException("query");
if (requestUrl == null) throw new ArgumentNullException("requestUrl");
ServiceEndpoint tokenEndpoint = null;
- string token = Util.GetOptionalArg(query, Token.TokenKey);
+ // The query parameter may be the POST query or the GET query,
+ // but the token parameter will always be in the GET query because
+ // it was added to the return_to parameter list.
+ IDictionary<string, string> requestUrlQuery = Util.NameValueCollectionToDictionary(
+ HttpUtility.ParseQueryString(requestUrl.Query));
+ string token = Util.GetOptionalArg(requestUrlQuery, Token.TokenKey);
if (token != null) {
tokenEndpoint = Token.Deserialize(token, store).Endpoint;
}
diff --git a/src/DotNetOpenId/RelyingParty/DirectRequest.cs b/src/DotNetOpenId/RelyingParty/DirectRequest.cs
index d9a8276..9b9d732 100644
--- a/src/DotNetOpenId/RelyingParty/DirectRequest.cs
+++ b/src/DotNetOpenId/RelyingParty/DirectRequest.cs
@@ -4,6 +4,7 @@ using System.Text;
using System.Net;
using System.Diagnostics;
using System.Globalization;
+using System.IO;
namespace DotNetOpenId.RelyingParty {
[DebuggerDisplay("OpenId: {Protocol.Version}")]
@@ -27,6 +28,14 @@ namespace DotNetOpenId.RelyingParty {
IDictionary<string, string> args = null;
try {
resp = UntrustedWebRequest.Request(Provider.ProviderEndpoint, body);
+ // If an internal server error occurred, there won't be any KV-form stream
+ // to read in. So instead, preserve whatever error the server did send back
+ // and throw it in the exception.
+ if (resp.StatusCode == HttpStatusCode.InternalServerError) {
+ string errorStream = new StreamReader(resp.ResponseStream).ReadToEnd();
+ throw new OpenIdException(string.Format(CultureInfo.CurrentCulture,
+ Strings.ProviderRespondedWithError, errorStream));
+ }
args = ProtocolMessages.KeyValueForm.GetDictionary(resp.ResponseStream);
} catch (ArgumentException e) {
throw new OpenIdException("Failure decoding Key-Value Form response from provider.", e);
diff --git a/src/DotNetOpenId/RelyingParty/IAuthenticationRequest.cs b/src/DotNetOpenId/RelyingParty/IAuthenticationRequest.cs
index 3297b64..0fdc4f1 100644
--- a/src/DotNetOpenId/RelyingParty/IAuthenticationRequest.cs
+++ b/src/DotNetOpenId/RelyingParty/IAuthenticationRequest.cs
@@ -26,30 +26,21 @@ namespace DotNetOpenId.RelyingParty {
void AddExtension(IExtensionRequest extension);
/// <summary>
/// Redirects the user agent to the provider for authentication.
+ /// Execution of the current page terminates after this call.
/// </summary>
/// <remarks>
/// This method requires an ASP.NET HttpContext.
/// </remarks>
void RedirectToProvider();
/// <summary>
- /// Redirects the user agent to the provider for authentication.
- /// </summary>
- /// <param name="endResponse">
- /// Whether execution of this response should cease after this call.
- /// </param>
- /// <remarks>
- /// This method requires an ASP.NET HttpContext.
- /// </remarks>
- void RedirectToProvider(bool endResponse);
- /// <summary>
/// Gets/sets the mode the Provider should use during authentication.
/// </summary>
AuthenticationRequestMode Mode { get; set; }
/// <summary>
- /// Gets the URL the user agent should be redirected to to begin the
- /// OpenID authentication process.
+ /// Gets the HTTP response the relying party should send to the user agent
+ /// to redirect it to the OpenID Provider to start the OpenID authentication process.
/// </summary>
- Uri RedirectToProviderUrl { get; }
+ IResponse RedirectingResponse { get; }
/// <summary>
/// Gets the URL that the user agent will return to after authentication
/// completes or fails at the Provider.
diff --git a/src/DotNetOpenId/RelyingParty/IndirectMessageRequest.cs b/src/DotNetOpenId/RelyingParty/IndirectMessageRequest.cs
new file mode 100644
index 0000000..8348374
--- /dev/null
+++ b/src/DotNetOpenId/RelyingParty/IndirectMessageRequest.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace DotNetOpenId.RelyingParty {
+ internal class IndirectMessageRequest : IEncodable {
+ public IndirectMessageRequest(Uri receivingUrl, IDictionary<string, string> fields) {
+ if (receivingUrl == null) throw new ArgumentNullException("receivingUrl");
+ if (fields == null) throw new ArgumentNullException("fields");
+ RedirectUrl = receivingUrl;
+ EncodedFields = fields;
+ }
+
+ #region IEncodable Members
+
+ public EncodingType EncodingType { get { return EncodingType.IndirectMessage ; } }
+ public IDictionary<string, string> EncodedFields { get; private set; }
+ public Uri RedirectUrl { get; private set; }
+ public Protocol Protocol {
+ get { throw new NotImplementedException(); }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenId/RelyingParty/OpenIdRelyingParty.cs
index 5203966..f53b8f0 100644
--- a/src/DotNetOpenId/RelyingParty/OpenIdRelyingParty.cs
+++ b/src/DotNetOpenId/RelyingParty/OpenIdRelyingParty.cs
@@ -17,6 +17,7 @@ namespace DotNetOpenId.RelyingParty {
IRelyingPartyApplicationStore store;
Uri request;
IDictionary<string, string> query;
+ MessageEncoder encoder;
/// <summary>
/// Constructs an OpenId consumer that uses the current HttpContext request
@@ -25,7 +26,9 @@ namespace DotNetOpenId.RelyingParty {
/// <remarks>
/// This method requires a current ASP.NET HttpContext.
/// </remarks>
- public OpenIdRelyingParty() : this(HttpApplicationStore, Util.GetRequestUrlFromContext()) { }
+ public OpenIdRelyingParty()
+ : this(HttpApplicationStore,
+ Util.GetRequestUrlFromContext(), Util.GetQueryFromContext()) { }
/// <summary>
/// Constructs an OpenId consumer that uses a given querystring and IAssociationStore.
/// </summary>
@@ -39,6 +42,12 @@ namespace DotNetOpenId.RelyingParty {
/// Optional. The current incoming HTTP request that may contain an OpenId assertion.
/// If not included, any OpenId authentication assertions will not be processed.
/// </param>
+ /// <param name="query">
+ /// The name/value pairs that came in on the
+ /// QueryString of a GET request or in the entity of a POST request.
+ /// For example: (Request.HttpMethod == "GET" ? Request.QueryString : Request.Form).
+ /// This must be supplied if <paramref name="requestUrl"/> is supplied.
+ /// </param>
/// <remarks>
/// The IRelyingPartyApplicationStore must be shared across an entire web farm
/// because of the design of how nonces are stored/retrieved. Even if
@@ -47,15 +56,20 @@ namespace DotNetOpenId.RelyingParty {
/// which must therefore share the nonce information in the application
/// state store in order to stop the intruder.
/// </remarks>
- public OpenIdRelyingParty(IRelyingPartyApplicationStore store, Uri requestUrl) {
+ public OpenIdRelyingParty(IRelyingPartyApplicationStore store, Uri requestUrl, NameValueCollection query) :
+ this(store, requestUrl, Util.NameValueCollectionToDictionary(query)) {
+ }
+ OpenIdRelyingParty(IRelyingPartyApplicationStore store, Uri requestUrl, IDictionary<string, string> query) {
this.store = store;
if (store != null) {
store.ClearExpiredAssociations(); // every so often we should do this.
}
if (requestUrl != null) {
+ if (query == null) throw new ArgumentNullException("query");
this.request = requestUrl;
- this.query = Util.NameValueCollectionToDictionary(HttpUtility.ParseQueryString(requestUrl.Query));
+ this.query = query;
}
+ this.encoder = new MessageEncoder();
}
/// <summary>
@@ -79,7 +93,7 @@ namespace DotNetOpenId.RelyingParty {
/// send to the user agent to initiate the authentication.
/// </returns>
public IAuthenticationRequest CreateRequest(Identifier userSuppliedIdentifier, Realm realm, Uri returnToUrl) {
- return AuthenticationRequest.Create(userSuppliedIdentifier, realm, returnToUrl, store);
+ return AuthenticationRequest.Create(userSuppliedIdentifier, realm, returnToUrl, store, encoder);
}
/// <remarks>
@@ -138,9 +152,6 @@ namespace DotNetOpenId.RelyingParty {
if (!query.ContainsKey(protocol.openid.mode))
return false;
- if (HttpContext.Current != null && !HttpContext.Current.Request.RequestType.Equals("GET", StringComparison.Ordinal))
- return false;
-
return true;
}
}
diff --git a/src/DotNetOpenId/RelyingParty/OpenIdTextBox.cs b/src/DotNetOpenId/RelyingParty/OpenIdTextBox.cs
index d8f7e61..57a5fd4 100644
--- a/src/DotNetOpenId/RelyingParty/OpenIdTextBox.cs
+++ b/src/DotNetOpenId/RelyingParty/OpenIdTextBox.cs
@@ -552,7 +552,7 @@ namespace DotNetOpenId.RelyingParty
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
- if (!Enabled) return;
+ if (!Enabled || Page.IsPostBack) return;
var consumer = new OpenIdRelyingParty();
if (consumer.Response != null) {
switch (consumer.Response.Status) {
diff --git a/src/DotNetOpenId/Provider/Response.cs b/src/DotNetOpenId/Response.cs
index c8ca8b9..d4469ec 100644
--- a/src/DotNetOpenId/Provider/Response.cs
+++ b/src/DotNetOpenId/Response.cs
@@ -3,24 +3,37 @@ using System.Collections.Specialized;
using System.Text;
using System.Net;
using System.Web;
+using System.Diagnostics;
-namespace DotNetOpenId.Provider {
+namespace DotNetOpenId {
/// <summary>
- /// A response to an OpenID request in terms a web server understands.
+ /// A response to an OpenID request in terms the host web site can forward to the user agent.
/// </summary>
class Response : IResponse {
- internal Response(HttpStatusCode code, WebHeaderCollection headers, byte[] body) {
+ /// <param name="code">The HTTP status code.</param>
+ /// <param name="headers">The collection of any HTTP headers that should be included. Cannot be null, but can be an empty collection.</param>
+ /// <param name="body">The payload of the response, if any. Cannot be null, but can be an empty array.</param>
+ /// <param name="encodableMessage">
+ /// Used to assist testing to decipher the field contents of a Response.
+ /// </param>
+ internal Response(HttpStatusCode code, WebHeaderCollection headers, byte[] body, IEncodable encodableMessage) {
+ if (headers == null) throw new ArgumentNullException("headers");
+ if (body == null) throw new ArgumentNullException("body");
+ Debug.Assert(encodableMessage != null, "For testing, this is useful to have.");
Code = code;
Headers = headers ?? new WebHeaderCollection();
Body = body;
+ EncodableMessage = encodableMessage;
}
public HttpStatusCode Code { get; private set; }
public WebHeaderCollection Headers { get; private set; }
public byte[] Body { get; private set; }
+ internal IEncodable EncodableMessage { get; private set; }
/// <summary>
/// Sends this response to the user agent or OpenId consumer.
+ /// Execution of the current page terminates after this call.
/// </summary>
/// <remarks>
/// This method requires a current ASP.NET HttpContext.
@@ -36,6 +49,7 @@ namespace DotNetOpenId.Provider {
HttpContext.Current.Response.OutputStream.Flush();
}
HttpContext.Current.Response.OutputStream.Close();
+ HttpContext.Current.Response.End();
}
}
}
diff --git a/src/DotNetOpenId/Util.cs b/src/DotNetOpenId/Util.cs
index 82892b2..65bf3b0 100644
--- a/src/DotNetOpenId/Util.cs
+++ b/src/DotNetOpenId/Util.cs
@@ -88,7 +88,7 @@ namespace DotNetOpenId {
internal const string DefaultNamespace = "DotNetOpenId";
public static IDictionary<string, string> NameValueCollectionToDictionary(NameValueCollection nvc) {
- if (nvc == null) throw new ArgumentNullException("nvc");
+ if (nvc == null) return null;
var dict = new Dictionary<string, string>(nvc.Count);
for (int i = 0; i < nvc.Count; i++) {
string key = nvc.GetKey(i);
@@ -103,6 +103,7 @@ namespace DotNetOpenId {
return dict;
}
public static NameValueCollection DictionaryToNameValueCollection(IDictionary<string, string> dict) {
+ if (dict == null) return null;
NameValueCollection nvc = new NameValueCollection(dict.Count);
foreach (var pair in dict) {
nvc.Add(pair.Key, pair.Value);