diff options
Diffstat (limited to 'samples')
54 files changed, 843 insertions, 674 deletions
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj index 570d91f..976a325 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj +++ b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj @@ -59,11 +59,7 @@ <Compile Include="CustomExtensions\AcmeRequest.cs" /> <Compile Include="CustomExtensions\AcmeResponse.cs" /> <Compile Include="GoogleConsumer.cs" /> - <Compile Include="OAuthIdentity.cs" /> - <Compile Include="OAuthPrincipal.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/OAuthIdentity.cs b/samples/DotNetOpenAuth.ApplicationBlock/OAuthIdentity.cs deleted file mode 100644 index ea9ec0b..0000000 --- a/samples/DotNetOpenAuth.ApplicationBlock/OAuthIdentity.cs +++ /dev/null @@ -1,63 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="OAuthIdentity.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.ApplicationBlock { - using System; - using System.Runtime.InteropServices; - using System.Security.Principal; - - /// <summary> - /// Represents an OAuth consumer that is impersonating a known user on the system. - /// </summary> - [Serializable] - [ComVisible(true)] - internal class OAuthIdentity : IIdentity { - /// <summary> - /// Initializes a new instance of the <see cref="OAuthIdentity"/> class. - /// </summary> - /// <param name="username">The username.</param> - internal OAuthIdentity(string username) { - if (String.IsNullOrEmpty(username)) { - throw new ArgumentNullException("username"); - } - - this.Name = username; - } - - #region IIdentity Members - - /// <summary> - /// Gets the type of authentication used. - /// </summary> - /// <value>The constant "OAuth"</value> - /// <returns> - /// The type of authentication used to identify the user. - /// </returns> - public string AuthenticationType { - get { return "OAuth"; } - } - - /// <summary> - /// Gets a value indicating whether the user has been authenticated. - /// </summary> - /// <value>The value <c>true</c></value> - /// <returns>true if the user was authenticated; otherwise, false. - /// </returns> - public bool IsAuthenticated { - get { return true; } - } - - /// <summary> - /// Gets the name of the user who authorized the OAuth token the consumer is using for authorization. - /// </summary> - /// <returns> - /// The name of the user on whose behalf the code is running. - /// </returns> - public string Name { get; private set; } - - #endregion - } -} diff --git a/samples/DotNetOpenAuth.ApplicationBlock/OAuthPrincipal.cs b/samples/DotNetOpenAuth.ApplicationBlock/OAuthPrincipal.cs deleted file mode 100644 index 88f3b83..0000000 --- a/samples/DotNetOpenAuth.ApplicationBlock/OAuthPrincipal.cs +++ /dev/null @@ -1,67 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="OAuthPrincipal.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.ApplicationBlock { - using System; - using System.Linq; - using System.Runtime.InteropServices; - using System.Security.Principal; - - /// <summary> - /// Represents an OAuth consumer that is impersonating a known user on the system. - /// </summary> - [Serializable] - [ComVisible(true)] - internal class OAuthPrincipal : IPrincipal { - /// <summary> - /// The roles this user belongs to. - /// </summary> - private string[] roles; - - /// <summary> - /// Initializes a new instance of the <see cref="OAuthPrincipal"/> class. - /// </summary> - /// <param name="identity">The identity.</param> - /// <param name="roles">The roles this user belongs to.</param> - internal OAuthPrincipal(OAuthIdentity identity, string[] roles) { - this.Identity = identity; - this.roles = roles; - } - - /// <summary> - /// Initializes a new instance of the <see cref="OAuthPrincipal"/> class. - /// </summary> - /// <param name="username">The username.</param> - /// <param name="roles">The roles this user belongs to.</param> - internal OAuthPrincipal(string username, string[] roles) - : this(new OAuthIdentity(username), roles) { - } - - #region IPrincipal Members - - /// <summary> - /// Gets the identity of the current principal. - /// </summary> - /// <value></value> - /// <returns> - /// The <see cref="T:System.Security.Principal.IIdentity"/> object associated with the current principal. - /// </returns> - public IIdentity Identity { get; private set; } - - /// <summary> - /// Determines whether the current principal belongs to the specified role. - /// </summary> - /// <param name="role">The name of the role for which to check membership.</param> - /// <returns> - /// true if the current principal is a member of the specified role; otherwise, false. - /// </returns> - public bool IsInRole(string role) { - return this.roles.Contains(role); - } - - #endregion - } -} diff --git a/samples/DotNetOpenAuth.ApplicationBlock/Provider/AnonymousIdentifierProviderBase.cs b/samples/DotNetOpenAuth.ApplicationBlock/Provider/AnonymousIdentifierProviderBase.cs deleted file mode 100644 index 1df7267..0000000 --- a/samples/DotNetOpenAuth.ApplicationBlock/Provider/AnonymousIdentifierProviderBase.cs +++ /dev/null @@ -1,122 +0,0 @@ -//----------------------------------------------------------------------- -// <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 deleted file mode 100644 index a737d30..0000000 --- a/samples/DotNetOpenAuth.ApplicationBlock/Provider/AuthenticationRequestExtensions.cs +++ /dev/null @@ -1,38 +0,0 @@ -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 8a188ac..ea7da97 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/Util.cs +++ b/samples/DotNetOpenAuth.ApplicationBlock/Util.cs @@ -5,8 +5,6 @@ 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> @@ -30,17 +28,6 @@ } /// <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/OAuthServiceProvider/App_Code/DataApi.cs b/samples/OAuthServiceProvider/App_Code/DataApi.cs index a765159..d5adb10 100644 --- a/samples/OAuthServiceProvider/App_Code/DataApi.cs +++ b/samples/OAuthServiceProvider/App_Code/DataApi.cs @@ -1,20 +1,31 @@ using System.Linq; using System.ServiceModel; +/// <summary> +/// The WCF service API. +/// </summary> +/// <remarks> +/// Note how there is no code here that is bound to OAuth or any other +/// credential/authorization scheme. That's all part of the channel/binding elsewhere. +/// And the reference to OperationContext.Current.ServiceSecurityContext.PrimaryIdentity +/// is the user being impersonated by the WCF client. +/// In the OAuth case, it is the user who authorized the OAuth access token that was used +/// to gain access to the service. +/// </remarks> public class DataApi : IDataApi { - private static OAuthToken AccessToken { - get { return OperationContext.Current.IncomingMessageProperties["OAuthAccessToken"] as OAuthToken; } + private User User { + get { return OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.GetUser(); } } public int? GetAge() { - return AccessToken.User.Age; + return User.Age; } public string GetName() { - return AccessToken.User.FullName; + return User.FullName; } public string[] GetFavoriteSites() { - return AccessToken.User.FavoriteSites.Select(site => site.SiteUrl).ToArray(); + return User.FavoriteSites.Select(site => site.SiteUrl).ToArray(); } } diff --git a/samples/OAuthServiceProvider/App_Code/DatabaseTokenManager.cs b/samples/OAuthServiceProvider/App_Code/DatabaseTokenManager.cs index 8ca4539..710508d 100644 --- a/samples/OAuthServiceProvider/App_Code/DatabaseTokenManager.cs +++ b/samples/OAuthServiceProvider/App_Code/DatabaseTokenManager.cs @@ -26,7 +26,15 @@ public class DatabaseTokenManager : IServiceProviderTokenManager { public IServiceProviderRequestToken GetRequestToken(string token) { try { - return Global.DataContext.OAuthTokens.First(t => t.Token == token); + return Global.DataContext.OAuthTokens.First(t => t.Token == token && t.State != TokenAuthorizationState.AccessToken); + } catch (InvalidOperationException ex) { + throw new KeyNotFoundException("Unrecognized token", ex); + } + } + + public IServiceProviderAccessToken GetAccessToken(string token) { + try { + return Global.DataContext.OAuthTokens.First(t => t.Token == token && t.State == TokenAuthorizationState.AccessToken); } catch (InvalidOperationException ex) { throw new KeyNotFoundException("Unrecognized token", ex); } diff --git a/samples/OAuthServiceProvider/App_Code/OAuthAuthorizationManager.cs b/samples/OAuthServiceProvider/App_Code/OAuthAuthorizationManager.cs index fce1ad4..8589932 100644 --- a/samples/OAuthServiceProvider/App_Code/OAuthAuthorizationManager.cs +++ b/samples/OAuthServiceProvider/App_Code/OAuthAuthorizationManager.cs @@ -1,7 +1,11 @@ using System; +using System.Collections.Generic; +using System.IdentityModel.Policy; using System.Linq; +using System.Security.Principal; using System.ServiceModel; using System.ServiceModel.Channels; +using System.ServiceModel.Security; using DotNetOpenAuth; using DotNetOpenAuth.OAuth; @@ -24,10 +28,28 @@ public class OAuthAuthorizationManager : ServiceAuthorizationManager { if (auth != null) { var accessToken = Global.DataContext.OAuthTokens.Single(token => token.Token == auth.AccessToken); + var principal = sp.CreatePrincipal(auth); + var policy = new OAuthPrincipalAuthorizationPolicy(principal); + var policies = new List<IAuthorizationPolicy> { + policy, + }; + + var securityContext = new ServiceSecurityContext(policies.AsReadOnly()); + if (operationContext.IncomingMessageProperties.Security != null) { + operationContext.IncomingMessageProperties.Security.ServiceSecurityContext = securityContext; + } else { + operationContext.IncomingMessageProperties.Security = new SecurityMessageProperty { + ServiceSecurityContext = securityContext, + }; + } + + securityContext.AuthorizationContext.Properties["Identities"] = new List<IIdentity> { + principal.Identity, + }; + // Only allow this method call if the access token scope permits it. string[] scopes = accessToken.Scope.Split('|'); if (scopes.Contains(operationContext.IncomingMessageHeaders.Action)) { - operationContext.IncomingMessageProperties["OAuthAccessToken"] = accessToken; return true; } } diff --git a/samples/OAuthServiceProvider/App_Code/OAuthPrincipalAuthorizationPolicy.cs b/samples/OAuthServiceProvider/App_Code/OAuthPrincipalAuthorizationPolicy.cs new file mode 100644 index 0000000..5bd6b05 --- /dev/null +++ b/samples/OAuthServiceProvider/App_Code/OAuthPrincipalAuthorizationPolicy.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.IdentityModel.Claims; +using System.IdentityModel.Policy; +using System.Linq; +using System.Web; +using DotNetOpenAuth.OAuth.ChannelElements; + +public class OAuthPrincipalAuthorizationPolicy : IAuthorizationPolicy { + private readonly Guid uniqueId = Guid.NewGuid(); + private readonly OAuthPrincipal principal; + + /// <summary> + /// Initializes a new instance of the <see cref="OAuthPrincipalAuthorizationPolicy"/> class. + /// </summary> + /// <param name="principal">The principal.</param> + public OAuthPrincipalAuthorizationPolicy(OAuthPrincipal principal) { + this.principal = principal; + } + + #region IAuthorizationComponent Members + + /// <summary> + /// Gets a unique ID for this instance. + /// </summary> + public string Id { + get { return this.uniqueId.ToString(); } + } + + #endregion + + #region IAuthorizationPolicy Members + + public ClaimSet Issuer { + get { return ClaimSet.System; } + } + + public bool Evaluate(EvaluationContext evaluationContext, ref object state) { + evaluationContext.AddClaimSet(this, new DefaultClaimSet(Claim.CreateNameClaim(this.principal.Identity.Name))); + evaluationContext.Properties["Principal"] = this.principal; + return true; + } + + #endregion +} diff --git a/samples/OAuthServiceProvider/App_Code/OAuthToken.cs b/samples/OAuthServiceProvider/App_Code/OAuthToken.cs index 2f26799..ea18b2b 100644 --- a/samples/OAuthServiceProvider/App_Code/OAuthToken.cs +++ b/samples/OAuthServiceProvider/App_Code/OAuthToken.cs @@ -10,7 +10,7 @@ using System.Linq; using System.Web; using DotNetOpenAuth.OAuth.ChannelElements; -public partial class OAuthToken : IServiceProviderRequestToken { +public partial class OAuthToken : IServiceProviderRequestToken, IServiceProviderAccessToken { #region IServiceProviderRequestToken Members string IServiceProviderRequestToken.Token { @@ -41,4 +41,24 @@ public partial class OAuthToken : IServiceProviderRequestToken { } #endregion + + #region IServiceProviderAccessToken Members + + string IServiceProviderAccessToken.Token { + get { return this.Token; } + } + + DateTime? IServiceProviderAccessToken.ExpirationDate { + get { return null; } + } + + string IServiceProviderAccessToken.Username { + get { return this.User.OpenIDClaimedIdentifier; } + } + + string[] IServiceProviderAccessToken.Roles { + get { return this.Scope.Split('|'); } + } + + #endregion } diff --git a/samples/OAuthServiceProvider/App_Code/Utilities.cs b/samples/OAuthServiceProvider/App_Code/Utilities.cs new file mode 100644 index 0000000..2c25fe8 --- /dev/null +++ b/samples/OAuthServiceProvider/App_Code/Utilities.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Principal; +using System.Web; + +/// <summary> +/// Extension methods and other helpful utility methods. +/// </summary> +public static class Utilities { + /// <summary> + /// Gets the database entity representing the user identified by a given <see cref="IIdentity"/> instance. + /// </summary> + /// <param name="identity">The identity of the user.</param> + /// <returns> + /// The database object for that user; or <c>null</c> if the user could not + /// be found or if <paramref name="identity"/> is <c>null</c> or represents an anonymous identity. + /// </returns> + public static User GetUser(this IIdentity identity) { + if (identity == null || !identity.IsAuthenticated) { + return null; + } + + return Global.DataContext.Users.SingleOrDefault(user => user.OpenIDClaimedIdentifier == identity.Name); + } +} diff --git a/samples/OAuthServiceProvider/Web.config b/samples/OAuthServiceProvider/Web.config index 894ad38..d039daa 100644 --- a/samples/OAuthServiceProvider/Web.config +++ b/samples/OAuthServiceProvider/Web.config @@ -143,7 +143,9 @@ <behavior name="DataApiBehavior"> <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="true"/> - <serviceAuthorization serviceAuthorizationManagerType="OAuthAuthorizationManager, __code"/> + <serviceAuthorization + serviceAuthorizationManagerType="OAuthAuthorizationManager, __code" + principalPermissionMode="Custom" /> </behavior> </serviceBehaviors> </behaviors> diff --git a/samples/OpenIdOfflineProvider/CheckIdWindow.xaml.cs b/samples/OpenIdOfflineProvider/CheckIdWindow.xaml.cs index 597f72f..5b4dd24 100644 --- a/samples/OpenIdOfflineProvider/CheckIdWindow.xaml.cs +++ b/samples/OpenIdOfflineProvider/CheckIdWindow.xaml.cs @@ -40,7 +40,7 @@ namespace DotNetOpenAuth.OpenIdOfflineProvider { this.immediateModeLabel.Visibility = request.Immediate ? Visibility.Visible : Visibility.Collapsed; this.setupModeLabel.Visibility = request.Immediate ? Visibility.Collapsed : Visibility.Visible; - bool isRPDiscoverable = request.IsReturnUrlDiscoverable(provider.Provider.Channel.WebRequestHandler); + bool isRPDiscoverable = request.IsReturnUrlDiscoverable(provider.Provider) == RelyingPartyDiscoveryResult.Success; this.discoverableYesLabel.Visibility = isRPDiscoverable ? Visibility.Visible : Visibility.Collapsed; this.discoverableNoLabel.Visibility = isRPDiscoverable ? Visibility.Collapsed : Visibility.Visible; diff --git a/samples/OpenIdProviderMvc/Code/AnonymousIdentifierProvider.cs b/samples/OpenIdProviderMvc/Code/AnonymousIdentifierProvider.cs index 2b9e01c..6dc210d 100644 --- a/samples/OpenIdProviderMvc/Code/AnonymousIdentifierProvider.cs +++ b/samples/OpenIdProviderMvc/Code/AnonymousIdentifierProvider.cs @@ -1,15 +1,29 @@ namespace OpenIdProviderMvc.Code { using System; using System.Web.Security; - using DotNetOpenAuth.ApplicationBlock.Provider; using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.Provider; using OpenIdProviderMvc.Models; - internal class AnonymousIdentifierProvider : AnonymousIdentifierProviderBase { + internal class AnonymousIdentifierProvider : PrivatePersonalIdentifierProviderBase { + /// <summary> + /// Initializes a new instance of the <see cref="AnonymousIdentifierProvider"/> class. + /// </summary> internal AnonymousIdentifierProvider() : base(Util.GetAppPathRootedUri("anon?id=")) { } + /// <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="CreateSalt"/> or by a custom method. + /// </remarks> 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. @@ -17,7 +31,12 @@ string username = User.GetUserFromClaimedIdentifier(new Uri(localIdentifier)); string salt = membership.GetSalt(username); return Convert.FromBase64String(salt); - ////return AnonymousIdentifierProviderBase.GetNewSalt(5); + + // If users were encountered without a salt, one could be generated like this, + // and would also need to be saved to the user's account. + //// var newSalt = AnonymousIdentifierProviderBase.GetNewSalt(5); + //// user.Salt = newSalt; + //// return newSalt; } } } diff --git a/samples/OpenIdProviderMvc/Controllers/HomeController.cs b/samples/OpenIdProviderMvc/Controllers/HomeController.cs index 346e838..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,10 +22,7 @@ } public ActionResult Xrds() { - return View(); - } - - public ActionResult PpidXrds() { + ViewData["OPIdentifier"] = true; return View(); } } diff --git a/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs b/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs index e353268..bd0fdbf 100644 --- a/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs +++ b/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs @@ -5,9 +5,10 @@ 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.Behaviors; + using DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy; using DotNetOpenAuth.OpenId.Provider; using OpenIdProviderMvc.Code; @@ -20,67 +21,18 @@ 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(pseudonymous); + if (authRequest.IsReturnUrlDiscoverable(OpenIdProvider) == RelyingPartyDiscoveryResult.Success && + User.Identity.IsAuthenticated && + (authRequest.IsDirectedIdentity || this.UserControlsIdentifier(authRequest))) { + return this.SendAssertion(); } else { - return RedirectToAction("LogOn", "Account", new { returnUrl = Url.Action("SendAssertion", new { pseudonymous = pseudonymous }) }); + return RedirectToAction("LogOn", "Account", new { returnUrl = Url.Action("SendAssertion") }); } } @@ -93,5 +45,46 @@ namespace OpenIdProviderMvc.Controllers { return View(); } } + + [Authorize] + public ActionResult SendAssertion() { + IAuthenticationRequest authReq = PendingAuthenticationRequest; + PendingAuthenticationRequest = null; // clear session static so we don't do this again + if (authReq == null) { + throw new InvalidOperationException("There's no pending authentication request!"); + } + + if (authReq.IsDirectedIdentity) { + authReq.LocalIdentifier = Models.User.GetClaimedIdentifierForUser(User.Identity.Name); + } + if (!authReq.IsDelegatedIdentifier) { + authReq.ClaimedIdentifier = authReq.LocalIdentifier; + } + + // Respond to AX/sreg extension requests. + //// Real web sites would have code here + + authReq.IsAuthenticated = this.UserControlsIdentifier(authReq); + return OpenIdProvider.PrepareResponse(authReq).AsActionResult(); + } + + /// <summary> + /// Checks whether the logged in user controls the OP local identifier in the given authentication request. + /// </summary> + /// <param name="authReq">The authentication request.</param> + /// <returns><c>true</c> if the user controls the identifier; <c>false</c> otherwise.</returns> + private bool UserControlsIdentifier(IAuthenticationRequest authReq) { + if (authReq == null) { + throw new ArgumentNullException("authReq"); + } + + if (User == null || User.Identity == null) { + return false; + } + + Uri userLocalIdentifier = Models.User.GetClaimedIdentifierForUser(User.Identity.Name); + return authReq.LocalIdentifier == userLocalIdentifier || + authReq.LocalIdentifier == PpidGeneration.PpidIdentifierProvider.GetIdentifier(userLocalIdentifier, authReq.Realm); + } } } diff --git a/samples/OpenIdProviderMvc/Controllers/UserController.cs b/samples/OpenIdProviderMvc/Controllers/UserController.cs index 8b3f944..5e0c21f 100644 --- a/samples/OpenIdProviderMvc/Controllers/UserController.cs +++ b/samples/OpenIdProviderMvc/Controllers/UserController.cs @@ -7,38 +7,37 @@ 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) { - 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; - return View(); - } + if (!anon) { + this.ViewData["username"] = id; + } - public ActionResult Xrds(string id) { return View(); } - public ActionResult PpidXrds() { + public ActionResult Xrds(string id) { 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 8c57961..8390c46 100644 --- a/samples/OpenIdProviderMvc/Global.asax.cs +++ b/samples/OpenIdProviderMvc/Global.asax.cs @@ -14,21 +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 }); - routes.MapRoute( - "PpidXrds", - "PpidXrds", - new { controller = "Home", action = "PpidXrds" }); // Parameter defaults + new { controller = "User", action = "Identity", id = string.Empty, anon = true }); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters @@ -38,5 +36,20 @@ protected void Application_Start() { RegisterRoutes(RouteTable.Routes); } + + 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/OpenIdProviderMvc.csproj b/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj index 5caf26d..0c01c64 100644 --- a/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj +++ b/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj @@ -92,13 +92,9 @@ <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\Shared\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" /> <Content Include="Content\Site.css" /> <Content Include="Scripts\jquery-1.3.1.js" /> diff --git a/samples/OpenIdProviderMvc/Views/Home/PpidXrds.aspx b/samples/OpenIdProviderMvc/Views/Home/PpidXrds.aspx deleted file mode 100644 index 990a3df..0000000 --- a/samples/OpenIdProviderMvc/Views/Home/PpidXrds.aspx +++ /dev/null @@ -1,18 +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> - <URI><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/OpenId/PpidProvider"))%></URI> - </Service> - </XRD> -</xrds:XRDS> 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 f33a694..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/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 deleted file mode 100644 index 67256bd..0000000 --- a/samples/OpenIdProviderMvc/Views/User/PpidXrds.aspx +++ /dev/null @@ -1,13 +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> - <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/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/OpenIdProviderMvc/Web.config b/samples/OpenIdProviderMvc/Web.config index 029414a..f36bfcf 100644 --- a/samples/OpenIdProviderMvc/Web.config +++ b/samples/OpenIdProviderMvc/Web.config @@ -46,11 +46,18 @@ <!-- this is an optional configuration section where aspects of dotnetopenauth can be customized --> <dotNetOpenAuth> <openid> - <relyingParty> - <security requireSsl="false"/> + <provider> + <security requireSsl="false" /> + <behaviors> + <!-- Behaviors activate themselves automatically for individual matching requests. + The first one in this list to match an incoming request "owns" the request. If no + profile matches, the default behavior is assumed. --> + <!--<add type="DotNetOpenAuth.OpenId.Behaviors.GsaIcamProfile, DotNetOpenAuth" />--> + <add type="DotNetOpenAuth.OpenId.Behaviors.PpidGeneration, DotNetOpenAuth" /> + </behaviors> <!-- Uncomment the following to activate the sample custom store. --> <!--<store type="RelyingPartyWebForms.CustomStore, RelyingPartyWebForms" />--> - </relyingParty> + </provider> </openid> <messaging> <untrustedWebRequest> diff --git a/samples/OpenIdProviderWebForms/Code/Util.cs b/samples/OpenIdProviderWebForms/Code/Util.cs index 84d3c63..8700dbd 100644 --- a/samples/OpenIdProviderWebForms/Code/Util.cs +++ b/samples/OpenIdProviderWebForms/Code/Util.cs @@ -6,10 +6,6 @@ namespace OpenIdProviderWebForms.Code { using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Net; - using System.Text; using System.Web; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Provider; @@ -51,6 +47,26 @@ namespace OpenIdProviderWebForms.Code { // to know the answer. idrequest.IsAuthenticated = userOwningOpenIdUrl == HttpContext.Current.User.Identity.Name; } + + if (idrequest.IsAuthenticated.Value) { + // add extension responses here. + } + } else { + HttpContext.Current.Response.Redirect("~/decide.aspx", true); + } + } + + internal static void ProcessAnonymousRequest(IAnonymousRequest request) { + if (request.Immediate) { + // NOTE: in a production provider site, you may want to only + // respond affirmatively if the user has already authorized this consumer + // to know the answer. + request.IsApproved = HttpContext.Current.User.Identity.IsAuthenticated; + + if (request.IsApproved.Value) { + // Add extension responses here. + // These would typically be filled in from a user database + } } else { HttpContext.Current.Response.Redirect("~/decide.aspx", true); } diff --git a/samples/OpenIdProviderWebForms/Provider.ashx.cs b/samples/OpenIdProviderWebForms/Provider.ashx.cs index 40acc04..c8441cf 100644 --- a/samples/OpenIdProviderWebForms/Provider.ashx.cs +++ b/samples/OpenIdProviderWebForms/Provider.ashx.cs @@ -24,17 +24,20 @@ // But authentication requests cannot be responded to until something on // this site decides whether to approve or disapprove the authentication. if (!request.IsResponseReady) { - var idrequest = (IAuthenticationRequest)request; - - // We store the authentication request in the user's session so that + // We store the request in the user's session so that // redirects and user prompts can appear and eventually some page can decide // to respond to the OpenID authentication request either affirmatively or // negatively. - ProviderEndpoint.PendingAuthenticationRequest = idrequest; + ProviderEndpoint.PendingAnonymousRequest = request as IAnonymousRequest; + ProviderEndpoint.PendingAuthenticationRequest = request as IAuthenticationRequest; // We delegate that approval process to our utility method that we share // with our other Provider sample page server.aspx. - Code.Util.ProcessAuthenticationChallenge(idrequest); + if (ProviderEndpoint.PendingAuthenticationRequest != null) { + Code.Util.ProcessAuthenticationChallenge(ProviderEndpoint.PendingAuthenticationRequest); + } else if (ProviderEndpoint.PendingAnonymousRequest != null) { + Code.Util.ProcessAnonymousRequest(ProviderEndpoint.PendingAnonymousRequest); + } // As part of authentication approval, the user may need to authenticate // to this Provider and/or decide whether to allow the requesting RP site @@ -52,7 +55,7 @@ ProviderEndpoint.Provider.SendResponse(request); // Make sure that any PendingAuthenticationRequest that MAY be set is cleared. - ProviderEndpoint.PendingAuthenticationRequest = null; + ProviderEndpoint.PendingRequest = null; } } } diff --git a/samples/OpenIdProviderWebForms/Web.config b/samples/OpenIdProviderWebForms/Web.config index a6cd2eb..159dcd1 100644 --- a/samples/OpenIdProviderWebForms/Web.config +++ b/samples/OpenIdProviderWebForms/Web.config @@ -47,6 +47,13 @@ <dotNetOpenAuth> <openid> <provider> + <security requireSsl="false" /> + <behaviors> + <!-- Behaviors activate themselves automatically for individual matching requests. + The first one in this list to match an incoming request "owns" the request. If no + profile matches, the default behavior is assumed. --> + <!--<add type="DotNetOpenAuth.OpenId.Behaviors.PpidGeneration, DotNetOpenAuth" />--> + </behaviors> <!-- Uncomment the following to activate the sample custom store. --> <!--<store type="OpenIdProviderWebForms.Code.CustomStore, OpenIdProviderWebForms" />--> </provider> diff --git a/samples/OpenIdProviderWebForms/decide.aspx b/samples/OpenIdProviderWebForms/decide.aspx index 54c2f01..4a6e2d8 100644 --- a/samples/OpenIdProviderWebForms/decide.aspx +++ b/samples/OpenIdProviderWebForms/decide.aspx @@ -1,34 +1,24 @@ -<%@ Page Language="C#" AutoEventWireup="true" Inherits="OpenIdProviderWebForms.decide" CodeBehind="decide.aspx.cs" MasterPageFile="~/Site.Master" %> +<%@ Page Language="C#" AutoEventWireup="true" Inherits="OpenIdProviderWebForms.decide" + CodeBehind="decide.aspx.cs" MasterPageFile="~/Site.Master" %> <%@ Register Src="ProfileFields.ascx" TagName="ProfileFields" TagPrefix="uc1" %> <asp:Content runat="server" ContentPlaceHolderID="Main"> - <p> - A site has asked to authenticate that you own the identifier below. You should - only do this if you wish to log in to the site given by the Realm.</p> - <p> - This site - <asp:Label ID="relyingPartyVerificationResultLabel" runat="server" - Font-Bold="True" Text="failed" /> verification. </p> + <p><asp:Label ID="siteRequestLabel" runat="server" Text="A site has asked to authenticate that you own the identifier below." /> + You should only do this if you wish to log in to the site given by the Realm.</p> + <p>This site <asp:Label ID="relyingPartyVerificationResultLabel" runat="server" Font-Bold="True" + Text="failed" /> verification. </p> <table> <tr> - <td> - Identifier: </td> - <td> - <asp:Label runat="server" ID='identityUrlLabel' /> - </td> + <td>Identifier: </td> + <td><asp:Label runat="server" ID='identityUrlLabel' /> </td> </tr> <tr> - <td> - Realm: </td> - <td> - <asp:Label runat="server" ID='realmLabel' /> - </td> + <td>Realm: </td> + <td><asp:Label runat="server" ID='realmLabel' /> </td> </tr> </table> - <p> - Allow this authentication to proceed? - </p> + <p>Allow this to proceed? </p> <uc1:ProfileFields ID="profileFields" runat="server" Visible="false" /> <asp:Button ID="yes_button" OnClick="Yes_Click" Text=" yes " runat="Server" /> <asp:Button ID="no_button" OnClick="No_Click" Text=" no " runat="Server" /> -</asp:Content>
\ No newline at end of file +</asp:Content> diff --git a/samples/OpenIdProviderWebForms/decide.aspx.cs b/samples/OpenIdProviderWebForms/decide.aspx.cs index 777a688..3a14cf7 100644 --- a/samples/OpenIdProviderWebForms/decide.aspx.cs +++ b/samples/OpenIdProviderWebForms/decide.aspx.cs @@ -12,61 +12,75 @@ namespace OpenIdProviderWebForms { /// </summary> public partial class decide : Page { protected void Page_Load(object src, EventArgs e) { - if (ProviderEndpoint.PendingAuthenticationRequest == null) { + if (ProviderEndpoint.PendingRequest == null) { Response.Redirect("~/"); } - if (ProviderEndpoint.PendingAuthenticationRequest.IsDirectedIdentity) { - ProviderEndpoint.PendingAuthenticationRequest.LocalIdentifier = Code.Util.BuildIdentityUrl(); - } this.relyingPartyVerificationResultLabel.Text = - ProviderEndpoint.PendingAuthenticationRequest.IsReturnUrlDiscoverable(ProviderEndpoint.Provider.Channel.WebRequestHandler) ? "passed" : "failed"; + ProviderEndpoint.PendingRequest.IsReturnUrlDiscoverable(ProviderEndpoint.Provider) == RelyingPartyDiscoveryResult.Success ? "passed" : "failed"; + + this.realmLabel.Text = ProviderEndpoint.PendingRequest.Realm.ToString(); - this.identityUrlLabel.Text = ProviderEndpoint.PendingAuthenticationRequest.LocalIdentifier.ToString(); - this.realmLabel.Text = ProviderEndpoint.PendingAuthenticationRequest.Realm.ToString(); + if (ProviderEndpoint.PendingAuthenticationRequest != null) { + if (ProviderEndpoint.PendingAuthenticationRequest.IsDirectedIdentity) { + ProviderEndpoint.PendingAuthenticationRequest.LocalIdentifier = Code.Util.BuildIdentityUrl(); + } + this.identityUrlLabel.Text = ProviderEndpoint.PendingAuthenticationRequest.LocalIdentifier.ToString(); - // check that the logged in user is the same as the user requesting authentication to the consumer. If not, then log them out. - if (string.Equals(User.Identity.Name, Code.Util.ExtractUserName(ProviderEndpoint.PendingAuthenticationRequest.LocalIdentifier), StringComparison.OrdinalIgnoreCase)) { - // if simple registration fields were used, then prompt the user for them - var requestedFields = ProviderEndpoint.PendingAuthenticationRequest.GetExtension<ClaimsRequest>(); - if (requestedFields != null) { - this.profileFields.Visible = true; - this.profileFields.SetRequiredFieldsFromRequest(requestedFields); - if (!IsPostBack) { - var sregResponse = requestedFields.CreateResponse(); - sregResponse.Email = Membership.GetUser().Email; - this.profileFields.SetOpenIdProfileFields(sregResponse); - } + // check that the logged in user is the same as the user requesting authentication to the consumer. If not, then log them out. + if (!string.Equals(User.Identity.Name, Code.Util.ExtractUserName(ProviderEndpoint.PendingAuthenticationRequest.LocalIdentifier), StringComparison.OrdinalIgnoreCase)) { + FormsAuthentication.SignOut(); + Response.Redirect(Request.Url.AbsoluteUri); } } else { - FormsAuthentication.SignOut(); - Response.Redirect(Request.Url.AbsoluteUri); + this.identityUrlLabel.Text = "(not applicable)"; + this.siteRequestLabel.Text = "A site has asked for information about you."; + } + + // if simple registration fields were used, then prompt the user for them + var requestedFields = ProviderEndpoint.PendingRequest.GetExtension<ClaimsRequest>(); + if (requestedFields != null) { + this.profileFields.Visible = true; + this.profileFields.SetRequiredFieldsFromRequest(requestedFields); + if (!IsPostBack) { + var sregResponse = requestedFields.CreateResponse(); + sregResponse.Email = Membership.GetUser().Email; + this.profileFields.SetOpenIdProfileFields(sregResponse); + } } } protected void Yes_Click(object sender, EventArgs e) { - var sregRequest = ProviderEndpoint.PendingAuthenticationRequest.GetExtension<ClaimsRequest>(); + var sregRequest = ProviderEndpoint.PendingRequest.GetExtension<ClaimsRequest>(); ClaimsResponse sregResponse = null; if (sregRequest != null) { sregResponse = this.profileFields.GetOpenIdProfileFields(sregRequest); - ProviderEndpoint.PendingAuthenticationRequest.AddResponseExtension(sregResponse); + ProviderEndpoint.PendingRequest.AddResponseExtension(sregResponse); } - var papeRequest = ProviderEndpoint.PendingAuthenticationRequest.GetExtension<PolicyRequest>(); + var papeRequest = ProviderEndpoint.PendingRequest.GetExtension<PolicyRequest>(); PolicyResponse papeResponse = null; if (papeRequest != null) { papeResponse = new PolicyResponse(); papeResponse.NistAssuranceLevel = NistAssuranceLevel.InsufficientForLevel1; - ProviderEndpoint.PendingAuthenticationRequest.AddResponseExtension(papeResponse); + ProviderEndpoint.PendingRequest.AddResponseExtension(papeResponse); } - ProviderEndpoint.PendingAuthenticationRequest.IsAuthenticated = true; - Debug.Assert(ProviderEndpoint.PendingAuthenticationRequest.IsResponseReady, "Setting authentication should be all that's necessary."); + if (ProviderEndpoint.PendingAuthenticationRequest != null) { + ProviderEndpoint.PendingAuthenticationRequest.IsAuthenticated = true; + } else { + ProviderEndpoint.PendingAnonymousRequest.IsApproved = true; + } + Debug.Assert(ProviderEndpoint.PendingRequest.IsResponseReady, "Setting authentication should be all that's necessary."); ProviderEndpoint.SendResponse(); } protected void No_Click(object sender, EventArgs e) { - ProviderEndpoint.PendingAuthenticationRequest.IsAuthenticated = false; - Debug.Assert(ProviderEndpoint.PendingAuthenticationRequest.IsResponseReady, "Setting authentication should be all that's necessary."); + if (ProviderEndpoint.PendingAuthenticationRequest != null) { + ProviderEndpoint.PendingAuthenticationRequest.IsAuthenticated = false; + } else { + ProviderEndpoint.PendingAnonymousRequest.IsApproved = false; + } + Debug.Assert(ProviderEndpoint.PendingRequest.IsResponseReady, "Setting authentication should be all that's necessary."); ProviderEndpoint.SendResponse(); } } diff --git a/samples/OpenIdProviderWebForms/decide.aspx.designer.cs b/samples/OpenIdProviderWebForms/decide.aspx.designer.cs index 795d1c7..05386cd 100644 --- a/samples/OpenIdProviderWebForms/decide.aspx.designer.cs +++ b/samples/OpenIdProviderWebForms/decide.aspx.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.4918 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -14,6 +14,15 @@ namespace OpenIdProviderWebForms { public partial class decide { /// <summary> + /// siteRequestLabel control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.Label siteRequestLabel; + + /// <summary> /// relyingPartyVerificationResultLabel control. /// </summary> /// <remarks> diff --git a/samples/OpenIdProviderWebForms/server.aspx b/samples/OpenIdProviderWebForms/server.aspx index 10030a6..d3ce78d 100644 --- a/samples/OpenIdProviderWebForms/server.aspx +++ b/samples/OpenIdProviderWebForms/server.aspx @@ -14,7 +14,7 @@ This server.aspx page is the default provider endpoint to use. To switch to the .ashx handler, change the user_xrds.aspx and op_xrds.aspx files to point to provider.ashx instead of server.aspx. --%> - <openid:ProviderEndpoint runat="server" OnAuthenticationChallenge="provider_AuthenticationChallenge" /> + <openid:ProviderEndpoint runat="server" OnAuthenticationChallenge="provider_AuthenticationChallenge" OnAnonymousRequest="provider_AnonymousRequest" /> <p> <asp:Label ID="serverEndpointUrl" runat="server" EnableViewState="false" /> is an OpenID server endpoint. diff --git a/samples/OpenIdProviderWebForms/server.aspx.cs b/samples/OpenIdProviderWebForms/server.aspx.cs index c0af0b4..89e14f4 100644 --- a/samples/OpenIdProviderWebForms/server.aspx.cs +++ b/samples/OpenIdProviderWebForms/server.aspx.cs @@ -14,5 +14,9 @@ namespace OpenIdProviderWebForms { protected void provider_AuthenticationChallenge(object sender, AuthenticationChallengeEventArgs e) { Code.Util.ProcessAuthenticationChallenge(e.Request); } + + protected void provider_AnonymousRequest(object sender, AnonymousRequestEventArgs e) { + Code.Util.ProcessAnonymousRequest(e.Request); + } } }
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyClassicAsp/login.asp b/samples/OpenIdRelyingPartyClassicAsp/login.asp index 878ab39..d222e57 100644 --- a/samples/OpenIdRelyingPartyClassicAsp/login.asp +++ b/samples/OpenIdRelyingPartyClassicAsp/login.asp @@ -17,23 +17,37 @@ thisPageUrl = "http://" + Request.ServerVariables("HTTP_HOST") + Request.ServerVariables("URL") requestUrl = "http://" + Request.ServerVariables("HTTP_HOST") + Request.ServerVariables("HTTP_URL") Set dnoi = server.CreateObject("DotNetOpenAuth.OpenId.RelyingParty.OpenIdRelyingParty") + On Error Resume Next Set authentication = dnoi.ProcessAuthentication(requestUrl, Request.Form) + If Err.number <> 0 Then + Response.Write "<p>" + Server.HTMLEncode(Err.Description) + "</p>" + End If + On Error Goto 0 if Not authentication Is Nothing then If authentication.Successful Then Session("ClaimedIdentifier") = authentication.ClaimedIdentifier - Session("Email") = authentication.ClaimsResponse.Email - Session("Nickname") = authentication.ClaimsResponse.Nickname - Session("FullName") = authentication.ClaimsResponse.FullName + If Not authentication.ClaimsResponse Is Nothing Then + Session("Email") = authentication.ClaimsResponse.Email + Session("Nickname") = authentication.ClaimsResponse.Nickname + Session("FullName") = authentication.ClaimsResponse.FullName + End If Response.Redirect "MembersOnly.asp" else Response.Write "Authentication failed: " + authentication.ExceptionMessage end if elseif Request.Form("openid_identifier") <> "" then dim redirectUrl + On Error Resume Next ' redirectUrl = dnoi.CreateRequest(Request.Form("openid_identifier"), realm, thisPageUrl) redirectUrl = dnoi.CreateRequestWithSimpleRegistration(Request.Form("openid_identifier"), realm, thisPageUrl, "nickname,email", "fullname") - Response.Redirect redirectUrl + If Err.number <> 0 Then + Response.Write "<p>" + Server.HTMLEncode(Err.Description) + "</p>" + Else + Response.Redirect redirectUrl + End If + On Error Goto 0 End If + %> <form action="login.asp" method="post"> OpenID Login: diff --git a/samples/OpenIdRelyingPartyMvc/Global.asax.cs b/samples/OpenIdRelyingPartyMvc/Global.asax.cs index a43b734..d57a13f 100644 --- a/samples/OpenIdRelyingPartyMvc/Global.asax.cs +++ b/samples/OpenIdRelyingPartyMvc/Global.asax.cs @@ -17,6 +17,11 @@ "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = string.Empty }); // Parameter defaults + + routes.MapRoute( + "Root", + string.Empty, + new { controller = "Home", action = "Index", id = string.Empty }); } protected void Application_Start(object sender, EventArgs e) { diff --git a/samples/OpenIdRelyingPartyMvc/Web.config b/samples/OpenIdRelyingPartyMvc/Web.config index bb9f8e1..c3bfa41 100644 --- a/samples/OpenIdRelyingPartyMvc/Web.config +++ b/samples/OpenIdRelyingPartyMvc/Web.config @@ -47,6 +47,11 @@ <openid> <relyingParty> <security requireSsl="false" /> + <behaviors> + <!-- The following OPTIONAL behavior allows RPs to use SREG only, but be compatible + with OPs that use Attribute Exchange (in various formats). --> + <add type="DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform, DotNetOpenAuth" /> + </behaviors> </relyingParty> </openid> <messaging> diff --git a/samples/OpenIdRelyingPartyWebForms/MembersOnly/Default.aspx b/samples/OpenIdRelyingPartyWebForms/MembersOnly/Default.aspx index 46458e8..cbc13ee 100644 --- a/samples/OpenIdRelyingPartyWebForms/MembersOnly/Default.aspx +++ b/samples/OpenIdRelyingPartyWebForms/MembersOnly/Default.aspx @@ -1,6 +1,6 @@ <%@ Page Language="C#" AutoEventWireup="true" MasterPageFile="~/Site.Master" %> <%@ Import Namespace="OpenIdRelyingPartyWebForms" %> - +<%@ Register Src="~/MembersOnly/ProfileFieldsDisplay.ascx" TagPrefix="cc1" TagName="ProfileFieldsDisplay" %> <asp:Content ID="Content1" runat="server" ContentPlaceHolderID="Main"> <h2> Members Only Area @@ -22,85 +22,13 @@ </ul> <% } %> -<% if (State.ProfileFields != null) { %> +<% if (State.ProfileFields != null) { + profileFieldsDisplay.ProfileValues = State.ProfileFields; %> <p> In addition to authenticating you, your OpenID Provider may have told us something about you using the Simple Registration extension: </p> - <table id="profileFieldsTable" runat="server"> - <tr> - <td> - Nickname - </td> - <td> - <%=State.ProfileFields.Nickname %> - </td> - </tr> - <tr> - <td> - Email - </td> - <td> - <%=State.ProfileFields.Email%> - </td> - </tr> - <tr> - <td> - FullName - </td> - <td> - <%=State.ProfileFields.FullName%> - </td> - </tr> - <tr> - <td> - Date of Birth - </td> - <td> - <%=State.ProfileFields.BirthDate.ToString()%> - </td> - </tr> - <tr> - <td> - Gender - </td> - <td> - <%=State.ProfileFields.Gender.ToString()%> - </td> - </tr> - <tr> - <td> - Post Code - </td> - <td> - <%=State.ProfileFields.PostalCode%> - </td> - </tr> - <tr> - <td> - Country - </td> - <td> - <%=State.ProfileFields.Country%> - </td> - </tr> - <tr> - <td> - Language - </td> - <td> - <%=State.ProfileFields.Language%> - </td> - </tr> - <tr> - <td> - Timezone - </td> - <td> - <%=State.ProfileFields.TimeZone%> - </td> - </tr> - </table> + <cc1:ProfileFieldsDisplay runat="server" ID="profileFieldsDisplay" /> <% } %> </asp:Content> diff --git a/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx b/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx new file mode 100644 index 0000000..b2e5f7e --- /dev/null +++ b/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx @@ -0,0 +1,75 @@ +<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ProfileFieldsDisplay.ascx.cs" Inherits="OpenIdRelyingPartyWebForms.MembersOnly.ProfileFieldsDisplay" %> +<table id="profileFieldsTable" runat="server"> + <tr> + <td> + Nickname + </td> + <td> + <%=ProfileValues.Nickname %> + </td> + </tr> + <tr> + <td> + Email + </td> + <td> + <%=ProfileValues.Email%> + </td> + </tr> + <tr> + <td> + FullName + </td> + <td> + <%=ProfileValues.FullName%> + </td> + </tr> + <tr> + <td> + Date of Birth + </td> + <td> + <%=ProfileValues.BirthDate.ToString()%> + </td> + </tr> + <tr> + <td> + Gender + </td> + <td> + <%=ProfileValues.Gender.ToString()%> + </td> + </tr> + <tr> + <td> + Post Code + </td> + <td> + <%=ProfileValues.PostalCode%> + </td> + </tr> + <tr> + <td> + Country + </td> + <td> + <%=ProfileValues.Country%> + </td> + </tr> + <tr> + <td> + Language + </td> + <td> + <%=ProfileValues.Language%> + </td> + </tr> + <tr> + <td> + Timezone + </td> + <td> + <%=ProfileValues.TimeZone%> + </td> + </tr> +</table> diff --git a/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx.cs b/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx.cs new file mode 100644 index 0000000..4fb127e --- /dev/null +++ b/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx.cs @@ -0,0 +1,13 @@ +namespace OpenIdRelyingPartyWebForms.MembersOnly { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Web; + using System.Web.UI; + using System.Web.UI.WebControls; + using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; + + public partial class ProfileFieldsDisplay : System.Web.UI.UserControl { + public ClaimsResponse ProfileValues { get; set; } + } +}
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx.designer.cs b/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx.designer.cs new file mode 100644 index 0000000..4a5dc8a --- /dev/null +++ b/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx.designer.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:2.0.50727.4918 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace OpenIdRelyingPartyWebForms.MembersOnly { + + + public partial class ProfileFieldsDisplay { + + /// <summary> + /// profileFieldsTable control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.HtmlControls.HtmlTable profileFieldsTable; + } +} diff --git a/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx b/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx new file mode 100644 index 0000000..dc0c2bb --- /dev/null +++ b/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx @@ -0,0 +1,41 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="NoIdentityOpenId.aspx.cs" + MasterPageFile="~/Site.Master" Inherits="OpenIdRelyingPartyWebForms.NoIdentityOpenId" %> + +<asp:Content runat="server" ContentPlaceHolderID="Main"> + <h2>No-login OpenID extension Page </h2> + <p>This demonstrates an RP sending an extension-only request to an OP that carries + extensions that request anonymous information about you. In this scenario, the OP + would still authenticate the user, but would not assert any OpenID Identifier back + to the RP, but might provide information regarding the user such as age or membership + in an organization. </p> + <p><b>Note: </b>At time of this writing, most OPs do not support this feature, although + it is documented in the OpenID 2.0 spec. </p> + <asp:Label ID="Label1" runat="server" Text="OpenID Identifier" /> <asp:TextBox ID="openIdBox" + runat="server" /> + <asp:Button ID="beginButton" runat="server" Text="Begin" OnClick="beginButton_Click" /> + <asp:CustomValidator runat="server" ID="openidValidator" ErrorMessage="Invalid OpenID Identifier" + ControlToValidate="openIdBox" EnableViewState="false" Display="Dynamic" OnServerValidate="openidValidator_ServerValidate" /> + <asp:Label runat="server" EnableViewState="false" ID="resultMessage" /> + <asp:Panel runat="server" ID="ExtensionResponsesPanel" EnableViewState="false" Visible="false"> + <p>We have received a reasonable response from the Provider. Below is the Simple Registration + response we received, if any: </p> + <table id="profileFieldsTable" runat="server"> + <tr> + <td>Gender </td> + <td><asp:Label runat="server" ID="genderLabel" /> </td> + </tr> + <tr> + <td>Post Code </td> + <td><asp:Label runat="server" ID="postalCodeLabel" /> </td> + </tr> + <tr> + <td>Country </td> + <td><asp:Label runat="server" ID="countryLabel" /> </td> + </tr> + <tr> + <td>Timezone </td> + <td><asp:Label runat="server" ID="timeZoneLabel" /> </td> + </tr> + </table> + </asp:Panel> +</asp:Content> diff --git a/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx.cs b/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx.cs new file mode 100644 index 0000000..37a5515 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx.cs @@ -0,0 +1,79 @@ +namespace OpenIdRelyingPartyWebForms { + using System; + using System.Web.UI.WebControls; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; + using DotNetOpenAuth.OpenId.RelyingParty; + + public partial class NoIdentityOpenId : System.Web.UI.Page { + protected void Page_Load(object sender, EventArgs e) { + this.openIdBox.Focus(); + using (OpenIdRelyingParty rp = new OpenIdRelyingParty()) { + IAuthenticationResponse response = rp.GetResponse(); + if (response != null) { + switch (response.Status) { + case AuthenticationStatus.ExtensionsOnly: + this.ExtensionResponsesPanel.Visible = true; + + // This is the "success" status we get when no authentication was requested. + var sreg = response.GetExtension<ClaimsResponse>(); + if (sreg != null) { + this.timeZoneLabel.Text = sreg.TimeZone; + this.postalCodeLabel.Text = sreg.PostalCode; + this.countryLabel.Text = sreg.Country; + if (sreg.Gender.HasValue) { + this.genderLabel.Text = sreg.Gender.Value.ToString(); + } + } + break; + case AuthenticationStatus.Canceled: + this.resultMessage.Text = "Canceled at OP. This may be a sign that the OP doesn't support this message."; + break; + case AuthenticationStatus.Failed: + this.resultMessage.Text = "OP returned a failure: " + response.Exception; + break; + case AuthenticationStatus.SetupRequired: + case AuthenticationStatus.Authenticated: + default: + this.resultMessage.Text = "OP returned an unexpected response."; + break; + } + } + } + } + + protected void beginButton_Click(object sender, EventArgs e) { + if (!this.Page.IsValid) { + return; // don't login if custom validation failed. + } + try { + using (OpenIdRelyingParty rp = new OpenIdRelyingParty()) { + var request = rp.CreateRequest(this.openIdBox.Text); + request.IsExtensionOnly = true; + + // This is where you would add any OpenID extensions you wanted + // to include in the request. + request.AddExtension(new ClaimsRequest { + Country = DemandLevel.Request, + Gender = DemandLevel.Require, + PostalCode = DemandLevel.Require, + TimeZone = DemandLevel.Require, + }); + + request.RedirectToProvider(); + } + } catch (ProtocolException ex) { + // The user probably entered an Identifier that + // was not a valid OpenID endpoint. + this.openidValidator.Text = ex.Message; + this.openidValidator.IsValid = false; + } + } + + protected void openidValidator_ServerValidate(object source, ServerValidateEventArgs args) { + // This catches common typos that result in an invalid OpenID Identifier. + args.IsValid = Identifier.IsValid(args.Value); + } + } +} diff --git a/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx.designer.cs b/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx.designer.cs new file mode 100644 index 0000000..fb959a3 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx.designer.cs @@ -0,0 +1,115 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:2.0.50727.4918 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace OpenIdRelyingPartyWebForms { + + + public partial class NoIdentityOpenId { + + /// <summary> + /// Label1 control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.Label Label1; + + /// <summary> + /// openIdBox control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.TextBox openIdBox; + + /// <summary> + /// beginButton control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.Button beginButton; + + /// <summary> + /// openidValidator control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.CustomValidator openidValidator; + + /// <summary> + /// resultMessage control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.Label resultMessage; + + /// <summary> + /// ExtensionResponsesPanel control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.Panel ExtensionResponsesPanel; + + /// <summary> + /// profileFieldsTable control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.HtmlControls.HtmlTable profileFieldsTable; + + /// <summary> + /// genderLabel control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.Label genderLabel; + + /// <summary> + /// postalCodeLabel control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.Label postalCodeLabel; + + /// <summary> + /// countryLabel control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.Label countryLabel; + + /// <summary> + /// timeZoneLabel control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.Label timeZoneLabel; + } +} diff --git a/samples/OpenIdRelyingPartyWebForms/OpenIdRelyingPartyWebForms.csproj b/samples/OpenIdRelyingPartyWebForms/OpenIdRelyingPartyWebForms.csproj index c45f007..d3bf92c 100644 --- a/samples/OpenIdRelyingPartyWebForms/OpenIdRelyingPartyWebForms.csproj +++ b/samples/OpenIdRelyingPartyWebForms/OpenIdRelyingPartyWebForms.csproj @@ -132,6 +132,13 @@ <Compile Include="MembersOnly\DisplayGoogleContacts.aspx.designer.cs"> <DependentUpon>DisplayGoogleContacts.aspx</DependentUpon> </Compile> + <Compile Include="MembersOnly\ProfileFieldsDisplay.ascx.cs"> + <DependentUpon>ProfileFieldsDisplay.ascx</DependentUpon> + <SubType>ASPXCodeBehind</SubType> + </Compile> + <Compile Include="MembersOnly\ProfileFieldsDisplay.ascx.designer.cs"> + <DependentUpon>ProfileFieldsDisplay.ascx</DependentUpon> + </Compile> <Compile Include="m\Login.aspx.cs"> <DependentUpon>Login.aspx</DependentUpon> <SubType>ASPXCodeBehind</SubType> @@ -139,6 +146,13 @@ <Compile Include="m\Login.aspx.designer.cs"> <DependentUpon>Login.aspx</DependentUpon> </Compile> + <Compile Include="NoIdentityOpenId.aspx.cs"> + <DependentUpon>NoIdentityOpenId.aspx</DependentUpon> + <SubType>ASPXCodeBehind</SubType> + </Compile> + <Compile Include="NoIdentityOpenId.aspx.designer.cs"> + <DependentUpon>NoIdentityOpenId.aspx</DependentUpon> + </Compile> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="TracePage.aspx.cs"> <DependentUpon>TracePage.aspx</DependentUpon> @@ -161,8 +175,10 @@ <Content Include="images\yahoo.png" /> <Content Include="loginPlusOAuth.aspx" /> <Content Include="MembersOnly\DisplayGoogleContacts.aspx" /> + <Content Include="MembersOnly\ProfileFieldsDisplay.ascx" /> <Content Include="MembersOnly\Web.config" /> <Content Include="m\Login.aspx" /> + <Content Include="NoIdentityOpenId.aspx" /> </ItemGroup> <ItemGroup> <None Include="Code\CustomStoreDataSet.xsc"> diff --git a/samples/OpenIdRelyingPartyWebForms/Web.config b/samples/OpenIdRelyingPartyWebForms/Web.config index 7983e21..c48c198 100644 --- a/samples/OpenIdRelyingPartyWebForms/Web.config +++ b/samples/OpenIdRelyingPartyWebForms/Web.config @@ -23,12 +23,17 @@ <!--<servicePointManager checkCertificateRevocationList="true"/>--> </settings> </system.net> - <!-- this is an optional configuration section where aspects of dotnetopenauth can be customized --> <dotNetOpenAuth> <openid> <relyingParty> <security requireSsl="false" /> + <behaviors> + <!-- The following OPTIONAL behavior allows RPs to use SREG only, but be compatible + with OPs that use Attribute Exchange (in various formats). --> + <add type="DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform, DotNetOpenAuth" /> + <!--<add type="DotNetOpenAuth.OpenId.Behaviors.GsaIcamProfile, DotNetOpenAuth" />--> + </behaviors> <!-- Uncomment the following to activate the sample custom store. --> <!--<store type="OpenIdRelyingPartyWebForms.CustomStore, OpenIdRelyingPartyWebForms" />--> </relyingParty> diff --git a/samples/OpenIdRelyingPartyWebForms/login.aspx b/samples/OpenIdRelyingPartyWebForms/login.aspx index 6e66fd3..281725c 100644 --- a/samples/OpenIdRelyingPartyWebForms/login.aspx +++ b/samples/OpenIdRelyingPartyWebForms/login.aspx @@ -5,7 +5,7 @@ <asp:Content runat="server" ContentPlaceHolderID="Main"> <h2>Login Page </h2> <rp:OpenIdLogin ID="OpenIdLogin1" runat="server" CssClass="openid_login" RequestCountry="Request" - RequestEmail="Request" RequestGender="Require" RequestPostalCode="Require" RequestTimeZone="Require" + RequestEmail="Require" RequestGender="Require" RequestPostalCode="Require" RequestTimeZone="Require" RememberMeVisible="True" PolicyUrl="~/PrivacyPolicy.aspx" TabIndex="1" OnLoggedIn="OpenIdLogin1_LoggedIn" OnLoggingIn="OpenIdLogin1_LoggingIn" OnSetupRequired="OpenIdLogin1_SetupRequired" /> @@ -13,18 +13,20 @@ <asp:CheckBox ID="requireSslCheckBox" runat="server" Text="RequireSsl (high security) mode" oncheckedchanged="requireSslCheckBox_CheckedChanged" /><br /> - <asp:CheckBox ID="immediateCheckBox" runat="server" Text="Immediate mode" /><br /> + <h4 style="margin-top: 0; margin-bottom: 0">PAPE policies</h4> <asp:CheckBoxList runat="server" ID="papePolicies"> <asp:ListItem Text="Request phishing resistant authentication" Value="http://schemas.openid.net/pape/policies/2007/06/phishing-resistant" /> <asp:ListItem Text="Request multi-factor authentication" Value="http://schemas.openid.net/pape/policies/2007/06/multi-factor" /> <asp:ListItem Text="Request physical multi-factor authentication" Value="http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical" /> + <asp:ListItem Text="Request PPID identifier" Value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier" /> </asp:CheckBoxList> + <p>Try the PPID identifier functionality against the OpenIDProviderMvc sample.</p> </fieldset> <br /> <asp:Label ID="setupRequiredLabel" runat="server" EnableViewState="False" Text="You must log into your Provider first to use Immediate mode." Visible="False" /> <p> - <asp:ImageButton runat="server" ImageUrl="~/images/yahoo.png" ID="yahooLoginButton" - OnClick="yahooLoginButton_Click" /> + <rp:OpenIdButton runat="server" ImageUrl="~/images/yahoo.png" Text="Login with Yahoo!" ID="yahooLoginButton" + Identifier="https://me.yahoo.com/" /> </p> </asp:Content> diff --git a/samples/OpenIdRelyingPartyWebForms/login.aspx.cs b/samples/OpenIdRelyingPartyWebForms/login.aspx.cs index 37a714c..1de942a 100644 --- a/samples/OpenIdRelyingPartyWebForms/login.aspx.cs +++ b/samples/OpenIdRelyingPartyWebForms/login.aspx.cs @@ -39,20 +39,7 @@ namespace OpenIdRelyingPartyWebForms { this.setupRequiredLabel.Visible = true; } - protected void yahooLoginButton_Click(object sender, ImageClickEventArgs e) { - OpenIdRelyingParty openid = new OpenIdRelyingParty(); - var req = openid.CreateRequest("yahoo.com"); - this.prepareRequest(req); - req.RedirectToProvider(); - - // We don't listen for the response from the provider explicitly - // because the OpenIdLogin control is already doing that for us. - } - private void prepareRequest(IAuthenticationRequest request) { - // Setup is the default for the login control. But the user may have checked the box to override that. - request.Mode = this.immediateCheckBox.Checked ? AuthenticationRequestMode.Immediate : AuthenticationRequestMode.Setup; - // Collect the PAPE policies requested by the user. List<string> policies = new List<string>(); foreach (ListItem item in this.papePolicies.Items) { diff --git a/samples/OpenIdRelyingPartyWebForms/login.aspx.designer.cs b/samples/OpenIdRelyingPartyWebForms/login.aspx.designer.cs index 436ef7b..944f5ff 100644 --- a/samples/OpenIdRelyingPartyWebForms/login.aspx.designer.cs +++ b/samples/OpenIdRelyingPartyWebForms/login.aspx.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. @@ -32,15 +32,6 @@ namespace OpenIdRelyingPartyWebForms { protected global::System.Web.UI.WebControls.CheckBox requireSslCheckBox; /// <summary> - /// immediateCheckBox control. - /// </summary> - /// <remarks> - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// </remarks> - protected global::System.Web.UI.WebControls.CheckBox immediateCheckBox; - - /// <summary> /// papePolicies control. /// </summary> /// <remarks> @@ -65,6 +56,6 @@ namespace OpenIdRelyingPartyWebForms { /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// </remarks> - protected global::System.Web.UI.WebControls.ImageButton yahooLoginButton; + protected global::DotNetOpenAuth.OpenId.RelyingParty.OpenIdButton yahooLoginButton; } } diff --git a/samples/OpenIdRelyingPartyWebForms/loginProgrammatic.aspx b/samples/OpenIdRelyingPartyWebForms/loginProgrammatic.aspx index a00eccd..78179f7 100644 --- a/samples/OpenIdRelyingPartyWebForms/loginProgrammatic.aspx +++ b/samples/OpenIdRelyingPartyWebForms/loginProgrammatic.aspx @@ -12,4 +12,5 @@ Visible="False" /> <asp:Label ID="loginCanceledLabel" runat="server" EnableViewState="False" Text="Login canceled" Visible="False" /> + <asp:CheckBox ID="noLoginCheckBox" runat="server" Text="Extensions only (no login) -- most OPs don't yet support this" /> </asp:Content>
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyWebForms/loginProgrammatic.aspx.designer.cs b/samples/OpenIdRelyingPartyWebForms/loginProgrammatic.aspx.designer.cs index 0363be7..239d7b8 100644 --- a/samples/OpenIdRelyingPartyWebForms/loginProgrammatic.aspx.designer.cs +++ b/samples/OpenIdRelyingPartyWebForms/loginProgrammatic.aspx.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. @@ -66,5 +66,14 @@ namespace OpenIdRelyingPartyWebForms { /// To modify move field declaration from designer file to code-behind file. /// </remarks> protected global::System.Web.UI.WebControls.Label loginCanceledLabel; + + /// <summary> + /// noLoginCheckBox control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.CheckBox noLoginCheckBox; } } diff --git a/samples/OpenIdRelyingPartyWebForms/xrds.aspx b/samples/OpenIdRelyingPartyWebForms/xrds.aspx index e169bc7..52e27f7 100644 --- a/samples/OpenIdRelyingPartyWebForms/xrds.aspx +++ b/samples/OpenIdRelyingPartyWebForms/xrds.aspx @@ -17,6 +17,11 @@ is default.aspx. <URI priority="1"><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/login.aspx"))%></URI> <URI priority="2"><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/loginProgrammatic.aspx"))%></URI> <URI priority="3"><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/ajaxlogin.aspx"))%></URI> + <URI priority="3"><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/NoIdentityOpenId.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> |