diff options
26 files changed, 404 insertions, 50 deletions
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj index 976a325..83d3527 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj +++ b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj @@ -60,6 +60,8 @@ <Compile Include="CustomExtensions\AcmeResponse.cs" /> <Compile Include="GoogleConsumer.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="Provider\AnonymousIdentifierProviderBase.cs" /> + <Compile Include="Provider\AuthenticationRequestExtensions.cs" /> <Compile Include="TwitterConsumer.cs" /> <Compile Include="Util.cs" /> </ItemGroup> diff --git a/samples/DotNetOpenAuth.ApplicationBlock/Provider/AnonymousIdentifierProviderBase.cs b/samples/DotNetOpenAuth.ApplicationBlock/Provider/AnonymousIdentifierProviderBase.cs new file mode 100644 index 0000000..1df7267 --- /dev/null +++ b/samples/DotNetOpenAuth.ApplicationBlock/Provider/AnonymousIdentifierProviderBase.cs @@ -0,0 +1,122 @@ +//----------------------------------------------------------------------- +// <copyright file="AnonymousIdentifierProviderBase.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.ApplicationBlock.Provider { + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Security.Cryptography; + using System.Text; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId; + + public abstract class AnonymousIdentifierProviderBase { + private int newSaltLength = 20; + + /// <summary> + /// Initializes a new instance of the <see cref="AnonymousIdentifierProviderBase"/> class. + /// </summary> + /// <param name="baseIdentifier">The base URI on which to append the anonymous part.</param> + public AnonymousIdentifierProviderBase(Uri baseIdentifier) { + if (baseIdentifier == null) { + throw new ArgumentNullException("baseIdentifier"); + } + + this.Hasher = HashAlgorithm.Create("SHA256"); + this.Encoder = Encoding.UTF8; + this.BaseIdentifier = baseIdentifier; + } + + public Uri BaseIdentifier { get; private set; } + + protected HashAlgorithm Hasher { get; private set; } + + protected Encoding Encoder { get; private set; } + + protected int NewSaltLength { + get { + return this.newSaltLength; + } + + set { + if (value <= 0) { + throw new ArgumentOutOfRangeException("value"); + } + + this.newSaltLength = value; + } + } + + #region IAnonymousIdentifierProvider Members + + public Uri GetAnonymousIdentifier(Identifier localIdentifier, Realm relyingPartyRealm) { + byte[] salt = this.GetHashSaltForLocalIdentifier(localIdentifier); + string valueToHash = localIdentifier + "#" + (relyingPartyRealm ?? string.Empty); + byte[] valueAsBytes = this.Encoder.GetBytes(valueToHash); + byte[] bytesToHash = new byte[valueAsBytes.Length + salt.Length]; + valueAsBytes.CopyTo(bytesToHash, 0); + salt.CopyTo(bytesToHash, valueAsBytes.Length); + byte[] hash = this.Hasher.ComputeHash(bytesToHash); + string base64Hash = Convert.ToBase64String(hash); + Uri anonymousIdentifier = this.AppendIdentifiers(this.BaseIdentifier, base64Hash); + return anonymousIdentifier; + } + + #endregion + + protected virtual byte[] GetNewSalt() { + // We COULD use a crypto random function, but for a salt it seems overkill. + return Util.GetNonCryptoRandomData(this.NewSaltLength); + } + + protected Uri AppendIdentifiers(Uri baseIdentifier, string uriHash) { + if (baseIdentifier == null) { + throw new ArgumentNullException("baseIdentifier"); + } + if (String.IsNullOrEmpty(uriHash)) { + throw new ArgumentNullException("uriHash"); + } + + if (string.IsNullOrEmpty(baseIdentifier.Query)) { + // The uriHash will appear on the path itself. + string pathEncoded = Uri.EscapeUriString(uriHash.Replace('/', '_')); + return new Uri(baseIdentifier, pathEncoded); + } else { + // The uriHash will appear on the query string. + string dataEncoded = Uri.EscapeDataString(uriHash); + return new Uri(baseIdentifier + dataEncoded); + } + } + + /// <summary> + /// Gets the salt to use for generating an anonymous identifier for a given OP local identifier. + /// </summary> + /// <param name="localIdentifier">The OP local identifier.</param> + /// <returns>The salt to use in the hash.</returns> + /// <remarks> + /// It is important that this method always return the same value for a given + /// <paramref name="localIdentifier"/>. + /// New salts can be generated for local identifiers without previously assigned salt + /// values by calling <see cref="GetNewSalt"/> or by a custom method. + /// </remarks> + protected abstract byte[] GetHashSaltForLocalIdentifier(Identifier localIdentifier); + +#if CONTRACTS_FULL + /// <summary> + /// Verifies conditions that should be true for any valid state of this object. + /// </summary> + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")] + [ContractInvariantMethod] + protected void ObjectInvariant() { + Contract.Invariant(this.Hasher != null); + Contract.Invariant(this.Encoder != null); + Contract.Invariant(this.BaseIdentifier != null); + Contract.Invariant(this.NewHashLength > 0); + } +#endif + } +} diff --git a/samples/DotNetOpenAuth.ApplicationBlock/Provider/AuthenticationRequestExtensions.cs b/samples/DotNetOpenAuth.ApplicationBlock/Provider/AuthenticationRequestExtensions.cs new file mode 100644 index 0000000..a737d30 --- /dev/null +++ b/samples/DotNetOpenAuth.ApplicationBlock/Provider/AuthenticationRequestExtensions.cs @@ -0,0 +1,38 @@ +namespace DotNetOpenAuth.ApplicationBlock.Provider { + using System; + using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.Provider; + + public static class AuthenticationRequestExtensions { + /// <summary> + /// Removes all personally identifiable information from the positive assertion. + /// </summary> + /// <param name="request">The incoming authentication request.</param> + /// <param name="localIdentifier">The OP local identifier, before the anonymous hash is applied to it.</param> + /// <param name="anonymousIdentifierProvider">The anonymous identifier provider.</param> + /// <param name="pairwiseUnique">if set to <c>true</c> the anonymous identifier will be unique to the requesting relying party's realm.</param> + /// <remarks> + /// The openid.claimed_id and openid.identity values are hashed. + /// </remarks> + public static void ScrubPersonallyIdentifiableInformation(this IAuthenticationRequest request, Identifier localIdentifier, AnonymousIdentifierProviderBase anonymousIdentifierProvider, bool pairwiseUnique) { + if (request == null) { + throw new ArgumentNullException("request"); + } + if (!request.IsDirectedIdentity) { + throw new InvalidOperationException("This operation is supported only under identifier select (directed identity) scenarios."); + } + if (anonymousIdentifierProvider == null) { + throw new ArgumentNullException("anonymousIdentifierProvider"); + } + if (localIdentifier == null) { + throw new ArgumentNullException("localIdentifier"); + } + + // When generating the anonymous identifiers, the openid.identity and openid.claimed_id + // will always end up with matching values. + var anonymousIdentifier = anonymousIdentifierProvider.GetAnonymousIdentifier(localIdentifier, pairwiseUnique ? request.Realm : null); + request.ClaimedIdentifier = anonymousIdentifier; + request.LocalIdentifier = anonymousIdentifier; + } + } +} diff --git a/samples/DotNetOpenAuth.ApplicationBlock/Util.cs b/samples/DotNetOpenAuth.ApplicationBlock/Util.cs index ea7da97..8a188ac 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/Util.cs +++ b/samples/DotNetOpenAuth.ApplicationBlock/Util.cs @@ -5,6 +5,8 @@ using DotNetOpenAuth.Messaging; internal static class Util { + internal static readonly Random NonCryptoRandomDataGenerator = new Random(); + /// <summary> /// Enumerates through the individual set bits in a flag enum. /// </summary> @@ -28,6 +30,17 @@ } /// <summary> + /// Gets a buffer of random data (not cryptographically strong). + /// </summary> + /// <param name="length">The length of the sequence to generate.</param> + /// <returns>The generated values, which may contain zeros.</returns> + internal static byte[] GetNonCryptoRandomData(int length) { + byte[] buffer = new byte[length]; + NonCryptoRandomDataGenerator.NextBytes(buffer); + return buffer; + } + + /// <summary> /// Copies the contents of one stream to another. /// </summary> /// <param name="copyFrom">The stream to copy from, at the position where copying should begin.</param> diff --git a/samples/OpenIdProviderMvc/App_Data/Users.xml b/samples/OpenIdProviderMvc/App_Data/Users.xml index cffe009..7f70bc6 100644 --- a/samples/OpenIdProviderMvc/App_Data/Users.xml +++ b/samples/OpenIdProviderMvc/App_Data/Users.xml @@ -3,21 +3,26 @@ <User> <UserName>bob</UserName> <Password>test</Password> + <Salt>CDI=</Salt> </User> <User> <UserName>bob1</UserName> <Password>test</Password> + <Salt>CAI=</Salt> </User> <User> <UserName>bob2</UserName> <Password>test</Password> + <Salt>hYMC</Salt> </User> <User> <UserName>bob3</UserName> <Password>test</Password> + <Salt>hTKDAg==</Salt> </User> <User> <UserName>bob4</UserName> <Password>test</Password> + <Salt>hTkDAg==</Salt> </User> </Users> diff --git a/samples/OpenIdProviderMvc/Code/AnonymousIdentifierProvider.cs b/samples/OpenIdProviderMvc/Code/AnonymousIdentifierProvider.cs new file mode 100644 index 0000000..2b9e01c --- /dev/null +++ b/samples/OpenIdProviderMvc/Code/AnonymousIdentifierProvider.cs @@ -0,0 +1,23 @@ +namespace OpenIdProviderMvc.Code { + using System; + using System.Web.Security; + using DotNetOpenAuth.ApplicationBlock.Provider; + using DotNetOpenAuth.OpenId; + using OpenIdProviderMvc.Models; + + internal class AnonymousIdentifierProvider : AnonymousIdentifierProviderBase { + internal AnonymousIdentifierProvider() + : base(Util.GetAppPathRootedUri("anon?id=")) { + } + + protected override byte[] GetHashSaltForLocalIdentifier(Identifier localIdentifier) { + // 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. + var membership = (ReadOnlyXmlMembershipProvider)Membership.Provider; + string username = User.GetUserFromClaimedIdentifier(new Uri(localIdentifier)); + string salt = membership.GetSalt(username); + return Convert.FromBase64String(salt); + ////return AnonymousIdentifierProviderBase.GetNewSalt(5); + } + } +} diff --git a/samples/OpenIdProviderMvc/Code/ReadOnlyXmlMembershipProvider.cs b/samples/OpenIdProviderMvc/Code/ReadOnlyXmlMembershipProvider.cs index 3da0f8e..cc5a321 100644 --- a/samples/OpenIdProviderMvc/Code/ReadOnlyXmlMembershipProvider.cs +++ b/samples/OpenIdProviderMvc/Code/ReadOnlyXmlMembershipProvider.cs @@ -236,6 +236,11 @@ throw new NotSupportedException(); } + internal string GetSalt(string userName) { + this.ReadMembershipDataStore(); + return this.users[userName].Email; + } + // Helper method private void ReadMembershipDataStore() { lock (this) { @@ -246,11 +251,13 @@ XmlNodeList nodes = doc.GetElementsByTagName("User"); foreach (XmlNode node in nodes) { + // Yes, we're misusing some of these fields. A real app would + // have the right fields from a database to use. MembershipUser user = new MembershipUser( Name, // Provider name node["UserName"].InnerText, // Username null, // providerUserKey - null, // Email + node["Salt"].InnerText, // Email string.Empty, // passwordQuestion node["Password"].InnerText, // Comment true, // isApproved diff --git a/samples/OpenIdProviderMvc/Code/Util.cs b/samples/OpenIdProviderMvc/Code/Util.cs new file mode 100644 index 0000000..6623952 --- /dev/null +++ b/samples/OpenIdProviderMvc/Code/Util.cs @@ -0,0 +1,17 @@ +namespace OpenIdProviderMvc.Code { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Web; + + internal static class Util { + internal static Uri GetAppPathRootedUri(string value) { + string appPath = HttpContext.Current.Request.ApplicationPath.ToLowerInvariant(); + if (!appPath.EndsWith("/")) { + appPath += "/"; + } + + return new Uri(HttpContext.Current.Request.Url, appPath + value); + } + } +} diff --git a/samples/OpenIdProviderMvc/Controllers/HomeController.cs b/samples/OpenIdProviderMvc/Controllers/HomeController.cs index 5ba08b3..346e838 100644 --- a/samples/OpenIdProviderMvc/Controllers/HomeController.cs +++ b/samples/OpenIdProviderMvc/Controllers/HomeController.cs @@ -23,5 +23,9 @@ public ActionResult Xrds() { return View(); } + + public ActionResult PpidXrds() { + return View(); + } } } diff --git a/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs b/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs index fff0a62..e353268 100644 --- a/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs +++ b/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs @@ -5,8 +5,11 @@ namespace OpenIdProviderMvc.Controllers { using System.Web; using System.Web.Mvc; using System.Web.Mvc.Ajax; + using DotNetOpenAuth.ApplicationBlock.Provider; using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Provider; + using OpenIdProviderMvc.Code; public class OpenIdController : Controller { internal static OpenIdProvider OpenIdProvider = new OpenIdProvider(); @@ -17,16 +20,67 @@ namespace OpenIdProviderMvc.Controllers { } [ValidateInput(false)] + public ActionResult PpidProvider() { + return this.DoProvider(true); + } + + [ValidateInput(false)] public ActionResult Provider() { + return this.DoProvider(false); + } + + [Authorize] + public ActionResult SendAssertion(bool pseudonymous) { + IAuthenticationRequest authReq = PendingAuthenticationRequest; + PendingAuthenticationRequest = null; + if (authReq == null) { + throw new InvalidOperationException(); + } + + Identifier localIdentifier = Models.User.GetClaimedIdentifierForUser(User.Identity.Name); + + if (pseudonymous) { + if (!authReq.IsDirectedIdentity) { + throw new InvalidOperationException("Directed identity is the only supported scenario for anonymous identifiers."); + } + + var anonProvider = new AnonymousIdentifierProvider(); + authReq.ScrubPersonallyIdentifiableInformation(localIdentifier, anonProvider, true); + authReq.IsAuthenticated = true; + } else { + if (authReq.IsDirectedIdentity) { + authReq.LocalIdentifier = localIdentifier; + authReq.ClaimedIdentifier = localIdentifier; + authReq.IsAuthenticated = true; + } else { + if (authReq.LocalIdentifier == localIdentifier) { + authReq.IsAuthenticated = true; + if (!authReq.IsDelegatedIdentifier) { + authReq.ClaimedIdentifier = authReq.LocalIdentifier; + } + } else { + authReq.IsAuthenticated = false; + } + } + + // TODO: Respond to AX/sreg extension requests here. + // We don't want to add these extension responses for anonymous identifiers + // because they could leak information about the user's identity. + } + + return OpenIdProvider.PrepareResponse(authReq).AsActionResult(); + } + + private ActionResult DoProvider(bool pseudonymous) { IRequest request = OpenIdProvider.GetRequest(); if (request != null) { var authRequest = request as IAuthenticationRequest; if (authRequest != null) { PendingAuthenticationRequest = authRequest; if (User.Identity.IsAuthenticated && (authRequest.IsDirectedIdentity || Models.User.GetClaimedIdentifierForUser(User.Identity.Name) == authRequest.LocalIdentifier)) { - return this.SendAssertion(); + return this.SendAssertion(pseudonymous); } else { - return RedirectToAction("LogOn", "Account", new { returnUrl = Url.Action("SendAssertion") }); + return RedirectToAction("LogOn", "Account", new { returnUrl = Url.Action("SendAssertion", new { pseudonymous = pseudonymous }) }); } } @@ -39,30 +93,5 @@ namespace OpenIdProviderMvc.Controllers { return View(); } } - - [Authorize] - public ActionResult SendAssertion() { - IAuthenticationRequest authReq = PendingAuthenticationRequest; - PendingAuthenticationRequest = null; - if (authReq == null) { - throw new InvalidOperationException(); - } - - if (authReq.IsDirectedIdentity) { - authReq.LocalIdentifier = Models.User.GetClaimedIdentifierForUser(User.Identity.Name); - authReq.ClaimedIdentifier = authReq.LocalIdentifier; - authReq.IsAuthenticated = true; - } else { - if (authReq.LocalIdentifier == Models.User.GetClaimedIdentifierForUser(User.Identity.Name)) { - authReq.IsAuthenticated = true; - if (!authReq.IsDelegatedIdentifier) { - authReq.ClaimedIdentifier = authReq.LocalIdentifier; - } - } else { - authReq.IsAuthenticated = false; - } - } - return OpenIdProvider.PrepareResponse(authReq).AsActionResult(); - } } } diff --git a/samples/OpenIdProviderMvc/Controllers/UserController.cs b/samples/OpenIdProviderMvc/Controllers/UserController.cs index 70bea04..c160fce 100644 --- a/samples/OpenIdProviderMvc/Controllers/UserController.cs +++ b/samples/OpenIdProviderMvc/Controllers/UserController.cs @@ -7,6 +7,14 @@ namespace OpenIdProviderMvc.Controllers { using System.Web.Mvc.Ajax; public class UserController : Controller { + public ActionResult PpidIdentity() { + if (Request.AcceptTypes.Contains("application/xrds+xml")) { + return View("PpidXrds"); + } + + return View(); + } + public ActionResult Identity(string id) { var redirect = this.RedirectIfNotNormalizedRequestUri(); if (redirect != null) { @@ -25,6 +33,10 @@ namespace OpenIdProviderMvc.Controllers { return View(); } + public ActionResult PpidXrds() { + return View(); + } + private ActionResult RedirectIfNotNormalizedRequestUri() { Uri normalized = Models.User.GetNormalizedClaimedIdentifier(Request.Url); if (Request.Url != normalized) { diff --git a/samples/OpenIdProviderMvc/Global.asax.cs b/samples/OpenIdProviderMvc/Global.asax.cs index b0d1b60..8c57961 100644 --- a/samples/OpenIdProviderMvc/Global.asax.cs +++ b/samples/OpenIdProviderMvc/Global.asax.cs @@ -22,6 +22,14 @@ "user/{id}/{action}", new { controller = "User", action = "Identity", id = string.Empty }); routes.MapRoute( + "PPID identifiers", + "anon", + new { controller = "User", action = "PpidIdentity", id = string.Empty }); + routes.MapRoute( + "PpidXrds", + "PpidXrds", + new { controller = "Home", action = "PpidXrds" }); // Parameter defaults + routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = string.Empty }); // Parameter defaults diff --git a/samples/OpenIdProviderMvc/Models/User.cs b/samples/OpenIdProviderMvc/Models/User.cs index 577aa05..443c004 100644 --- a/samples/OpenIdProviderMvc/Models/User.cs +++ b/samples/OpenIdProviderMvc/Models/User.cs @@ -5,17 +5,23 @@ using System.Text.RegularExpressions; using System.Web; using System.Web.Routing; + 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/"); } + } + internal static Uri GetClaimedIdentifierForUser(string username) { - string appPath = HttpContext.Current.Request.ApplicationPath; - if (!appPath.EndsWith("/")) { - appPath += "/"; + if (String.IsNullOrEmpty(username)) { + throw new ArgumentNullException("username"); } - Uri claimedIdentifier = new Uri( - HttpContext.Current.Request.Url, - appPath + "user/" + username); - return new Uri(claimedIdentifier.AbsoluteUri.ToLowerInvariant()); + + return new Uri(ClaimedIdentifierBaseUri, username.ToLowerInvariant()); } internal static string GetUserFromClaimedIdentifier(Uri claimedIdentifier) { diff --git a/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj b/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj index 80e8e64..5caf26d 100644 --- a/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj +++ b/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj @@ -65,10 +65,12 @@ </ItemGroup> <ItemGroup> <Compile Include="Code\AccountMembershipService.cs" /> + <Compile Include="Code\AnonymousIdentifierProvider.cs" /> <Compile Include="Code\FormsAuthenticationService.cs" /> <Compile Include="Code\IFormsAuthentication.cs" /> <Compile Include="Code\IMembershipService.cs" /> <Compile Include="Code\ReadOnlyXmlMembershipProvider.cs" /> + <Compile Include="Code\Util.cs" /> <Compile Include="Controllers\AccountController.cs" /> <Compile Include="Controllers\HomeController.cs" /> <Compile Include="Controllers\OpenIdController.cs" /> @@ -90,8 +92,11 @@ <Content Include="Views\Account\ChangePassword.aspx" /> <Content Include="Views\Account\ChangePasswordSuccess.aspx" /> <Content Include="Views\Account\Register.aspx" /> + <Content Include="Views\Home\PpidXrds.aspx" /> <Content Include="Views\Home\Xrds.aspx" /> <Content Include="Views\OpenId\Provider.aspx" /> + <Content Include="Views\User\PpidXrds.aspx" /> + <Content Include="Views\User\PpidIdentity.aspx" /> <Content Include="Views\User\Identity.aspx" /> <Content Include="Views\User\Xrds.aspx" /> <Content Include="Web.config" /> @@ -117,6 +122,10 @@ <Project>{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}</Project> <Name>DotNetOpenAuth</Name> </ProjectReference> + <ProjectReference Include="..\DotNetOpenAuth.ApplicationBlock\DotNetOpenAuth.ApplicationBlock.csproj"> + <Project>{AA78D112-D889-414B-A7D4-467B34C7B663}</Project> + <Name>DotNetOpenAuth.ApplicationBlock</Name> + </ProjectReference> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets" /> diff --git a/samples/OpenIdProviderMvc/Views/Home/PpidXrds.aspx b/samples/OpenIdProviderMvc/Views/Home/PpidXrds.aspx new file mode 100644 index 0000000..990a3df --- /dev/null +++ b/samples/OpenIdProviderMvc/Views/Home/PpidXrds.aspx @@ -0,0 +1,18 @@ +<%@ 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> + <URI><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/OpenId/PpidProvider"))%></URI> + </Service> + </XRD> +</xrds:XRDS> diff --git a/samples/OpenIdProviderMvc/Views/Shared/Site.Master b/samples/OpenIdProviderMvc/Views/Shared/Site.Master index 8df2d5f..073908e 100644 --- a/samples/OpenIdProviderMvc/Views/Shared/Site.Master +++ b/samples/OpenIdProviderMvc/Views/Shared/Site.Master @@ -13,7 +13,7 @@ <div class="page"> <div id="header"> <div id="title"> - <h1>My MVC Application</h1> + <h1>OpenID Provider MVC Application</h1> </div> <div id="logindisplay"> <% Html.RenderPartial("LogOnUserControl"); %> diff --git a/samples/OpenIdProviderMvc/Views/User/Identity.aspx b/samples/OpenIdProviderMvc/Views/User/Identity.aspx index 632df43..bb50899 100644 --- a/samples/OpenIdProviderMvc/Views/User/Identity.aspx +++ b/samples/OpenIdProviderMvc/Views/User/Identity.aspx @@ -3,7 +3,7 @@ <%@ Register Assembly="DotNetOpenAuth" Namespace="DotNetOpenAuth.OpenId.Provider" TagPrefix="op" %> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> - <%=ViewData["username"]%> + <%=Html.Encode(ViewData["username"])%> identity page </asp:Content> <asp:Content runat="server" ContentPlaceHolderID="HeadContent"> @@ -12,7 +12,7 @@ </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2>This is - <%=ViewData["username"]%>'s OpenID identity page </h2> + <%=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> diff --git a/samples/OpenIdProviderMvc/Views/User/PpidIdentity.aspx b/samples/OpenIdProviderMvc/Views/User/PpidIdentity.aspx new file mode 100644 index 0000000..f33a694 --- /dev/null +++ b/samples/OpenIdProviderMvc/Views/User/PpidIdentity.aspx @@ -0,0 +1,16 @@ +<%@ 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/ppidxrds" 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/PpidXrds.aspx b/samples/OpenIdProviderMvc/Views/User/PpidXrds.aspx new file mode 100644 index 0000000..67256bd --- /dev/null +++ b/samples/OpenIdProviderMvc/Views/User/PpidXrds.aspx @@ -0,0 +1,13 @@ +<%@ 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> + <URI><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/OpenId/PpidProvider"))%></URI> + </Service> + <Service priority="20"> + <Type>http://openid.net/signon/1.0</Type> + <URI><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/OpenId/PpidProvider"))%></URI> + </Service> + </XRD> +</XRDS> diff --git a/samples/OpenIdRelyingPartyWebForms/MembersOnly/DisplayGoogleContacts.aspx.cs b/samples/OpenIdRelyingPartyWebForms/MembersOnly/DisplayGoogleContacts.aspx.cs index dd8d897..b14aba1 100644 --- a/samples/OpenIdRelyingPartyWebForms/MembersOnly/DisplayGoogleContacts.aspx.cs +++ b/samples/OpenIdRelyingPartyWebForms/MembersOnly/DisplayGoogleContacts.aspx.cs @@ -17,7 +17,7 @@ } else { this.emailLabel.Text = "unavailable"; } - claimedIdLabel.Text = User.Identity.Name; + this.claimedIdLabel.Text = this.User.Identity.Name; var contactsDocument = GoogleConsumer.GetContacts(Global.GoogleWebConsumer, State.GoogleAccessToken); this.RenderContacts(contactsDocument); } diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index e2894cf..a6069b4 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -25,7 +25,7 @@ <DocumentationFile>..\..\bin\Debug\DotNetOpenAuth.xml</DocumentationFile> <RunCodeAnalysis>false</RunCodeAnalysis> <CodeAnalysisRules>-Microsoft.Design#CA1054;-Microsoft.Design#CA1056;-Microsoft.Design#CA1055</CodeAnalysisRules> - <CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking> + <CodeContractsEnableRuntimeChecking>False</CodeContractsEnableRuntimeChecking> <CodeContractsCustomRewriterAssembly> </CodeContractsCustomRewriterAssembly> <CodeContractsCustomRewriterClass> diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/ICombinedOpenIdProviderTokenManager.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/ICombinedOpenIdProviderTokenManager.cs index 951d8e8..ff004cb 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/ICombinedOpenIdProviderTokenManager.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/ICombinedOpenIdProviderTokenManager.cs @@ -7,6 +7,14 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { using DotNetOpenAuth.OpenId; + /// <summary> + /// An interface that providers that play a dual role as OpenID Provider + /// and OAuth Service Provider should implement on their token manager classes. + /// </summary> + /// <remarks> + /// This interface should be implemented by the same class that implements + /// <see cref="ITokenManager"/> in order to enable the OpenID+OAuth extension. + /// </remarks> public interface ICombinedOpenIdProviderTokenManager { /// <summary> /// Gets the OAuth consumer key for a given OpenID relying party realm. diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IOpenIdOAuthTokenManager.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IOpenIdOAuthTokenManager.cs index 592bff3..b3ee320 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/IOpenIdOAuthTokenManager.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/IOpenIdOAuthTokenManager.cs @@ -5,8 +5,8 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth.ChannelElements { - using DotNetOpenAuth.OpenId.Extensions.OAuth; using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.Extensions.OAuth; /// <summary> /// Additional methods an <see cref="ITokenManager"/> implementing class diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs index 3c4116a..a6e02fe 100644 --- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs +++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.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.4918 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -322,7 +322,7 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> - /// Looks up a localized string similar to The OpenId Provider issued an assertion for an Identifier whose discovery information did not match. + /// Looks up a localized string similar to The OpenID Provider issued an assertion for an Identifier whose discovery information did not match. ///Assertion endpoint info: ///{0} ///Discovered endpoint info: @@ -389,7 +389,7 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> - /// Looks up a localized string similar to No XRDS document containing OpenId relying party endpoint information could be found at {0}.. + /// Looks up a localized string similar to No XRDS document containing OpenID relying party endpoint information could be found at {0}.. /// </summary> internal static string NoRelyingPartyEndpointDiscovered { get { @@ -416,7 +416,7 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> - /// Looks up a localized string similar to No OpenId endpoint found.. + /// Looks up a localized string similar to No OpenID endpoint found.. /// </summary> internal static string OpenIdEndpointNotFound { get { @@ -425,7 +425,7 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> - /// Looks up a localized string similar to No OpenId url is provided.. + /// Looks up a localized string similar to No OpenID url is provided.. /// </summary> internal static string OpenIdTextBoxEmpty { get { diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx index 7356c10..95fe655 100644 --- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx +++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx @@ -181,7 +181,7 @@ <value>Not a recognized XRI format: '{0}'.</value> </data> <data name="IssuedAssertionFailsIdentifierDiscovery" xml:space="preserve"> - <value>The OpenId Provider issued an assertion for an Identifier whose discovery information did not match. + <value>The OpenID Provider issued an assertion for an Identifier whose discovery information did not match. Assertion endpoint info: {0} Discovered endpoint info: @@ -206,7 +206,7 @@ Discovered endpoint info: <value>Diffie-Hellman session type '{0}' not found for OpenID {1}.</value> </data> <data name="OpenIdEndpointNotFound" xml:space="preserve"> - <value>No OpenId endpoint found.</value> + <value>No OpenID endpoint found.</value> </data> <data name="OperationOnlyValidForSetupRequiredState" xml:space="preserve"> <value>This operation is only allowed when IAuthenticationResponse.State == AuthenticationStatus.SetupRequired.</value> @@ -266,7 +266,7 @@ Discovered endpoint info: <value>An authentication request has already been created using CreateRequest().</value> </data> <data name="OpenIdTextBoxEmpty" xml:space="preserve"> - <value>No OpenId url is provided.</value> + <value>No OpenID url is provided.</value> </data> <data name="ClientScriptExtensionPropertyNameCollision" xml:space="preserve"> <value>An extension with this property name ('{0}') has already been registered.</value> @@ -281,7 +281,7 @@ Discovered endpoint info: <value>This operation is not supported by serialized authentication responses. Try this operation from the LoggedIn event handler.</value> </data> <data name="NoRelyingPartyEndpointDiscovered" xml:space="preserve"> - <value>No XRDS document containing OpenId relying party endpoint information could be found at {0}.</value> + <value>No XRDS document containing OpenID relying party endpoint information could be found at {0}.</value> </data> <data name="AbsoluteUriRequired" xml:space="preserve"> <value>An absolute URI is required for this value.</value> diff --git a/src/DotNetOpenAuth/Yadis/Yadis.cs b/src/DotNetOpenAuth/Yadis/Yadis.cs index 98bffc9..0caffb6 100644 --- a/src/DotNetOpenAuth/Yadis/Yadis.cs +++ b/src/DotNetOpenAuth/Yadis/Yadis.cs @@ -27,7 +27,11 @@ namespace DotNetOpenAuth.Yadis { /// <summary> /// Gets or sets the cache that can be used for HTTP requests made during identifier discovery. /// </summary> +#if DEBUG + internal static readonly RequestCachePolicy IdentifierDiscoveryCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.BypassCache); +#else internal static readonly RequestCachePolicy IdentifierDiscoveryCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.CacheIfAvailable); +#endif /// <summary> /// The maximum number of bytes to read from an HTTP response |