summaryrefslogtreecommitdiffstats
path: root/samples/DotNetOpenAuth.ApplicationBlock
diff options
context:
space:
mode:
Diffstat (limited to 'samples/DotNetOpenAuth.ApplicationBlock')
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/CustomExtensions/AcmeRequest.cs2
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/CustomExtensions/AcmeResponse.cs2
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj4
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs2
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/OAuthIdentity.cs63
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/OAuthPrincipal.cs67
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/Provider/AnonymousIdentifierProviderBase.cs122
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/Provider/AuthenticationRequestExtensions.cs38
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/Util.cs13
9 files changed, 312 insertions, 1 deletions
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/CustomExtensions/AcmeRequest.cs b/samples/DotNetOpenAuth.ApplicationBlock/CustomExtensions/AcmeRequest.cs
index 84fdc36..8859c10 100644
--- a/samples/DotNetOpenAuth.ApplicationBlock/CustomExtensions/AcmeRequest.cs
+++ b/samples/DotNetOpenAuth.ApplicationBlock/CustomExtensions/AcmeRequest.cs
@@ -27,6 +27,8 @@ namespace DotNetOpenAuth.ApplicationBlock.CustomExtensions {
get { return Enumerable.Empty<string>(); }
}
+ public bool IsSignedByRemoteParty { get; set; }
+
#endregion
#region IMessage Members
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/CustomExtensions/AcmeResponse.cs b/samples/DotNetOpenAuth.ApplicationBlock/CustomExtensions/AcmeResponse.cs
index 3fae7d8..1e6748c 100644
--- a/samples/DotNetOpenAuth.ApplicationBlock/CustomExtensions/AcmeResponse.cs
+++ b/samples/DotNetOpenAuth.ApplicationBlock/CustomExtensions/AcmeResponse.cs
@@ -46,6 +46,8 @@ namespace DotNetOpenAuth.ApplicationBlock.CustomExtensions {
get { return Enumerable.Empty<string>(); }
}
+ public bool IsSignedByRemoteParty { get; set; }
+
#endregion
#region IMessage Members
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj
index 976a325..570d91f 100644
--- a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj
+++ b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj
@@ -59,7 +59,11 @@
<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/GoogleConsumer.cs b/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs
index bcdb477..4d3ce13 100644
--- a/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs
+++ b/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs
@@ -268,7 +268,7 @@ namespace DotNetOpenAuth.ApplicationBlock {
/// </summary>
/// <param name="scope">The scope, which may include one or several Google applications.</param>
/// <returns>A space-delimited list of URIs for the requested Google applications.</returns>
- private static string GetScopeUri(Applications scope) {
+ public static string GetScopeUri(Applications scope) {
return string.Join(" ", Util.GetIndividualFlags(scope).Select(app => DataScopeUris[(Applications)app]).ToArray());
}
}
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/OAuthIdentity.cs b/samples/DotNetOpenAuth.ApplicationBlock/OAuthIdentity.cs
new file mode 100644
index 0000000..ea9ec0b
--- /dev/null
+++ b/samples/DotNetOpenAuth.ApplicationBlock/OAuthIdentity.cs
@@ -0,0 +1,63 @@
+//-----------------------------------------------------------------------
+// <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
new file mode 100644
index 0000000..88f3b83
--- /dev/null
+++ b/samples/DotNetOpenAuth.ApplicationBlock/OAuthPrincipal.cs
@@ -0,0 +1,67 @@
+//-----------------------------------------------------------------------
+// <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
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>