summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--samples/OpenIdOfflineProvider/HttpHost.cs15
-rw-r--r--samples/OpenIdProviderMvc/Code/ReadOnlyXmlMembershipProvider.cs2
-rw-r--r--samples/OpenIdProviderMvc/Controllers/HomeController.cs2
-rw-r--r--samples/OpenIdProviderMvc/Controllers/UserController.cs33
-rw-r--r--samples/OpenIdProviderMvc/Global.asax.cs23
-rw-r--r--samples/OpenIdProviderMvc/Models/User.cs4
-rw-r--r--samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj4
-rw-r--r--samples/OpenIdProviderMvc/Views/Home/Xrds.aspx19
-rw-r--r--samples/OpenIdProviderMvc/Views/Shared/Xrds.aspx31
-rw-r--r--samples/OpenIdProviderMvc/Views/User/Identity.aspx24
-rw-r--r--samples/OpenIdProviderMvc/Views/User/PpidIdentity.aspx16
-rw-r--r--samples/OpenIdProviderMvc/Views/User/Xrds.aspx15
-rw-r--r--samples/OpenIdProviderWebForms/Code/CustomStore.cs5
-rw-r--r--samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.Designer.cs6
-rw-r--r--samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.xsd17
-rw-r--r--samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.xss2
-rw-r--r--samples/OpenIdRelyingPartyWebForms/Code/CustomStore.cs5
-rw-r--r--samples/OpenIdRelyingPartyWebForms/Code/CustomStoreDataSet.xsd17
-rw-r--r--samples/OpenIdRelyingPartyWebForms/Code/CustomStoreDataSet.xss2
-rw-r--r--samples/OpenIdRelyingPartyWebForms/Code/CustomStoreDataSet1.Designer.cs6
-rw-r--r--samples/OpenIdRelyingPartyWebForms/xrds.aspx4
-rw-r--r--src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj3
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/OutgoingWebResponseTests.cs36
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/MockRealm.cs2
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs21
-rw-r--r--src/DotNetOpenAuth.sln3
-rw-r--r--src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd9
-rw-r--r--src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs17
-rw-r--r--src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs11
-rw-r--r--src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs10
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs21
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/UI/UIConstants.cs6
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs84
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdXrdsHelper.cs13
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs6
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs34
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs44
-rw-r--r--src/DotNetOpenAuth/OpenId/Realm.cs24
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs6
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs8
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs6
-rw-r--r--src/DotNetOpenAuth/Xrds/XrdElement.cs7
45 files changed, 462 insertions, 167 deletions
diff --git a/samples/OpenIdOfflineProvider/HttpHost.cs b/samples/OpenIdOfflineProvider/HttpHost.cs
index 390275a..a2558f4 100644
--- a/samples/OpenIdOfflineProvider/HttpHost.cs
+++ b/samples/OpenIdOfflineProvider/HttpHost.cs
@@ -121,14 +121,19 @@ namespace DotNetOpenAuth.OpenIdOfflineProvider {
private void ProcessRequests() {
Contract.Requires(this.listener != null);
- try {
- while (true) {
+ while (true) {
+ try {
HttpListenerContext context = this.listener.GetContext();
this.handler(context);
+ } catch (HttpListenerException ex) {
+ if (this.listener.IsListening) {
+ App.Logger.Error("Unexpected exception.", ex);
+ } else {
+ // the listener is probably being shut down
+ App.Logger.Warn("HTTP listener is closing down.", ex);
+ break;
+ }
}
- } catch (HttpListenerException ex) {
- // the listener is probably being shut down
- App.Logger.Warn("HTTP listener is closing down.", ex);
}
}
}
diff --git a/samples/OpenIdProviderMvc/Code/ReadOnlyXmlMembershipProvider.cs b/samples/OpenIdProviderMvc/Code/ReadOnlyXmlMembershipProvider.cs
index cc5a321..d66573f 100644
--- a/samples/OpenIdProviderMvc/Code/ReadOnlyXmlMembershipProvider.cs
+++ b/samples/OpenIdProviderMvc/Code/ReadOnlyXmlMembershipProvider.cs
@@ -237,6 +237,8 @@
}
internal string GetSalt(string userName) {
+ // This is just a sample with no database... a real web app MUST return
+ // a reasonable salt here and have that salt be persistent for each user.
this.ReadMembershipDataStore();
return this.users[userName].Email;
}
diff --git a/samples/OpenIdProviderMvc/Controllers/HomeController.cs b/samples/OpenIdProviderMvc/Controllers/HomeController.cs
index 5ba08b3..fb03ce2 100644
--- a/samples/OpenIdProviderMvc/Controllers/HomeController.cs
+++ b/samples/OpenIdProviderMvc/Controllers/HomeController.cs
@@ -9,6 +9,7 @@
public class HomeController : Controller {
public ActionResult Index() {
if (Request.AcceptTypes.Contains("application/xrds+xml")) {
+ ViewData["OPIdentifier"] = true;
return View("Xrds");
}
@@ -21,6 +22,7 @@
}
public ActionResult Xrds() {
+ ViewData["OPIdentifier"] = true;
return View();
}
}
diff --git a/samples/OpenIdProviderMvc/Controllers/UserController.cs b/samples/OpenIdProviderMvc/Controllers/UserController.cs
index 4fc2f9f..5e0c21f 100644
--- a/samples/OpenIdProviderMvc/Controllers/UserController.cs
+++ b/samples/OpenIdProviderMvc/Controllers/UserController.cs
@@ -7,25 +7,28 @@ namespace OpenIdProviderMvc.Controllers {
using System.Web.Mvc.Ajax;
public class UserController : Controller {
- public ActionResult PpidIdentity() {
- if (Request.AcceptTypes.Contains("application/xrds+xml")) {
- return View("Xrds");
- }
-
- return View();
- }
-
- public ActionResult Identity(string id) {
- var redirect = this.RedirectIfNotNormalizedRequestUri();
- if (redirect != null) {
- return redirect;
+ /// <summary>
+ /// Identities the specified id.
+ /// </summary>
+ /// <param name="id">The username or anonymous identifier.</param>
+ /// <param name="anon">if set to <c>true</c> then <paramref name="id"/> represents an anonymous identifier rather than a username.</param>
+ /// <returns>The view to display.</returns>
+ public ActionResult Identity(string id, bool anon) {
+ if (!anon) {
+ var redirect = this.RedirectIfNotNormalizedRequestUri(id);
+ if (redirect != null) {
+ return redirect;
+ }
}
if (Request.AcceptTypes != null && Request.AcceptTypes.Contains("application/xrds+xml")) {
return View("Xrds");
}
- this.ViewData["username"] = id;
+ if (!anon) {
+ this.ViewData["username"] = id;
+ }
+
return View();
}
@@ -33,8 +36,8 @@ namespace OpenIdProviderMvc.Controllers {
return View();
}
- private ActionResult RedirectIfNotNormalizedRequestUri() {
- Uri normalized = Models.User.GetNormalizedClaimedIdentifier(Request.Url);
+ private ActionResult RedirectIfNotNormalizedRequestUri(string user) {
+ Uri normalized = Models.User.GetClaimedIdentifierForUser(user);
if (Request.Url != normalized) {
return Redirect(normalized.AbsoluteUri);
}
diff --git a/samples/OpenIdProviderMvc/Global.asax.cs b/samples/OpenIdProviderMvc/Global.asax.cs
index 3ca6104..8390c46 100644
--- a/samples/OpenIdProviderMvc/Global.asax.cs
+++ b/samples/OpenIdProviderMvc/Global.asax.cs
@@ -14,17 +14,19 @@
/// visit http://go.microsoft.com/?LinkId=9394801
/// </remarks>
public class MvcApplication : System.Web.HttpApplication {
+ private static object behaviorInitializationSyncObject = new object();
+
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"User identities",
"user/{id}/{action}",
- new { controller = "User", action = "Identity", id = string.Empty });
+ new { controller = "User", action = "Identity", id = string.Empty, anon = false });
routes.MapRoute(
"PPID identifiers",
"anon",
- new { controller = "User", action = "PpidIdentity", id = string.Empty });
+ new { controller = "User", action = "Identity", id = string.Empty, anon = true });
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
@@ -33,8 +35,21 @@
protected void Application_Start() {
RegisterRoutes(RouteTable.Routes);
- DotNetOpenAuth.OpenId.Behaviors.PpidGeneration.PpidIdentifierProvider = new Code.AnonymousIdentifierProvider();
- DotNetOpenAuth.OpenId.Behaviors.GsaIcamProfile.PpidIdentifierProvider = new Code.AnonymousIdentifierProvider();
+ }
+
+ protected void Application_BeginRequest(object sender, EventArgs e) {
+ InitializeBehaviors();
+ }
+
+ private static void InitializeBehaviors() {
+ if (DotNetOpenAuth.OpenId.Behaviors.PpidGeneration.PpidIdentifierProvider == null) {
+ lock (behaviorInitializationSyncObject) {
+ if (DotNetOpenAuth.OpenId.Behaviors.PpidGeneration.PpidIdentifierProvider == null) {
+ DotNetOpenAuth.OpenId.Behaviors.PpidGeneration.PpidIdentifierProvider = new Code.AnonymousIdentifierProvider();
+ DotNetOpenAuth.OpenId.Behaviors.GsaIcamProfile.PpidIdentifierProvider = new Code.AnonymousIdentifierProvider();
+ }
+ }
+ }
}
}
} \ No newline at end of file
diff --git a/samples/OpenIdProviderMvc/Models/User.cs b/samples/OpenIdProviderMvc/Models/User.cs
index 443c004..198b8fa 100644
--- a/samples/OpenIdProviderMvc/Models/User.cs
+++ b/samples/OpenIdProviderMvc/Models/User.cs
@@ -8,10 +8,6 @@
using OpenIdProviderMvc.Code;
internal class User {
- internal static Uri PpidClaimedIdentifierBaseUri {
- get { return Util.GetAppPathRootedUri("anon?id="); }
- }
-
internal static Uri ClaimedIdentifierBaseUri {
get { return Util.GetAppPathRootedUri("user/"); }
}
diff --git a/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj b/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj
index f568538..0c01c64 100644
--- a/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj
+++ b/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj
@@ -92,11 +92,9 @@
<Content Include="Views\Account\ChangePassword.aspx" />
<Content Include="Views\Account\ChangePasswordSuccess.aspx" />
<Content Include="Views\Account\Register.aspx" />
- <Content Include="Views\Home\Xrds.aspx" />
+ <Content Include="Views\Shared\Xrds.aspx" />
<Content Include="Views\OpenId\Provider.aspx" />
- <Content Include="Views\User\PpidIdentity.aspx" />
<Content Include="Views\User\Identity.aspx" />
- <Content Include="Views\User\Xrds.aspx" />
<Content Include="Web.config" />
<Content Include="Content\Site.css" />
<Content Include="Scripts\jquery-1.3.1.js" />
diff --git a/samples/OpenIdProviderMvc/Views/Home/Xrds.aspx b/samples/OpenIdProviderMvc/Views/Home/Xrds.aspx
deleted file mode 100644
index 7b0c417..0000000
--- a/samples/OpenIdProviderMvc/Views/Home/Xrds.aspx
+++ /dev/null
@@ -1,19 +0,0 @@
-<%@ Page Language="C#" AutoEventWireup="true" ContentType="application/xrds+xml" %><?xml version="1.0" encoding="UTF-8"?>
-<%--
-This page is a required as part of the service discovery phase of the openid
-protocol (step 1). It simply renders the xml for doing service discovery of
-server.aspx using the xrds mechanism.
-This XRDS doc is discovered via the user.aspx page.
---%>
-<xrds:XRDS
- xmlns:xrds="xri://$xrds"
- xmlns:openid="http://openid.net/xmlns/1.0"
- xmlns="xri://$xrd*($v*2.0)">
- <XRD>
- <Service priority="10">
- <Type>http://specs.openid.net/auth/2.0/server</Type>
- <Type>http://openid.net/extensions/sreg/1.1</Type>
- <URI><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/OpenId/Provider"))%></URI>
- </Service>
- </XRD>
-</xrds:XRDS>
diff --git a/samples/OpenIdProviderMvc/Views/Shared/Xrds.aspx b/samples/OpenIdProviderMvc/Views/Shared/Xrds.aspx
new file mode 100644
index 0000000..7aad102
--- /dev/null
+++ b/samples/OpenIdProviderMvc/Views/Shared/Xrds.aspx
@@ -0,0 +1,31 @@
+<%@ Page Language="C#" AutoEventWireup="true" ContentType="application/xrds+xml" %>
+<%@ OutputCache Duration="86400" VaryByParam="none" Location="Any" %><?xml version="1.0" encoding="UTF-8"?>
+<%--
+This XRDS view is used for both the OP identifier and the user identity pages.
+Only a couple of conditional checks are required to share the view, but sharing the view
+makes it very easy to ensure that all the Type URIs that this server supports are included
+for all XRDS discovery.
+--%>
+<xrds:XRDS
+ xmlns:xrds="xri://$xrds"
+ xmlns:openid="http://openid.net/xmlns/1.0"
+ xmlns="xri://$xrd*($v*2.0)">
+ <XRD>
+ <Service priority="10">
+<% if (ViewData["OPIdentifier"] != null) { %>
+ <Type>http://specs.openid.net/auth/2.0/server</Type>
+<% } else { %>
+ <Type>http://specs.openid.net/auth/2.0/signon</Type>
+<% } %>
+ <Type>http://openid.net/extensions/sreg/1.1</Type>
+ <Type>http://axschema.org/contact/email</Type>
+
+ <%--
+ Add these types when and if the Provider supports the respective aspects of the UI extension.
+ <Type>http://specs.openid.net/extensions/ui/1.0/mode/popup</Type>
+ <Type>http://specs.openid.net/extensions/ui/1.0/lang-pref</Type>
+ <Type>http://specs.openid.net/extensions/ui/1.0/icon</Type>--%>
+ <URI><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/OpenId/Provider"))%></URI>
+ </Service>
+ </XRD>
+</xrds:XRDS>
diff --git a/samples/OpenIdProviderMvc/Views/User/Identity.aspx b/samples/OpenIdProviderMvc/Views/User/Identity.aspx
index bb50899..51233a3 100644
--- a/samples/OpenIdProviderMvc/Views/User/Identity.aspx
+++ b/samples/OpenIdProviderMvc/Views/User/Identity.aspx
@@ -3,18 +3,26 @@
<%@ Register Assembly="DotNetOpenAuth" Namespace="DotNetOpenAuth.OpenId.Provider"
TagPrefix="op" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
- <%=Html.Encode(ViewData["username"])%>
- identity page
+ <%=Html.Encode(ViewData["username"] ?? string.Empty)%>
+ Identity page
</asp:Content>
<asp:Content runat="server" ContentPlaceHolderID="HeadContent">
- <op:IdentityEndpoint ID="IdentityEndpoint11" runat="server" ProviderEndpointUrl="~/OpenId/Provider" ProviderVersion="V11" />
- <op:IdentityEndpoint ID="IdentityEndpoint20" runat="server" ProviderEndpointUrl="~/OpenId/Provider" XrdsUrl="~/User/all/xrds" XrdsAutoAnswer="false" />
+ <op:IdentityEndpoint ID="IdentityEndpoint11" runat="server" ProviderEndpointUrl="~/OpenId/Provider"
+ ProviderVersion="V11" />
+ <op:IdentityEndpoint ID="IdentityEndpoint20" runat="server" ProviderEndpointUrl="~/OpenId/Provider"
+ XrdsUrl="~/User/all/xrds" XrdsAutoAnswer="false" XrdsAdvertisement="Both" />
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
- <h2>This is
- <%=Html.Encode(ViewData["username"])%>'s OpenID identity page </h2>
-
+ <h2>
+ <% if (!string.IsNullOrEmpty(ViewData["username"] as string)) { %>
+ This is
+ <%=Html.Encode(ViewData["username"])%>'s
+ <% } %>
+ OpenID identity page
+ </h2>
<% if (string.Equals(User.Identity.Name, ViewData["username"])) { %>
- <p>This is <b>your</b> identity page. </p>
+ <p>
+ This is <b>your</b> identity page.
+ </p>
<% } %>
</asp:Content>
diff --git a/samples/OpenIdProviderMvc/Views/User/PpidIdentity.aspx b/samples/OpenIdProviderMvc/Views/User/PpidIdentity.aspx
deleted file mode 100644
index 655e5d6..0000000
--- a/samples/OpenIdProviderMvc/Views/User/PpidIdentity.aspx
+++ /dev/null
@@ -1,16 +0,0 @@
-<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
-
-<%@ Register Assembly="DotNetOpenAuth" Namespace="DotNetOpenAuth.OpenId.Provider"
- TagPrefix="op" %>
-<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
- Identity page
-</asp:Content>
-<asp:Content runat="server" ContentPlaceHolderID="HeadContent">
- <op:IdentityEndpoint ID="IdentityEndpoint11" runat="server" ProviderEndpointUrl="~/OpenId/PpidProvider"
- ProviderVersion="V11" />
- <op:IdentityEndpoint ID="IdentityEndpoint20" runat="server" ProviderEndpointUrl="~/OpenId/PpidProvider"
- XrdsUrl="~/User/all/xrds" XrdsAutoAnswer="false" />
-</asp:Content>
-<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
- <h2>OpenID identity page </h2>
-</asp:Content>
diff --git a/samples/OpenIdProviderMvc/Views/User/Xrds.aspx b/samples/OpenIdProviderMvc/Views/User/Xrds.aspx
deleted file mode 100644
index 452742c..0000000
--- a/samples/OpenIdProviderMvc/Views/User/Xrds.aspx
+++ /dev/null
@@ -1,15 +0,0 @@
-<%@ Page Language="C#" AutoEventWireup="true" ContentType="application/xrds+xml" %><?xml version="1.0" encoding="UTF-8"?>
-<XRDS xmlns="xri://$xrds" xmlns:openid="http://openid.net/xmlns/1.0">
- <XRD xmlns="xri://$xrd*($v*2.0)">
- <Service priority="10">
- <Type>http://specs.openid.net/auth/2.0/signon</Type>
- <Type>http://openid.net/extensions/sreg/1.1</Type>
- <URI><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/OpenId/Provider"))%></URI>
- </Service>
- <Service priority="20">
- <Type>http://openid.net/signon/1.0</Type>
- <Type>http://openid.net/extensions/sreg/1.1</Type>
- <URI><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/OpenId/Provider"))%></URI>
- </Service>
- </XRD>
-</XRDS>
diff --git a/samples/OpenIdProviderWebForms/Code/CustomStore.cs b/samples/OpenIdProviderWebForms/Code/CustomStore.cs
index d8181fe..7face0b 100644
--- a/samples/OpenIdProviderWebForms/Code/CustomStore.cs
+++ b/samples/OpenIdProviderWebForms/Code/CustomStore.cs
@@ -109,13 +109,14 @@ namespace OpenIdProviderWebForms.Code {
// at you in the result of a race condition somewhere in your web site UI code
// and display some message to have the user try to log in again, and possibly
// warn them about a replay attack.
+ timestamp = timestamp.ToLocalTime();
lock (this) {
- if (dataSet.Nonce.FindByCodeContext(nonce, context) != null) {
+ if (dataSet.Nonce.FindByIssuedCodeContext(timestamp, nonce, context) != null) {
return false;
}
TimeSpan maxMessageAge = DotNetOpenAuth.Configuration.DotNetOpenAuthSection.Configuration.Messaging.MaximumMessageLifetime;
- dataSet.Nonce.AddNonceRow(context, nonce, timestamp.ToLocalTime(), (timestamp + maxMessageAge).ToLocalTime());
+ dataSet.Nonce.AddNonceRow(context, nonce, timestamp, timestamp + maxMessageAge);
return true;
}
}
diff --git a/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.Designer.cs b/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.Designer.cs
index d836261..813ff62 100644
--- a/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.Designer.cs
+++ b/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
-// Runtime Version:2.0.50727.3521
+// Runtime Version:2.0.50727.4927
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -678,8 +678,9 @@ namespace OpenIdProviderWebForms.Code {
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- public NonceRow FindByCodeContext(string Code, string Context) {
+ public NonceRow FindByIssuedCodeContext(System.DateTime Issued, string Code, string Context) {
return ((NonceRow)(this.Rows.Find(new object[] {
+ Issued,
Code,
Context})));
}
@@ -715,6 +716,7 @@ namespace OpenIdProviderWebForms.Code {
this.columnExpires = new global::System.Data.DataColumn("Expires", typeof(global::System.DateTime), null, global::System.Data.MappingType.Element);
base.Columns.Add(this.columnExpires);
this.Constraints.Add(new global::System.Data.UniqueConstraint("Constraint1", new global::System.Data.DataColumn[] {
+ this.columnIssued,
this.columnCode,
this.columnContext}, true));
this.columnContext.AllowDBNull = false;
diff --git a/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.xsd b/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.xsd
index 295fe74..04a96eb 100644
--- a/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.xsd
+++ b/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.xsd
@@ -15,20 +15,20 @@
<xs:element name="Association" msprop:Generator_UserTableName="Association" msprop:Generator_RowDeletedName="AssociationRowDeleted" msprop:Generator_RowChangedName="AssociationRowChanged" msprop:Generator_RowClassName="AssociationRow" msprop:Generator_RowChangingName="AssociationRowChanging" msprop:Generator_RowEvArgName="AssociationRowChangeEvent" msprop:Generator_RowEvHandlerName="AssociationRowChangeEventHandler" msprop:Generator_TableClassName="AssociationDataTable" msprop:Generator_TableVarName="tableAssociation" msprop:Generator_RowDeletingName="AssociationRowDeleting" msprop:Generator_TablePropName="Association">
<xs:complexType>
<xs:sequence>
- <xs:element name="DistinguishingFactor" msprop:Generator_UserColumnName="DistinguishingFactor" msprop:Generator_ColumnPropNameInRow="DistinguishingFactor" msprop:Generator_ColumnVarNameInTable="columnDistinguishingFactor" msprop:Generator_ColumnPropNameInTable="DistinguishingFactorColumn" type="xs:string" />
- <xs:element name="Handle" msprop:Generator_UserColumnName="Handle" msprop:Generator_ColumnPropNameInRow="Handle" msprop:Generator_ColumnVarNameInTable="columnHandle" msprop:Generator_ColumnPropNameInTable="HandleColumn" type="xs:string" />
- <xs:element name="Expires" msprop:Generator_UserColumnName="Expires" msprop:Generator_ColumnPropNameInRow="Expires" msprop:Generator_ColumnVarNameInTable="columnExpires" msprop:Generator_ColumnPropNameInTable="ExpiresColumn" type="xs:dateTime" />
- <xs:element name="PrivateData" msprop:Generator_UserColumnName="PrivateData" msprop:Generator_ColumnPropNameInRow="PrivateData" msprop:Generator_ColumnVarNameInTable="columnPrivateData" msprop:Generator_ColumnPropNameInTable="PrivateDataColumn" type="xs:base64Binary" />
+ <xs:element name="DistinguishingFactor" msprop:Generator_UserColumnName="DistinguishingFactor" msprop:Generator_ColumnVarNameInTable="columnDistinguishingFactor" msprop:Generator_ColumnPropNameInRow="DistinguishingFactor" msprop:Generator_ColumnPropNameInTable="DistinguishingFactorColumn" type="xs:string" />
+ <xs:element name="Handle" msprop:Generator_UserColumnName="Handle" msprop:Generator_ColumnVarNameInTable="columnHandle" msprop:Generator_ColumnPropNameInRow="Handle" msprop:Generator_ColumnPropNameInTable="HandleColumn" type="xs:string" />
+ <xs:element name="Expires" msprop:Generator_UserColumnName="Expires" msprop:Generator_ColumnVarNameInTable="columnExpires" msprop:Generator_ColumnPropNameInRow="Expires" msprop:Generator_ColumnPropNameInTable="ExpiresColumn" type="xs:dateTime" />
+ <xs:element name="PrivateData" msprop:Generator_UserColumnName="PrivateData" msprop:Generator_ColumnVarNameInTable="columnPrivateData" msprop:Generator_ColumnPropNameInRow="PrivateData" msprop:Generator_ColumnPropNameInTable="PrivateDataColumn" type="xs:base64Binary" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Nonce" msprop:Generator_UserTableName="Nonce" msprop:Generator_RowDeletedName="NonceRowDeleted" msprop:Generator_RowChangedName="NonceRowChanged" msprop:Generator_RowClassName="NonceRow" msprop:Generator_RowChangingName="NonceRowChanging" msprop:Generator_RowEvArgName="NonceRowChangeEvent" msprop:Generator_RowEvHandlerName="NonceRowChangeEventHandler" msprop:Generator_TableClassName="NonceDataTable" msprop:Generator_TableVarName="tableNonce" msprop:Generator_RowDeletingName="NonceRowDeleting" msprop:Generator_TablePropName="Nonce">
<xs:complexType>
<xs:sequence>
- <xs:element name="Context" msprop:Generator_UserColumnName="Context" msprop:Generator_ColumnPropNameInRow="Context" msprop:Generator_ColumnVarNameInTable="columnContext" msprop:Generator_ColumnPropNameInTable="ContextColumn" type="xs:string" />
- <xs:element name="Code" msprop:Generator_UserColumnName="Code" msprop:Generator_ColumnVarNameInTable="columnCode" msprop:Generator_ColumnPropNameInRow="Code" msprop:Generator_ColumnPropNameInTable="CodeColumn" type="xs:string" />
- <xs:element name="Issued" msprop:Generator_UserColumnName="Issued" msprop:Generator_ColumnVarNameInTable="columnIssued" msprop:Generator_ColumnPropNameInRow="Issued" msprop:Generator_ColumnPropNameInTable="IssuedColumn" type="xs:dateTime" />
- <xs:element name="Expires" msprop:Generator_UserColumnName="Expires" msprop:Generator_ColumnVarNameInTable="columnExpires" msprop:Generator_ColumnPropNameInRow="Expires" msprop:Generator_ColumnPropNameInTable="ExpiresColumn" type="xs:dateTime" />
+ <xs:element name="Context" msprop:Generator_UserColumnName="Context" msprop:Generator_ColumnVarNameInTable="columnContext" msprop:Generator_ColumnPropNameInRow="Context" msprop:Generator_ColumnPropNameInTable="ContextColumn" type="xs:string" />
+ <xs:element name="Code" msprop:Generator_UserColumnName="Code" msprop:Generator_ColumnPropNameInRow="Code" msprop:Generator_ColumnVarNameInTable="columnCode" msprop:Generator_ColumnPropNameInTable="CodeColumn" type="xs:string" />
+ <xs:element name="Issued" msprop:Generator_UserColumnName="Issued" msprop:Generator_ColumnPropNameInRow="Issued" msprop:Generator_ColumnVarNameInTable="columnIssued" msprop:Generator_ColumnPropNameInTable="IssuedColumn" type="xs:dateTime" />
+ <xs:element name="Expires" msprop:Generator_UserColumnName="Expires" msprop:Generator_ColumnPropNameInRow="Expires" msprop:Generator_ColumnVarNameInTable="columnExpires" msprop:Generator_ColumnPropNameInTable="ExpiresColumn" type="xs:dateTime" />
</xs:sequence>
</xs:complexType>
</xs:element>
@@ -41,6 +41,7 @@
</xs:unique>
<xs:unique name="Constraint1" msdata:PrimaryKey="true">
<xs:selector xpath=".//mstns:Nonce" />
+ <xs:field xpath="mstns:Issued" />
<xs:field xpath="mstns:Code" />
<xs:field xpath="mstns:Context" />
</xs:unique>
diff --git a/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.xss b/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.xss
index ede3b42..b19f728 100644
--- a/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.xss
+++ b/samples/OpenIdProviderWebForms/Code/CustomStoreDataSet.xss
@@ -7,7 +7,7 @@
<DiagramLayout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ex:showrelationlabel="False" ViewPortX="-10" ViewPortY="-10" xmlns:ex="urn:schemas-microsoft-com:xml-msdatasource-layout-extended" xmlns="urn:schemas-microsoft-com:xml-msdatasource-layout">
<Shapes>
<Shape ID="DesignTable:Association" ZOrder="2" X="349" Y="83" Height="105" Width="154" AdapterExpanded="true" DataTableExpanded="true" OldAdapterHeight="0" OldDataTableHeight="0" SplitterPosition="101" />
- <Shape ID="DesignTable:Nonce" ZOrder="1" X="567" Y="77" Height="86" Width="150" AdapterExpanded="true" DataTableExpanded="true" OldAdapterHeight="0" OldDataTableHeight="0" SplitterPosition="86" />
+ <Shape ID="DesignTable:Nonce" ZOrder="1" X="567" Y="77" Height="125" Width="150" AdapterExpanded="true" DataTableExpanded="true" OldAdapterHeight="0" OldDataTableHeight="0" SplitterPosition="121" />
</Shapes>
<Connectors />
</DiagramLayout> \ No newline at end of file
diff --git a/samples/OpenIdRelyingPartyWebForms/Code/CustomStore.cs b/samples/OpenIdRelyingPartyWebForms/Code/CustomStore.cs
index 2363aec..07f209b 100644
--- a/samples/OpenIdRelyingPartyWebForms/Code/CustomStore.cs
+++ b/samples/OpenIdRelyingPartyWebForms/Code/CustomStore.cs
@@ -64,13 +64,14 @@
// at you in the result of a race condition somewhere in your web site UI code
// and display some message to have the user try to log in again, and possibly
// warn them about a replay attack.
+ timestamp = timestamp.ToLocalTime();
lock (this) {
- if (dataSet.Nonce.FindByCodeContext(nonce, context) != null) {
+ if (dataSet.Nonce.FindByIssuedCodeContext(timestamp, nonce, context) != null) {
return false;
}
TimeSpan maxMessageAge = DotNetOpenAuth.Configuration.DotNetOpenAuthSection.Configuration.Messaging.MaximumMessageLifetime;
- dataSet.Nonce.AddNonceRow(context, nonce, timestamp.ToLocalTime(), (timestamp + maxMessageAge).ToLocalTime());
+ dataSet.Nonce.AddNonceRow(context, nonce, timestamp, timestamp + maxMessageAge);
return true;
}
}
diff --git a/samples/OpenIdRelyingPartyWebForms/Code/CustomStoreDataSet.xsd b/samples/OpenIdRelyingPartyWebForms/Code/CustomStoreDataSet.xsd
index b80310e..fa161fd 100644
--- a/samples/OpenIdRelyingPartyWebForms/Code/CustomStoreDataSet.xsd
+++ b/samples/OpenIdRelyingPartyWebForms/Code/CustomStoreDataSet.xsd
@@ -15,20 +15,20 @@
<xs:element name="Association" msprop:Generator_UserTableName="Association" msprop:Generator_RowDeletedName="AssociationRowDeleted" msprop:Generator_RowChangedName="AssociationRowChanged" msprop:Generator_RowClassName="AssociationRow" msprop:Generator_RowChangingName="AssociationRowChanging" msprop:Generator_RowEvArgName="AssociationRowChangeEvent" msprop:Generator_RowEvHandlerName="AssociationRowChangeEventHandler" msprop:Generator_TableClassName="AssociationDataTable" msprop:Generator_TableVarName="tableAssociation" msprop:Generator_RowDeletingName="AssociationRowDeleting" msprop:Generator_TablePropName="Association">
<xs:complexType>
<xs:sequence>
- <xs:element name="DistinguishingFactor" msprop:Generator_UserColumnName="DistinguishingFactor" msprop:Generator_ColumnVarNameInTable="columnDistinguishingFactor" msprop:Generator_ColumnPropNameInRow="DistinguishingFactor" msprop:Generator_ColumnPropNameInTable="DistinguishingFactorColumn" type="xs:string" />
- <xs:element name="Handle" msprop:Generator_UserColumnName="Handle" msprop:Generator_ColumnVarNameInTable="columnHandle" msprop:Generator_ColumnPropNameInRow="Handle" msprop:Generator_ColumnPropNameInTable="HandleColumn" type="xs:string" />
- <xs:element name="Expires" msprop:Generator_UserColumnName="Expires" msprop:Generator_ColumnVarNameInTable="columnExpires" msprop:Generator_ColumnPropNameInRow="Expires" msprop:Generator_ColumnPropNameInTable="ExpiresColumn" type="xs:dateTime" />
- <xs:element name="PrivateData" msprop:Generator_UserColumnName="PrivateData" msprop:Generator_ColumnVarNameInTable="columnPrivateData" msprop:Generator_ColumnPropNameInRow="PrivateData" msprop:Generator_ColumnPropNameInTable="PrivateDataColumn" type="xs:base64Binary" />
+ <xs:element name="DistinguishingFactor" msprop:Generator_UserColumnName="DistinguishingFactor" msprop:Generator_ColumnPropNameInRow="DistinguishingFactor" msprop:Generator_ColumnVarNameInTable="columnDistinguishingFactor" msprop:Generator_ColumnPropNameInTable="DistinguishingFactorColumn" type="xs:string" />
+ <xs:element name="Handle" msprop:Generator_UserColumnName="Handle" msprop:Generator_ColumnPropNameInRow="Handle" msprop:Generator_ColumnVarNameInTable="columnHandle" msprop:Generator_ColumnPropNameInTable="HandleColumn" type="xs:string" />
+ <xs:element name="Expires" msprop:Generator_UserColumnName="Expires" msprop:Generator_ColumnPropNameInRow="Expires" msprop:Generator_ColumnVarNameInTable="columnExpires" msprop:Generator_ColumnPropNameInTable="ExpiresColumn" type="xs:dateTime" />
+ <xs:element name="PrivateData" msprop:Generator_UserColumnName="PrivateData" msprop:Generator_ColumnPropNameInRow="PrivateData" msprop:Generator_ColumnVarNameInTable="columnPrivateData" msprop:Generator_ColumnPropNameInTable="PrivateDataColumn" type="xs:base64Binary" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Nonce" msprop:Generator_UserTableName="Nonce" msprop:Generator_RowDeletedName="NonceRowDeleted" msprop:Generator_RowChangedName="NonceRowChanged" msprop:Generator_RowClassName="NonceRow" msprop:Generator_RowChangingName="NonceRowChanging" msprop:Generator_RowEvArgName="NonceRowChangeEvent" msprop:Generator_RowEvHandlerName="NonceRowChangeEventHandler" msprop:Generator_TableClassName="NonceDataTable" msprop:Generator_TableVarName="tableNonce" msprop:Generator_RowDeletingName="NonceRowDeleting" msprop:Generator_TablePropName="Nonce">
<xs:complexType>
<xs:sequence>
- <xs:element name="Context" msprop:Generator_UserColumnName="Context" msprop:Generator_ColumnPropNameInRow="Context" msprop:Generator_ColumnVarNameInTable="columnContext" msprop:Generator_ColumnPropNameInTable="ContextColumn" type="xs:string" />
- <xs:element name="Code" msprop:Generator_UserColumnName="Code" msprop:Generator_ColumnPropNameInRow="Code" msprop:Generator_ColumnVarNameInTable="columnCode" msprop:Generator_ColumnPropNameInTable="CodeColumn" type="xs:string" />
- <xs:element name="Issued" msprop:Generator_UserColumnName="Issued" msprop:Generator_ColumnPropNameInRow="Issued" msprop:Generator_ColumnVarNameInTable="columnIssued" msprop:Generator_ColumnPropNameInTable="IssuedColumn" type="xs:dateTime" />
- <xs:element name="Expires" msprop:Generator_UserColumnName="Expires" msprop:Generator_ColumnVarNameInTable="columnExpires" msprop:Generator_ColumnPropNameInRow="Expires" msprop:Generator_ColumnPropNameInTable="ExpiresColumn" type="xs:dateTime" />
+ <xs:element name="Context" msprop:Generator_UserColumnName="Context" msprop:Generator_ColumnVarNameInTable="columnContext" msprop:Generator_ColumnPropNameInRow="Context" msprop:Generator_ColumnPropNameInTable="ContextColumn" type="xs:string" />
+ <xs:element name="Code" msprop:Generator_UserColumnName="Code" msprop:Generator_ColumnVarNameInTable="columnCode" msprop:Generator_ColumnPropNameInRow="Code" msprop:Generator_ColumnPropNameInTable="CodeColumn" type="xs:string" />
+ <xs:element name="Issued" msprop:Generator_UserColumnName="Issued" msprop:Generator_ColumnVarNameInTable="columnIssued" msprop:Generator_ColumnPropNameInRow="Issued" msprop:Generator_ColumnPropNameInTable="IssuedColumn" type="xs:dateTime" />
+ <xs:element name="Expires" msprop:Generator_UserColumnName="Expires" msprop:Generator_ColumnPropNameInRow="Expires" msprop:Generator_ColumnVarNameInTable="columnExpires" msprop:Generator_ColumnPropNameInTable="ExpiresColumn" type="xs:dateTime" />
</xs:sequence>
</xs:complexType>
</xs:element>
@@ -41,6 +41,7 @@
</xs:unique>
<xs:unique name="Constraint1" msdata:PrimaryKey="true">
<xs:selector xpath=".//mstns:Nonce" />
+ <xs:field xpath="mstns:Issued" />
<xs:field xpath="mstns:Code" />
<xs:field xpath="mstns:Context" />
</xs:unique>
diff --git a/samples/OpenIdRelyingPartyWebForms/Code/CustomStoreDataSet.xss b/samples/OpenIdRelyingPartyWebForms/Code/CustomStoreDataSet.xss
index 483a137..ab8f226 100644
--- a/samples/OpenIdRelyingPartyWebForms/Code/CustomStoreDataSet.xss
+++ b/samples/OpenIdRelyingPartyWebForms/Code/CustomStoreDataSet.xss
@@ -7,7 +7,7 @@
<DiagramLayout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ex:showrelationlabel="False" ViewPortX="0" ViewPortY="0" xmlns:ex="urn:schemas-microsoft-com:xml-msdatasource-layout-extended" xmlns="urn:schemas-microsoft-com:xml-msdatasource-layout">
<Shapes>
<Shape ID="DesignTable:Association" ZOrder="2" X="349" Y="83" Height="105" Width="154" AdapterExpanded="true" DataTableExpanded="true" OldAdapterHeight="0" OldDataTableHeight="0" SplitterPosition="101" />
- <Shape ID="DesignTable:Nonce" ZOrder="1" X="604" Y="86" Height="86" Width="150" AdapterExpanded="true" DataTableExpanded="true" OldAdapterHeight="0" OldDataTableHeight="0" SplitterPosition="82" />
+ <Shape ID="DesignTable:Nonce" ZOrder="1" X="604" Y="86" Height="125" Width="150" AdapterExpanded="true" DataTableExpanded="true" OldAdapterHeight="0" OldDataTableHeight="0" SplitterPosition="121" />
</Shapes>
<Connectors />
</DiagramLayout> \ No newline at end of file
diff --git a/samples/OpenIdRelyingPartyWebForms/Code/CustomStoreDataSet1.Designer.cs b/samples/OpenIdRelyingPartyWebForms/Code/CustomStoreDataSet1.Designer.cs
index 580b1fa..0c0e194 100644
--- a/samples/OpenIdRelyingPartyWebForms/Code/CustomStoreDataSet1.Designer.cs
+++ b/samples/OpenIdRelyingPartyWebForms/Code/CustomStoreDataSet1.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
-// Runtime Version:2.0.50727.4912
+// Runtime Version:2.0.50727.4927
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -678,8 +678,9 @@ namespace OpenIdRelyingPartyWebForms.Code {
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- public NonceRow FindByCodeContext(string Code, string Context) {
+ public NonceRow FindByIssuedCodeContext(System.DateTime Issued, string Code, string Context) {
return ((NonceRow)(this.Rows.Find(new object[] {
+ Issued,
Code,
Context})));
}
@@ -715,6 +716,7 @@ namespace OpenIdRelyingPartyWebForms.Code {
this.columnExpires = new global::System.Data.DataColumn("Expires", typeof(global::System.DateTime), null, global::System.Data.MappingType.Element);
base.Columns.Add(this.columnExpires);
this.Constraints.Add(new global::System.Data.UniqueConstraint("Constraint1", new global::System.Data.DataColumn[] {
+ this.columnIssued,
this.columnCode,
this.columnContext}, true));
this.columnContext.AllowDBNull = false;
diff --git a/samples/OpenIdRelyingPartyWebForms/xrds.aspx b/samples/OpenIdRelyingPartyWebForms/xrds.aspx
index 99a535c..92983fd 100644
--- a/samples/OpenIdRelyingPartyWebForms/xrds.aspx
+++ b/samples/OpenIdRelyingPartyWebForms/xrds.aspx
@@ -21,5 +21,9 @@ is default.aspx.
<URI priority="5"><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/loginPlusOAuth.aspx"))%></URI>
<URI priority="6"><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/loginPlusOAuthSampleOP.aspx"))%></URI>
</Service>
+ <Service>
+ <Type>http://specs.openid.net/extensions/ui/icon</Type>
+ <URI><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/images/dotnetopenid_tiny.gif"))%></URI>
+ </Service>
</XRD>
</xrds:XRDS>
diff --git a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
index b01eea5..27ea955 100644
--- a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
+++ b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
@@ -169,6 +169,7 @@
<Compile Include="Messaging\ErrorUtilitiesTests.cs" />
<Compile Include="Messaging\MessageSerializerTests.cs" />
<Compile Include="Messaging\MultipartPostPartTests.cs" />
+ <Compile Include="Messaging\OutgoingWebResponseTests.cs" />
<Compile Include="Messaging\Reflection\MessageDescriptionTests.cs" />
<Compile Include="Messaging\Reflection\MessageDictionaryTests.cs" />
<Compile Include="Messaging\MessagingTestBase.cs" />
@@ -330,4 +331,4 @@
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\tools\DotNetOpenAuth.Versioning.targets" />
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/DotNetOpenAuth.Test/Messaging/OutgoingWebResponseTests.cs b/src/DotNetOpenAuth.Test/Messaging/OutgoingWebResponseTests.cs
new file mode 100644
index 0000000..35f9259
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/Messaging/OutgoingWebResponseTests.cs
@@ -0,0 +1,36 @@
+//-----------------------------------------------------------------------
+// <copyright file="OutgoingWebResponseTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.Messaging {
+ using System.Net;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class OutgoingWebResponseTests {
+ /// <summary>
+ /// Verifies that setting the Body property correctly converts to a byte stream.
+ /// </summary>
+ [TestMethod]
+ public void SetBodyToByteStream() {
+ var response = new OutgoingWebResponse();
+ string stringValue = "abc";
+ response.Body = stringValue;
+ Assert.AreEqual(stringValue.Length, response.ResponseStream.Length);
+
+ // Verify that the actual bytes are correct.
+ Encoding encoding = new UTF8Encoding(false); // avoid emitting a byte-order mark
+ var expectedBuffer = encoding.GetBytes(stringValue);
+ var actualBuffer = new byte[stringValue.Length];
+ Assert.AreEqual(stringValue.Length, response.ResponseStream.Read(actualBuffer, 0, stringValue.Length));
+ CollectionAssert.AreEqual(expectedBuffer, actualBuffer);
+
+ // Verify that the header was set correctly.
+ Assert.AreEqual(encoding.HeaderName, response.Headers[HttpResponseHeader.ContentEncoding]);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/Mocks/MockRealm.cs b/src/DotNetOpenAuth.Test/Mocks/MockRealm.cs
index f89f119..dd17735 100644
--- a/src/DotNetOpenAuth.Test/Mocks/MockRealm.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/MockRealm.cs
@@ -37,7 +37,7 @@ namespace DotNetOpenAuth.Test.Mocks {
/// <returns>
/// The details of the endpoints if found, otherwise null.
/// </returns>
- internal override IEnumerable<RelyingPartyEndpointDescription> Discover(IDirectWebRequestHandler requestHandler, bool allowRedirects) {
+ internal override IEnumerable<RelyingPartyEndpointDescription> DiscoverReturnToEndpoints(IDirectWebRequestHandler requestHandler, bool allowRedirects) {
return this.relyingPartyDescriptions;
}
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs
index f69fc8b..7a60a32 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs
@@ -16,15 +16,30 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.UI {
public void Defaults() {
UIRequest request = new UIRequest();
Assert.AreEqual("popup", request.Mode);
- Assert.AreEqual(CultureInfo.CurrentUICulture, request.LanguagePreference);
+ Assert.AreEqual(1, request.LanguagePreference.Length);
+ Assert.AreEqual(CultureInfo.CurrentUICulture, request.LanguagePreference[0]);
}
[TestMethod]
- public void LanguagePreferenceEncoding() {
+ public void LanguagePreferenceEncodingDecoding() {
var request = new UIRequest();
- request.LanguagePreference = new CultureInfo("en-US");
MessageDictionary dictionary = this.MessageDescriptions.GetAccessor(request);
+
+ request.LanguagePreference = new[] { new CultureInfo("en-US") };
Assert.AreEqual("en-US", dictionary["lang"]);
+
+ request.LanguagePreference = new[] { new CultureInfo("en-US"), new CultureInfo("es-ES") };
+ Assert.AreEqual("en-US,es-ES", dictionary["lang"]);
+
+ // Now test decoding
+ dictionary["lang"] = "en-US";
+ Assert.AreEqual(1, request.LanguagePreference.Length);
+ Assert.AreEqual(new CultureInfo("en-US"), request.LanguagePreference[0]);
+
+ dictionary["lang"] = "en-US,es-ES";
+ Assert.AreEqual(2, request.LanguagePreference.Length);
+ Assert.AreEqual(new CultureInfo("en-US"), request.LanguagePreference[0]);
+ Assert.AreEqual(new CultureInfo("es-ES"), request.LanguagePreference[1]);
}
[TestMethod]
diff --git a/src/DotNetOpenAuth.sln b/src/DotNetOpenAuth.sln
index 54b81f3..2c15c50 100644
--- a/src/DotNetOpenAuth.sln
+++ b/src/DotNetOpenAuth.sln
@@ -7,7 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.Test", "DotN
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{20B5E173-C3C4-49F8-BD25-E69044075B4D}"
ProjectSection(SolutionItems) = preProject
- dotnetopenauth.vsmdi = dotnetopenauth.vsmdi
+ DotNetOpenAuth.vsmdi = DotNetOpenAuth.vsmdi
+ ..\LICENSE.txt = ..\LICENSE.txt
LocalTestRun.testrunconfig = LocalTestRun.testrunconfig
..\doc\README.Bin.html = ..\doc\README.Bin.html
..\doc\README.html = ..\doc\README.html
diff --git a/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd b/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd
index ea32f4c..a214053 100644
--- a/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd
+++ b/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd
@@ -197,6 +197,15 @@
</xs:choice>
<xs:attribute name="requireSsl" type="xs:boolean" default="false" />
<xs:attribute name="protectDownlevelReplayAttacks" type="xs:boolean" />
+ <xs:attribute name="unsolicitedAssertionVerification">
+ <xs:simpleType>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="RequireSuccess" />
+ <xs:enumeration value="LogWarningOnFailure" />
+ <xs:enumeration value="NeverVerify" />
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
<xs:attribute name="minimumHashBitLength" type="xs:int" />
<xs:attribute name="maximumHashBitLength" type="xs:int" />
</xs:complexType>
diff --git a/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs
index 457955c..cd927f2 100644
--- a/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs
@@ -41,6 +41,11 @@ namespace DotNetOpenAuth.Configuration {
private const string RequireSslConfigName = "requireSsl";
/// <summary>
+ /// Gets the name of the @unsolicitedAssertionVerification attribute.
+ /// </summary>
+ private const string UnsolicitedAssertionVerificationConfigName = "unsolicitedAssertionVerification";
+
+ /// <summary>
/// Initializes a new instance of the <see cref="OpenIdProviderSecuritySettingsElement"/> class.
/// </summary>
public OpenIdProviderSecuritySettingsElement() {
@@ -84,6 +89,17 @@ namespace DotNetOpenAuth.Configuration {
}
/// <summary>
+ /// Gets or sets the level of verification a Provider performs on an identifier before
+ /// sending an unsolicited assertion for it.
+ /// </summary>
+ /// <value>The default value is <see cref="UnsolicitedAssertionVerificationLevel.Always"/>.</value>
+ [ConfigurationProperty(UnsolicitedAssertionVerificationConfigName, DefaultValue = ProviderSecuritySettings.UnsolicitedAssertionVerificationDefault)]
+ public ProviderSecuritySettings.UnsolicitedAssertionVerificationLevel UnsolicitedAssertionVerification {
+ get { return (ProviderSecuritySettings.UnsolicitedAssertionVerificationLevel)this[UnsolicitedAssertionVerificationConfigName]; }
+ set { this[UnsolicitedAssertionVerificationConfigName] = value; }
+ }
+
+ /// <summary>
/// Gets or sets the configured lifetimes of the various association types.
/// </summary>
[ConfigurationProperty(AssociationsConfigName, IsDefaultCollection = false)]
@@ -109,6 +125,7 @@ namespace DotNetOpenAuth.Configuration {
settings.MinimumHashBitLength = this.MinimumHashBitLength;
settings.MaximumHashBitLength = this.MaximumHashBitLength;
settings.ProtectDownlevelReplayAttacks = this.ProtectDownlevelReplayAttacks;
+ settings.UnsolicitedAssertionVerification = this.UnsolicitedAssertionVerification;
foreach (AssociationTypeElement element in this.AssociationLifetimes) {
Contract.Assume(element != null);
settings.AssociationLifetimes.Add(element.AssociationType, element.MaximumLifetime);
diff --git a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs
index f4b1633..16b4546 100644
--- a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs
+++ b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs
@@ -374,7 +374,16 @@ namespace DotNetOpenAuth.Messaging {
WebHeaderCollection headers = new WebHeaderCollection();
foreach (string key in pairs) {
- headers.Add(key, pairs[key]);
+ try {
+ headers.Add(key, pairs[key]);
+ } catch (ArgumentException ex) {
+ Logger.Messaging.WarnFormat(
+ "{0} thrown when trying to add web header \"{1}: {2}\". {3}",
+ ex.GetType().Name,
+ key,
+ pairs[key],
+ ex.Message);
+ }
}
return headers;
diff --git a/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs b/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs
index 9ad228e..70b1032 100644
--- a/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs
+++ b/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs
@@ -104,7 +104,7 @@ namespace DotNetOpenAuth.Messaging {
/// This can be different from the <see cref="RequestUri"/> in cases of
/// redirection during the request.
/// </remarks>
- public Uri FinalUri { get; private set; }
+ public Uri FinalUri { get; internal set; }
/// <summary>
/// Gets the headers that must be included in the response to the user agent.
diff --git a/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs b/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs
index 91853fe..f6a930c 100644
--- a/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs
+++ b/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs
@@ -30,6 +30,11 @@ namespace DotNetOpenAuth.Messaging {
/// </remarks>
public class OutgoingWebResponse {
/// <summary>
+ /// The encoder to use for serializing the response body.
+ /// </summary>
+ private static Encoding bodyStringEncoder = new UTF8Encoding(false);
+
+ /// <summary>
/// Initializes a new instance of the <see cref="OutgoingWebResponse"/> class.
/// </summary>
internal OutgoingWebResponse() {
@@ -206,10 +211,9 @@ namespace DotNetOpenAuth.Messaging {
return;
}
- Encoding encoding = Encoding.UTF8;
- this.Headers[HttpResponseHeader.ContentEncoding] = encoding.HeaderName;
+ this.Headers[HttpResponseHeader.ContentEncoding] = bodyStringEncoder.HeaderName;
this.ResponseStream = new MemoryStream();
- StreamWriter writer = new StreamWriter(this.ResponseStream, encoding);
+ StreamWriter writer = new StreamWriter(this.ResponseStream, bodyStringEncoder);
writer.Write(body);
writer.Flush();
this.ResponseStream.Seek(0, SeekOrigin.Begin);
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
index 7fe8222..32d2fec 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
@@ -11,6 +11,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
+ using System.Linq;
using System.Net.Security;
using System.Reflection;
using System.Xml;
@@ -96,6 +97,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
Map<Identifier>(id => id.ToString(), safeIdentfier);
Map<bool>(value => value.ToString().ToLowerInvariant(), safeBool);
Map<CultureInfo>(c => c.Name, str => new CultureInfo(str));
+ Map<CultureInfo[]>(cs => string.Join(",", cs.Select(c => c.Name).ToArray()), str => str.Split(',').Select(s => new CultureInfo(s)).ToArray());
}
/// <summary>
diff --git a/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs
index a5cbdab..3ea1bf2 100644
--- a/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs
+++ b/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs
@@ -258,6 +258,15 @@ namespace DotNetOpenAuth.Messaging {
Uri redirectUri = new Uri(response.FinalUri, response.Headers[HttpResponseHeader.Location]);
request = request.Clone(redirectUri);
} else {
+ if (response.FinalUri != request.RequestUri) {
+ // Since we don't automatically follow redirects, there's only one scenario where this
+ // can happen: when the server sends a (non-redirecting) Content-Location header in the response.
+ // It's imperative that we do not trust that header though, so coerce the FinalUri to be
+ // what we just requested.
+ Logger.Http.WarnFormat("The response from {0} included an HTTP header indicating it's the same as {1}, but it's not a redirect so we won't trust that.", request.RequestUri, response.FinalUri);
+ response.FinalUri = request.RequestUri;
+ }
+
return response;
}
}
@@ -454,12 +463,14 @@ namespace DotNetOpenAuth.Messaging {
request.ReadWriteTimeout = (int)this.ReadWriteTimeout.TotalMilliseconds;
request.Timeout = (int)this.Timeout.TotalMilliseconds;
request.KeepAlive = false;
-
- // If SSL is required throughout, we cannot allow auto redirects because
- // it may include a pass through an unprotected HTTP request.
- // We have to follow redirects manually.
- request.AllowAutoRedirect = false;
}
+
+ // If SSL is required throughout, we cannot allow auto redirects because
+ // it may include a pass through an unprotected HTTP request.
+ // We have to follow redirects manually.
+ // It also allows us to ignore HttpWebResponse.FinalUri since that can be affected by
+ // the Content-Location header and open security holes.
+ request.AllowAutoRedirect = false;
}
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIConstants.cs b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIConstants.cs
index 963b301..1cc920a 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIConstants.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIConstants.cs
@@ -24,5 +24,11 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
/// specifying the user's preferred language through the UI extension.
/// </summary>
internal const string LangPrefSupported = "http://specs.openid.net/extensions/ui/1.0/lang-pref";
+
+ /// <summary>
+ /// The Type URI that appears in the XRDS document when the OP supports the RP
+ /// specifying the icon for the OP to display during authentication through the UI extension.
+ /// </summary>
+ internal const string IconSupported = "http://specs.openid.net/extensions/ui/1.0/icon";
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs
index d67d932..bee675d 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs
@@ -8,11 +8,14 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Messages;
+ using DotNetOpenAuth.OpenId.Provider;
using DotNetOpenAuth.OpenId.RelyingParty;
+ using DotNetOpenAuth.Xrds;
/// <summary>
/// OpenID User Interface extension 1.0 request message.
@@ -25,7 +28,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
/// <see cref="UIModes.Popup"/>. </para>
/// <para>An RP may determine whether an arbitrary OP supports this extension (and thereby determine
/// whether to use a standard full window redirect or a popup) via the
- /// <see cref="IProviderEndpoint.IsExtensionSupported"/> method on the <see cref="IAuthenticationRequest.Provider"/>
+ /// <see cref="IProviderEndpoint.IsExtensionSupported"/> method on the <see cref="DotNetOpenAuth.OpenId.RelyingParty.IAuthenticationRequest.Provider"/>
/// object.</para>
/// </remarks>
public sealed class UIRequest : IOpenIdMessageExtension, IMessageWithEvents {
@@ -46,6 +49,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
private static readonly string[] additionalTypeUris = new string[] {
UIConstants.LangPrefSupported,
UIConstants.PopupSupported,
+ UIConstants.IconSupported,
};
/// <summary>
@@ -57,18 +61,18 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
/// Initializes a new instance of the <see cref="UIRequest"/> class.
/// </summary>
public UIRequest() {
- this.LanguagePreference = CultureInfo.CurrentUICulture;
+ this.LanguagePreference = new[] { CultureInfo.CurrentUICulture };
}
/// <summary>
- /// Gets or sets the user's preferred language.
+ /// Gets or sets the list of user's preferred languages, sorted in decreasing preferred order.
/// </summary>
/// <value>The default is the <see cref="CultureInfo.CurrentUICulture"/> of the thread that created this instance.</value>
/// <remarks>
- /// The user's preferred language, reusing the Language Tag format used by the [Language Preference Attribute] (axschema.org, “Language Preference Attribute,” .) for [OpenID Attribute Exchange] (Hardt, D., Bufu, J., and J. Hoyt, “OpenID Attribute Exchange 1.0,” .) and defined in [RFC4646] (Phillips, A. and M. Davis, “Tags for Identifying Languages,” .). For example "en-US" represents the English language as spoken in the United States, and "fr-CA" represents the French language spoken in Canada.
+ /// The user's preferred languages as a [BCP 47] language priority list, represented as a comma-separated list of BCP 47 basic language ranges in descending priority order. For instance, the value "fr-CA,fr-FR,en-CA" represents the preference for French spoken in Canada, French spoken in France, followed by English spoken in Canada.
/// </remarks>
[MessagePart("lang", AllowEmpty = false)]
- public CultureInfo LanguagePreference { get; set; }
+ public CultureInfo[] LanguagePreference { get; set; }
/// <summary>
/// Gets the style of UI that the RP is hosting the OP's authentication page in.
@@ -87,7 +91,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
/// <remarks>
/// By default, the Provider displays the relying party's favicon.ico.
/// </remarks>
- [MessagePart("popup", AllowEmpty = false, IsRequired = false)]
+ [MessagePart("icon", AllowEmpty = false, IsRequired = false)]
public bool? Icon { get; set; }
#region IOpenIdMessageExtension Members
@@ -126,7 +130,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
#endregion
- #region IMessage Members
+ #region IMessage Properties
/// <summary>
/// Gets the version of the protocol or extension this message is prepared to implement.
@@ -149,6 +153,72 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
get { return this.extraData; }
}
+ #endregion
+
+ /// <summary>
+ /// Gets the URL of the RP icon for the OP to display.
+ /// </summary>
+ /// <param name="realm">The realm of the RP where the authentication request originated.</param>
+ /// <param name="webRequestHandler">The web request handler to use for discovery.
+ /// Usually available via <see cref="Channel.WebRequestHandler">OpenIdProvider.Channel.WebRequestHandler</see>.</param>
+ /// <returns>
+ /// A sequence of the RP's icons it has available for the Provider to display, in decreasing preferred order.
+ /// </returns>
+ /// <value>The icon URL.</value>
+ /// <remarks>
+ /// This property is automatically set for the OP with the result of RP discovery.
+ /// RPs should set this value by including an entry such as this in their XRDS document.
+ /// <example>
+ /// &lt;Service xmlns="xri://$xrd*($v*2.0)"&gt;
+ /// &lt;Type&gt;http://specs.openid.net/extensions/ui/icon&lt;/Type&gt;
+ /// &lt;URI&gt;http://consumer.example.com/images/image.jpg&lt;/URI&gt;
+ /// &lt;/Service&gt;
+ /// </example>
+ /// </remarks>
+ public static IEnumerable<Uri> GetRelyingPartyIconUrls(Realm realm, IDirectWebRequestHandler webRequestHandler) {
+ Contract.Requires(realm != null);
+ Contract.Requires(webRequestHandler != null);
+ ErrorUtilities.VerifyArgumentNotNull(realm, "realm");
+ ErrorUtilities.VerifyArgumentNotNull(webRequestHandler, "webRequestHandler");
+
+ XrdsDocument xrds = realm.Discover(webRequestHandler, false);
+ if (xrds == null) {
+ return Enumerable.Empty<Uri>();
+ } else {
+ return xrds.FindRelyingPartyIcons();
+ }
+ }
+
+ /// <summary>
+ /// Gets the URL of the RP icon for the OP to display.
+ /// </summary>
+ /// <param name="realm">The realm of the RP where the authentication request originated.</param>
+ /// <param name="provider">The Provider instance used to obtain the authentication request.</param>
+ /// <returns>
+ /// A sequence of the RP's icons it has available for the Provider to display, in decreasing preferred order.
+ /// </returns>
+ /// <value>The icon URL.</value>
+ /// <remarks>
+ /// This property is automatically set for the OP with the result of RP discovery.
+ /// RPs should set this value by including an entry such as this in their XRDS document.
+ /// <example>
+ /// &lt;Service xmlns="xri://$xrd*($v*2.0)"&gt;
+ /// &lt;Type&gt;http://specs.openid.net/extensions/ui/icon&lt;/Type&gt;
+ /// &lt;URI&gt;http://consumer.example.com/images/image.jpg&lt;/URI&gt;
+ /// &lt;/Service&gt;
+ /// </example>
+ /// </remarks>
+ public static IEnumerable<Uri> GetRelyingPartyIconUrls(Realm realm, OpenIdProvider provider) {
+ Contract.Requires(realm != null);
+ Contract.Requires(provider != null);
+ ErrorUtilities.VerifyArgumentNotNull(realm, "realm");
+ ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
+
+ return GetRelyingPartyIconUrls(realm, provider.Channel.WebRequestHandler);
+ }
+
+ #region IMessage methods
+
/// <summary>
/// Checks the message state for conformity to the protocol specification
/// and throws an exception if the message is invalid.
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdXrdsHelper.cs b/src/DotNetOpenAuth/OpenId/OpenIdXrdsHelper.cs
index fd9e8d8..664a127 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdXrdsHelper.cs
+++ b/src/DotNetOpenAuth/OpenId/OpenIdXrdsHelper.cs
@@ -33,6 +33,19 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
+ /// Finds the icons the relying party wants an OP to display as part of authentication,
+ /// per the UI extension spec.
+ /// </summary>
+ /// <param name="xrds">The XrdsDocument to search.</param>
+ /// <returns>A sequence of the icon URLs in preferred order.</returns>
+ internal static IEnumerable<Uri> FindRelyingPartyIcons(this XrdsDocument xrds) {
+ return from xrd in xrds.XrdElements
+ from service in xrd.OpenIdRelyingPartyIcons
+ from uri in service.UriElements
+ select uri.Uri;
+ }
+
+ /// <summary>
/// Creates the service endpoints described in this document, useful for requesting
/// authentication of one of the OpenID Providers that result from it.
/// </summary>
diff --git a/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs
index 7c6c4c1..38d1094 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs
@@ -137,7 +137,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
return RelyingPartyDiscoveryResult.NoServiceDocument;
}
- var returnToEndpoints = this.Realm.Discover(provider.Channel.WebRequestHandler, false);
+ var returnToEndpoints = this.Realm.DiscoverReturnToEndpoints(provider.Channel.WebRequestHandler, false);
if (returnToEndpoints == null) {
return RelyingPartyDiscoveryResult.NoServiceDocument;
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs b/src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs
index ade0d8e..01b4ac8 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs
@@ -12,12 +12,8 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <summary>
/// Applies a custom security policy to certain OpenID security settings and behaviors.
/// </summary>
- /// <remarks>
- /// BEFORE MARKING THIS INTERFACE PUBLIC: it's very important that we shift the methods to be channel-level
- /// rather than facade class level and for the OpenIdChannel to be the one to invoke these methods.
- /// </remarks>
[ContractClass(typeof(IProviderBehaviorContract))]
- internal interface IProviderBehavior {
+ public interface IProviderBehavior {
/// <summary>
/// Applies a well known set of security requirements to a default set of security settings.
/// </summary>
diff --git a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
index 2af6830..f141834 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
@@ -149,7 +149,11 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <summary>
/// Gets a list of custom behaviors to apply to OpenID actions.
/// </summary>
- internal ICollection<IProviderBehavior> Behaviors {
+ /// <remarks>
+ /// Adding behaviors can impact the security settings of the <see cref="OpenIdProvider"/>
+ /// in ways that subsequently removing the behaviors will not reverse.
+ /// </remarks>
+ public ICollection<IProviderBehavior> Behaviors {
get { return this.behaviors; }
}
@@ -359,21 +363,27 @@ namespace DotNetOpenAuth.OpenId.Provider {
// is authorized to send an assertion for the given claimed identifier,
// do due diligence by performing our own discovery on the claimed identifier
// and make sure that it is tied to this OP and OP local identifier.
- var serviceEndpoint = DotNetOpenAuth.OpenId.RelyingParty.ServiceEndpoint.CreateForClaimedIdentifier(claimedIdentifier, localIdentifier, new ProviderEndpointDescription(providerEndpoint, Protocol.Default.Version), null, null);
- var discoveredEndpoints = claimedIdentifier.Discover(this.WebRequestHandler);
- if (!discoveredEndpoints.Contains(serviceEndpoint)) {
- Logger.OpenId.DebugFormat(
- "Failed to send unsolicited assertion for {0} because its discovered services did not include this endpoint: {1}{2}{1}Discovered endpoints: {1}{3}",
- claimedIdentifier,
- Environment.NewLine,
- serviceEndpoint,
- discoveredEndpoints.ToStringDeferred(true));
- ErrorUtilities.ThrowProtocol(OpenIdStrings.UnsolicitedAssertionForUnrelatedClaimedIdentifier, claimedIdentifier);
+ if (this.SecuritySettings.UnsolicitedAssertionVerification != ProviderSecuritySettings.UnsolicitedAssertionVerificationLevel.NeverVerify) {
+ var serviceEndpoint = DotNetOpenAuth.OpenId.RelyingParty.ServiceEndpoint.CreateForClaimedIdentifier(claimedIdentifier, localIdentifier, new ProviderEndpointDescription(providerEndpoint, Protocol.Default.Version), null, null);
+ var discoveredEndpoints = claimedIdentifier.Discover(this.WebRequestHandler);
+ if (!discoveredEndpoints.Contains(serviceEndpoint)) {
+ Logger.OpenId.WarnFormat(
+ "Failed to send unsolicited assertion for {0} because its discovered services did not include this endpoint: {1}{2}{1}Discovered endpoints: {1}{3}",
+ claimedIdentifier,
+ Environment.NewLine,
+ serviceEndpoint,
+ discoveredEndpoints.ToStringDeferred(true));
+
+ // Only FAIL if the setting is set for it.
+ if (this.securitySettings.UnsolicitedAssertionVerification == ProviderSecuritySettings.UnsolicitedAssertionVerificationLevel.RequireSuccess) {
+ ErrorUtilities.ThrowProtocol(OpenIdStrings.UnsolicitedAssertionForUnrelatedClaimedIdentifier, claimedIdentifier);
+ }
+ }
}
Logger.OpenId.InfoFormat("Preparing unsolicited assertion for {0}", claimedIdentifier);
RelyingPartyEndpointDescription returnToEndpoint = null;
- var returnToEndpoints = relyingParty.Discover(this.WebRequestHandler, true);
+ var returnToEndpoints = relyingParty.DiscoverReturnToEndpoints(this.WebRequestHandler, true);
if (returnToEndpoints != null) {
returnToEndpoint = returnToEndpoints.FirstOrDefault();
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs b/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs
index 876e412..ddc10d2 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs
@@ -28,6 +28,11 @@ namespace DotNetOpenAuth.OpenId.Provider {
internal const bool SignOutgoingExtensionsDefault = true;
/// <summary>
+ /// The default value for the <see cref="UnsolicitedAssertionVerification"/> property.
+ /// </summary>
+ internal const UnsolicitedAssertionVerificationLevel UnsolicitedAssertionVerificationDefault = UnsolicitedAssertionVerificationLevel.RequireSuccess;
+
+ /// <summary>
/// The subset of association types and their customized lifetimes.
/// </summary>
private IDictionary<string, TimeSpan> associationLifetimes = new Dictionary<string, TimeSpan>();
@@ -39,6 +44,37 @@ namespace DotNetOpenAuth.OpenId.Provider {
: base(true) {
this.SignOutgoingExtensions = SignOutgoingExtensionsDefault;
this.ProtectDownlevelReplayAttacks = ProtectDownlevelReplayAttacksDefault;
+ this.UnsolicitedAssertionVerification = UnsolicitedAssertionVerificationDefault;
+ }
+
+ /// <summary>
+ /// The behavior a Provider takes when verifying that it is authoritative for an
+ /// identifier it is about to send an unsolicited assertion for.
+ /// </summary>
+ public enum UnsolicitedAssertionVerificationLevel {
+ /// <summary>
+ /// Always verify that the Provider is authoritative for an identifier before
+ /// sending an unsolicited assertion for it and fail if it is not.
+ /// </summary>
+ RequireSuccess,
+
+ /// <summary>
+ /// Always check that the Provider is authoritative for an identifier before
+ /// sending an unsolicited assertion for it, but only log failures, and proceed
+ /// to send the unsolicited assertion.
+ /// </summary>
+ LogWarningOnFailure,
+
+ /// <summary>
+ /// Never verify that the Provider is authoritative for an identifier before
+ /// sending an unsolicited assertion for it.
+ /// </summary>
+ /// <remarks>
+ /// This setting is useful for web servers that refuse to allow a Provider to
+ /// introspectively perform an HTTP GET on itself, when sending unsolicited assertions
+ /// for identifiers that the OP controls.
+ /// </remarks>
+ NeverVerify,
}
/// <summary>
@@ -57,6 +93,13 @@ namespace DotNetOpenAuth.OpenId.Provider {
public bool RequireSsl { get; set; }
/// <summary>
+ /// Gets or sets the level of verification a Provider performs on an identifier before
+ /// sending an unsolicited assertion for it.
+ /// </summary>
+ /// <value>The default value is <see cref="UnsolicitedAssertionVerificationLevel.Always"/>.</value>
+ public UnsolicitedAssertionVerificationLevel UnsolicitedAssertionVerification { get; set; }
+
+ /// <summary>
/// Gets or sets a value indicating whether OpenID 1.x relying parties that may not be
/// protecting their users from replay attacks are protected from
/// replay attacks by this provider.
@@ -101,6 +144,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
securitySettings.ProtectDownlevelReplayAttacks = this.ProtectDownlevelReplayAttacks;
securitySettings.RequireSsl = this.RequireSsl;
securitySettings.SignOutgoingExtensions = this.SignOutgoingExtensions;
+ securitySettings.UnsolicitedAssertionVerification = this.UnsolicitedAssertionVerification;
return securitySettings;
}
diff --git a/src/DotNetOpenAuth/OpenId/Realm.cs b/src/DotNetOpenAuth/OpenId/Realm.cs
index 770aca0..fb0fbfb 100644
--- a/src/DotNetOpenAuth/OpenId/Realm.cs
+++ b/src/DotNetOpenAuth/OpenId/Realm.cs
@@ -381,7 +381,26 @@ namespace DotNetOpenAuth.OpenId {
/// <returns>
/// The details of the endpoints if found; or <c>null</c> if no service document was discovered.
/// </returns>
- internal virtual IEnumerable<RelyingPartyEndpointDescription> Discover(IDirectWebRequestHandler requestHandler, bool allowRedirects) {
+ internal virtual IEnumerable<RelyingPartyEndpointDescription> DiscoverReturnToEndpoints(IDirectWebRequestHandler requestHandler, bool allowRedirects) {
+ XrdsDocument xrds = this.Discover(requestHandler, allowRedirects);
+ if (xrds != null) {
+ return xrds.FindRelyingPartyReceivingEndpoints();
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Searches for an XRDS document at the realm URL.
+ /// </summary>
+ /// <param name="requestHandler">The mechanism to use for sending HTTP requests.</param>
+ /// <param name="allowRedirects">Whether redirects may be followed when discovering the Realm.
+ /// This may be true when creating an unsolicited assertion, but must be
+ /// false when performing return URL verification per 2.0 spec section 9.2.1.</param>
+ /// <returns>
+ /// The XRDS document if found; or <c>null</c> if no service document was discovered.
+ /// </returns>
+ internal virtual XrdsDocument Discover(IDirectWebRequestHandler requestHandler, bool allowRedirects) {
// Attempt YADIS discovery
DiscoveryResult yadisResult = Yadis.Discover(requestHandler, this.UriWithWildcardChangedToWww, false);
if (yadisResult != null) {
@@ -389,8 +408,7 @@ namespace DotNetOpenAuth.OpenId {
ErrorUtilities.VerifyProtocol(allowRedirects || yadisResult.NormalizedUri == yadisResult.RequestUri, OpenIdStrings.RealmCausedRedirectUponDiscovery, yadisResult.RequestUri);
if (yadisResult.IsXrds) {
try {
- XrdsDocument xrds = new XrdsDocument(yadisResult.ResponseText);
- return xrds.FindRelyingPartyReceivingEndpoints();
+ return new XrdsDocument(yadisResult.ResponseText);
} catch (XmlException ex) {
throw ErrorUtilities.Wrap(ex, XrdsStrings.InvalidXRDSDocument);
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs
index 199368f..300a15f 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs
@@ -11,12 +11,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Applies a custom security policy to certain OpenID security settings and behaviors.
/// </summary>
- /// <remarks>
- /// BEFORE MARKING THIS INTERFACE PUBLIC: it's very important that we shift the methods to be channel-level
- /// rather than facade class level and for the OpenIdChannel to be the one to invoke these methods.
- /// </remarks>
[ContractClass(typeof(IRelyingPartyBehaviorContract))]
- internal interface IRelyingPartyBehavior {
+ public interface IRelyingPartyBehavior {
/// <summary>
/// Applies a well known set of security requirements to a default set of security settings.
/// </summary>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs
index 13d02a3..dbf6944 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs
@@ -116,8 +116,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <param name="eventArgument">A <see cref="T:System.String"/> that represents an optional event argument to be passed to the event handler.</param>
protected override void RaisePostBackEvent(string eventArgument) {
if (!this.PrecreateRequest) {
- IAuthenticationRequest request = this.CreateRequests().FirstOrDefault();
- request.RedirectToProvider();
+ try {
+ IAuthenticationRequest request = this.CreateRequests().First();
+ request.RedirectToProvider();
+ } catch (InvalidOperationException ex) {
+ throw ErrorUtilities.Wrap(ex, OpenIdStrings.OpenIdEndpointNotFound);
+ }
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
index f78420e..2808d39 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
@@ -218,7 +218,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets a list of custom behaviors to apply to OpenID actions.
/// </summary>
- internal ICollection<IRelyingPartyBehavior> Behaviors {
+ /// <remarks>
+ /// Adding behaviors can impact the security settings of this <see cref="OpenIdRelyingParty"/>
+ /// instance in ways that subsequently removing the behaviors will not reverse.
+ /// </remarks>
+ public ICollection<IRelyingPartyBehavior> Behaviors {
get { return this.behaviors; }
}
diff --git a/src/DotNetOpenAuth/Xrds/XrdElement.cs b/src/DotNetOpenAuth/Xrds/XrdElement.cs
index 72c5078..a8cc145 100644
--- a/src/DotNetOpenAuth/Xrds/XrdElement.cs
+++ b/src/DotNetOpenAuth/Xrds/XrdElement.cs
@@ -96,6 +96,13 @@ namespace DotNetOpenAuth.Xrds {
}
/// <summary>
+ /// Gets the services that would be discoverable at an RP for the UI extension icon.
+ /// </summary>
+ public IEnumerable<ServiceElement> OpenIdRelyingPartyIcons {
+ get { return this.SearchForServiceTypeUris(p => "http://specs.openid.net/extensions/ui/icon"); }
+ }
+
+ /// <summary>
/// Gets an enumeration of all Service/URI elements, sorted in priority order.
/// </summary>
public IEnumerable<UriElement> ServiceUris {