Merge pull request #11 from tomasherceg/master
OpenID - generic protocol extension infrastructure & Simple Registration Extension implementation
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Owin.Security.Providers.OpenID.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains an extension method that makes reading the SREG fields easier.
|
||||
/// </summary>
|
||||
public static class OpenIDSimpleRegistrationAuthenticationContextExtensions
|
||||
{
|
||||
public static OpenIDSimpleRegistrationResult GetSimpleRegistrationResult(this OpenIDAuthenticatedContext context)
|
||||
{
|
||||
if (!context.ProtocolExtensionData.ContainsKey(typeof (OpenIDSimpleRegistrationExtension)))
|
||||
{
|
||||
return new OpenIDSimpleRegistrationResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
return context.ProtocolExtensionData[typeof (OpenIDSimpleRegistrationExtension)] as OpenIDSimpleRegistrationResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Owin.Security.Providers.OpenID.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements the OpenID Simple Registration Extension http://openid.net/specs/openid-simple-registration-extension-1_0.html
|
||||
/// </summary>
|
||||
public class OpenIDSimpleRegistrationExtension : IOpenIDProtocolExtension
|
||||
{
|
||||
|
||||
private static readonly Dictionary<OpenIDSimpleRegistrationField, string> claimsMap = new Dictionary<OpenIDSimpleRegistrationField, string>()
|
||||
{
|
||||
{ OpenIDSimpleRegistrationField.NickName, "nickname" },
|
||||
{ OpenIDSimpleRegistrationField.FullName, "fullname" },
|
||||
{ OpenIDSimpleRegistrationField.Email, "email" },
|
||||
{ OpenIDSimpleRegistrationField.DayOfBirth, "dob" },
|
||||
{ OpenIDSimpleRegistrationField.Gender, "gender" },
|
||||
{ OpenIDSimpleRegistrationField.PostCode, "postcode" },
|
||||
{ OpenIDSimpleRegistrationField.Country, "country" },
|
||||
{ OpenIDSimpleRegistrationField.Language, "language" },
|
||||
{ OpenIDSimpleRegistrationField.Timezone, "timezone" }
|
||||
};
|
||||
|
||||
private const string sregNamespace = "http://openid.net/extensions/sreg/1.1";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of comma-separated SREG fields that are required.
|
||||
/// </summary>
|
||||
public HashSet<OpenIDSimpleRegistrationField> RequiredFields { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of comma-separated SREG fields that are optional.
|
||||
/// </summary>
|
||||
public HashSet<OpenIDSimpleRegistrationField> OptionalFields { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the SREG policy URL.
|
||||
/// </summary>
|
||||
public string PolicyUrl { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OpenIDSimpleRegistrationExtension"/> class.
|
||||
/// </summary>
|
||||
public OpenIDSimpleRegistrationExtension()
|
||||
{
|
||||
RequiredFields = new HashSet<OpenIDSimpleRegistrationField>() { OpenIDSimpleRegistrationField.Email, OpenIDSimpleRegistrationField.FullName };
|
||||
OptionalFields = new HashSet<OpenIDSimpleRegistrationField>();
|
||||
PolicyUrl = string.Empty;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Appends the SREG required attributes to the request URL constructed on challenge.
|
||||
/// </summary>
|
||||
public Task OnChallengeAsync(Microsoft.Owin.Security.AuthenticationResponseChallenge challenge, OpenIDAuthorizationEndpointInfo endpoint)
|
||||
{
|
||||
endpoint.Url += "&openid.ns.sreg=" + Uri.EscapeDataString(sregNamespace);
|
||||
|
||||
var requiredClaims = string.Join(",", RequiredFields.Select(f => claimsMap[f]));
|
||||
endpoint.Url += "&openid.sreg.required=" + Uri.EscapeDataString(requiredClaims);
|
||||
|
||||
if (OptionalFields.Any())
|
||||
{
|
||||
var optionalClaims = string.Join(",", OptionalFields.Select(f => claimsMap[f]));
|
||||
endpoint.Url += "&openid.sreg.optional=" + Uri.EscapeDataString(optionalClaims);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(PolicyUrl))
|
||||
{
|
||||
endpoint.Url += "&openid.sreg.policy_url=" + Uri.EscapeDataString(PolicyUrl);
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the authentication response message.
|
||||
/// </summary>
|
||||
public Task<bool> OnValidateMessageAsync(Infrastructure.Message message)
|
||||
{
|
||||
// no additional checks needed
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts SREG attributes and returns the results.
|
||||
/// </summary>
|
||||
public Task<object> OnExtractResultsAsync(ClaimsIdentity identity, string claimedId, Infrastructure.Message message)
|
||||
{
|
||||
var result = new OpenIDSimpleRegistrationResult();
|
||||
foreach (var claim in claimsMap)
|
||||
{
|
||||
string value;
|
||||
if (message.TryGetValue(claim.Value + "." + sregNamespace, out value))
|
||||
{
|
||||
result.Values.Add(claim.Key, value);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult((object)result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Owin.Security.Providers.OpenID.Extensions
|
||||
{
|
||||
public enum OpenIDSimpleRegistrationField
|
||||
{
|
||||
NickName,
|
||||
FullName,
|
||||
Email,
|
||||
DayOfBirth,
|
||||
Gender,
|
||||
PostCode,
|
||||
Country,
|
||||
Language,
|
||||
Timezone
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Owin.Security.Providers.OpenID.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains values of OpenID Simple Registration Extension fields.
|
||||
/// </summary>
|
||||
public class OpenIDSimpleRegistrationResult
|
||||
{
|
||||
|
||||
public IDictionary<OpenIDSimpleRegistrationField, string> Values { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OpenIDSimpleRegistrationResult"/> class.
|
||||
/// </summary>
|
||||
public OpenIDSimpleRegistrationResult()
|
||||
{
|
||||
Values = new Dictionary<OpenIDSimpleRegistrationField, string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SREG field value.
|
||||
/// </summary>
|
||||
public string GetFieldValue(OpenIDSimpleRegistrationField field)
|
||||
{
|
||||
if (!Values.ContainsKey(field)) return null;
|
||||
return Values[field];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
27
Owin.Security.Providers/OpenID/IOpenIDProtocolExtension.cs
Normal file
27
Owin.Security.Providers/OpenID/IOpenIDProtocolExtension.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Owin.Security;
|
||||
|
||||
namespace Owin.Security.Providers.OpenID
|
||||
{
|
||||
public interface IOpenIDProtocolExtension
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Adds the required information in the authorization endpoint URL.
|
||||
/// </summary>
|
||||
Task OnChallengeAsync(AuthenticationResponseChallenge challenge, OpenIDAuthorizationEndpointInfo endpoint);
|
||||
|
||||
/// <summary>
|
||||
/// Performs additional authentication response message validations.
|
||||
/// </summary>
|
||||
Task<bool> OnValidateMessageAsync(Infrastructure.Message message);
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the data form the authentication response message and returns them.
|
||||
/// </summary>
|
||||
Task<object> OnExtractResultsAsync(System.Security.Claims.ClaimsIdentity identity, string claimedId, Infrastructure.Message message);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using System.Linq;
|
||||
|
||||
namespace Owin.Security.Providers.OpenID.Infrastructure
|
||||
{
|
||||
internal class Message
|
||||
public class Message
|
||||
{
|
||||
public Message(IReadableStringCollection parameters, bool strict)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
namespace Owin.Security.Providers.OpenID.Infrastructure
|
||||
{
|
||||
internal class Property
|
||||
public class Property
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public string Namespace { get; set; }
|
||||
|
||||
@@ -153,6 +153,15 @@ namespace Owin.Security.Providers.OpenID
|
||||
}
|
||||
}
|
||||
|
||||
// Allow protocol extensions to add custom message validation rules
|
||||
foreach (var protocolExtension in Options.ProtocolExtensions)
|
||||
{
|
||||
if (!await protocolExtension.OnValidateMessageAsync(message))
|
||||
{
|
||||
messageValidated = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (messageValidated)
|
||||
{
|
||||
IDictionary<string, string> attributeExchangeProperties = new Dictionary<string, string>();
|
||||
@@ -190,7 +199,7 @@ namespace Owin.Security.Providers.OpenID
|
||||
}
|
||||
|
||||
SetIdentityInformations(identity, claimedId.Value, attributeExchangeProperties);
|
||||
|
||||
|
||||
var context = new OpenIDAuthenticatedContext(
|
||||
Context,
|
||||
identity,
|
||||
@@ -198,6 +207,14 @@ namespace Owin.Security.Providers.OpenID
|
||||
responseMessage,
|
||||
attributeExchangeProperties);
|
||||
|
||||
|
||||
// Let protocol extensions to extract the results from the message
|
||||
foreach (var protocolExtension in Options.ProtocolExtensions)
|
||||
{
|
||||
var result = await protocolExtension.OnExtractResultsAsync(identity, claimedId.Value, message);
|
||||
context.ProtocolExtensionData[protocolExtension.GetType()] = result;
|
||||
}
|
||||
|
||||
await Options.Provider.Authenticated(context);
|
||||
|
||||
return new AuthenticationTicket(context.Identity, context.Properties);
|
||||
@@ -353,8 +370,18 @@ namespace Owin.Security.Providers.OpenID
|
||||
|
||||
"&openid.ax.required=" + Uri.EscapeDataString("email,name,first,last,email2,name2,first2,last2");
|
||||
|
||||
// allow protocol extensions to add their own attributes to the endpoint URL
|
||||
var endpoint = new OpenIDAuthorizationEndpointInfo()
|
||||
{
|
||||
Url = authorizationEndpoint
|
||||
};
|
||||
foreach (var protocolExtension in Options.ProtocolExtensions)
|
||||
{
|
||||
await protocolExtension.OnChallengeAsync(challenge, endpoint);
|
||||
}
|
||||
|
||||
Response.StatusCode = 302;
|
||||
Response.Headers.Set("Location", authorizationEndpoint);
|
||||
Response.Headers.Set("Location", endpoint.Url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Owin;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Owin;
|
||||
using Microsoft.Owin.Security;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
@@ -77,6 +78,12 @@ namespace Owin.Security.Providers.OpenID
|
||||
/// </summary>
|
||||
public string ProviderLoginUri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of protocol extensions.
|
||||
/// </summary>
|
||||
public List<IOpenIDProtocolExtension> ProtocolExtensions { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="OpenIDAuthenticationOptions"/>
|
||||
/// </summary>
|
||||
@@ -87,6 +94,7 @@ namespace Owin.Security.Providers.OpenID
|
||||
CallbackPath = new PathString("/signin-openid");
|
||||
AuthenticationMode = AuthenticationMode.Passive;
|
||||
BackchannelTimeout = TimeSpan.FromSeconds(60);
|
||||
ProtocolExtensions = new List<IOpenIDProtocolExtension>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Owin.Security.Providers.OpenID
|
||||
{
|
||||
public class OpenIDAuthorizationEndpointInfo
|
||||
{
|
||||
|
||||
public string Url { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Owin;
|
||||
using System;
|
||||
using Microsoft.Owin;
|
||||
using Microsoft.Owin.Security;
|
||||
using Microsoft.Owin.Security.Provider;
|
||||
using System.Collections.Generic;
|
||||
@@ -12,7 +13,7 @@ namespace Owin.Security.Providers.OpenID
|
||||
/// </summary>
|
||||
public class OpenIDAuthenticatedContext : BaseContext
|
||||
{
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="OpenIDAuthenticatedContext"/>
|
||||
/// </summary>
|
||||
/// <param name="context">The OWIN environment</param>
|
||||
@@ -32,6 +33,7 @@ namespace Owin.Security.Providers.OpenID
|
||||
Properties = properties;
|
||||
ResponseMessage = responseMessage;
|
||||
AttributeExchangeProperties = attributeExchangeProperties;
|
||||
ProtocolExtensionData = new Dictionary<Type, object>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -47,5 +49,7 @@ namespace Owin.Security.Providers.OpenID
|
||||
public XElement ResponseMessage { get; set; }
|
||||
|
||||
public IDictionary<string, string> AttributeExchangeProperties { get; private set; }
|
||||
|
||||
public IDictionary<Type, object> ProtocolExtensionData { get; private set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,12 +83,18 @@
|
||||
<Compile Include="LinkedIn\Provider\LinkedInReturnEndpointContext.cs" />
|
||||
<Compile Include="LinkedIn\Provider\ILinkedInAuthenticationProvider.cs" />
|
||||
<Compile Include="OpenID\Constants.cs" />
|
||||
<Compile Include="OpenID\Extensions\OpenIDSimpleRegistrationAuthenticationContextExtensions.cs" />
|
||||
<Compile Include="OpenID\Extensions\OpenIDSimpleRegistrationExtension.cs" />
|
||||
<Compile Include="OpenID\Extensions\OpenIDSimpleRegistrationResult.cs" />
|
||||
<Compile Include="OpenID\Extensions\OpenIDSimpleRegistrationField.cs" />
|
||||
<Compile Include="OpenID\Infrastructure\Message.cs" />
|
||||
<Compile Include="OpenID\Infrastructure\Property.cs" />
|
||||
<Compile Include="OpenID\IOpenIDProtocolExtension.cs" />
|
||||
<Compile Include="OpenID\OpenIDAuthenticationExtensions.cs" />
|
||||
<Compile Include="OpenID\OpenIDAuthenticationHandler.cs" />
|
||||
<Compile Include="OpenID\OpenIDAuthenticationMiddleware.cs" />
|
||||
<Compile Include="OpenID\OpenIDAuthenticationOptions.cs" />
|
||||
<Compile Include="OpenID\OpenIDAuthorizationEndpointInfo.cs" />
|
||||
<Compile Include="OpenID\Provider\IOpenIDAuthenticationProvider.cs" />
|
||||
<Compile Include="OpenID\Provider\OpenIDAuthenticatedContext.cs" />
|
||||
<Compile Include="OpenID\Provider\OpenIDAuthenticationProvider.cs" />
|
||||
|
||||
Reference in New Issue
Block a user