Merge pull request #122 from kvoyk/master
Cosign provider for Identity Server
This commit is contained in:
7
Owin.Security.Providers/Cosign/Constants.cs
Normal file
7
Owin.Security.Providers/Cosign/Constants.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Owin.Security.Providers.Cosign
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
public const string DefaultAuthenticationType = "Cosign";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace Owin.Security.Providers.Cosign
|
||||
{
|
||||
public static class CosignAuthenticationExtensions
|
||||
{
|
||||
public static IAppBuilder UseCosignAuthentication(this IAppBuilder app,
|
||||
CosignAuthenticationOptions options)
|
||||
{
|
||||
if (app == null)
|
||||
throw new ArgumentNullException("app");
|
||||
if (options == null)
|
||||
throw new ArgumentNullException("options");
|
||||
|
||||
app.Use(typeof(CosignAuthenticationMiddleware), app, options);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
public static IAppBuilder UseCosignAuthentication(this IAppBuilder app, string clientId, string clientSecret)
|
||||
{
|
||||
return app.UseCosignAuthentication(new CosignAuthenticationOptions
|
||||
{
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
345
Owin.Security.Providers/Cosign/CosignAuthenticationHandler.cs
Normal file
345
Owin.Security.Providers/Cosign/CosignAuthenticationHandler.cs
Normal file
@@ -0,0 +1,345 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Eventing.Reader;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Owin;
|
||||
using Microsoft.Owin.Logging;
|
||||
using Microsoft.Owin.Security;
|
||||
using Microsoft.Owin.Security.Infrastructure;
|
||||
using Owin.Security.Providers.Cosign.Provider;
|
||||
|
||||
namespace Owin.Security.Providers.Cosign
|
||||
{
|
||||
public class CosignAuthenticationHandler : AuthenticationHandler<CosignAuthenticationOptions>
|
||||
{
|
||||
/*
|
||||
Cosign sends authenticated users to iis web site root (not to application).
|
||||
We need redirect user back to Identity Server application.
|
||||
This can be done with different approaches http handler, url rewrite...
|
||||
Here is UrlRewrite configuration
|
||||
<rewrite>
|
||||
<rules>
|
||||
<clear />
|
||||
<rule name="Cosign-RedirectCore1" enabled="true" stopProcessing="true">
|
||||
<match url="cosign/valid?" negate="false" />
|
||||
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
|
||||
<add input="{QUERY_STRING}" pattern="core=core1" />
|
||||
</conditions>
|
||||
<action type="Redirect" url="https://yourserver/host/path/signin-cosign" redirectType="SeeOther" />
|
||||
</rule>
|
||||
<rule name="Cosign-RedirectCore2" enabled="true" stopProcessing="true">
|
||||
<match url="cosign/valid?" negate="false" />
|
||||
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
|
||||
<add input="{QUERY_STRING}" pattern="core=core2" />
|
||||
</conditions>
|
||||
<action type="Redirect" url="https://yourserver/host/path/signin-cosign" redirectType="SeeOther" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
*/
|
||||
private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
|
||||
private readonly ILogger logger;
|
||||
|
||||
public CosignAuthenticationHandler(ILogger logger)
|
||||
{
|
||||
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
protected override Task<AuthenticationTicket> AuthenticateCoreAsync()
|
||||
{
|
||||
AuthenticationProperties properties = null;
|
||||
|
||||
try
|
||||
{
|
||||
string serviceCookieValue = null;
|
||||
string state = null;
|
||||
|
||||
/*BUG: IReadableStringCollection has a bug. Some charactes can be missed in the collection and replaces with blank space.
|
||||
Example: having "x" character in QueryString will result in having " " in the collection.
|
||||
I will use QueryString from Request object instead of IReadableStringCollection*/
|
||||
|
||||
//IReadableStringCollection query = Request.Query;
|
||||
//IList<string> values = query.GetValues("cosign-" + Options.ClientServer);
|
||||
//if (values != null && values.Count == 1)
|
||||
//{
|
||||
// serviceCookieValue = values[0];
|
||||
//}
|
||||
//values = query.GetValues("state");
|
||||
//if (values != null && values.Count == 1)
|
||||
//{
|
||||
// state = values[0];
|
||||
//}
|
||||
|
||||
string queryString = Request.QueryString.Value;
|
||||
string[] values = queryString.Split(new string[] {"&"}, StringSplitOptions.RemoveEmptyEntries);
|
||||
serviceCookieValue =
|
||||
values.First(a => a.Contains(Options.ClientServer))
|
||||
.Replace("cosign-" + Options.ClientServer + "=", "");
|
||||
state =
|
||||
values.First(a => a.Contains("state"))
|
||||
.Replace("state=", "");
|
||||
|
||||
properties = Options.StateDataFormat.Unprotect(state);
|
||||
if (properties == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
//// OAuth2 10.12 CSRF
|
||||
//if (!ValidateCorrelationId(properties, logger))
|
||||
//{
|
||||
// return new AuthenticationTicket(null, properties);
|
||||
//}
|
||||
|
||||
|
||||
// Get host related information.
|
||||
IPHostEntry hostEntry = Dns.GetHostEntry(Options.CosignServer);
|
||||
|
||||
// Loop through the AddressList to obtain the supported AddressFamily. This is to avoid
|
||||
// an exception that occurs when the host IP Address is not compatible with the address family
|
||||
// (typical in the IPv6 case).
|
||||
foreach (IPAddress address in hostEntry.AddressList)
|
||||
{
|
||||
IPEndPoint ipLocalEndPoint = new IPEndPoint(address, Options.CosignServicePort);
|
||||
|
||||
using (TcpClient tcpClient = new TcpClient())
|
||||
{
|
||||
|
||||
tcpClient.Connect(address, Options.CosignServicePort);
|
||||
if (tcpClient.Connected)
|
||||
{
|
||||
logger.WriteInformation("Cosign authenticaion handler. Connected to server ip: " + address);
|
||||
|
||||
//read message from connected server and validate response
|
||||
NetworkStream networkStream = tcpClient.GetStream();
|
||||
byte[] buffer = new byte[256];
|
||||
var bytesRead = networkStream.ReadAsync(buffer, 0, buffer.Length);
|
||||
var receivedData = Encoding.UTF8.GetString(buffer, 0, bytesRead.Result);
|
||||
//expected message: 220 2 Collaborative Web Single Sign-On [COSIGNv3 FACTORS=5 REKEY]
|
||||
if (receivedData.Substring(0, 3) != "220")
|
||||
continue;
|
||||
|
||||
//initiate secure negotiation and validate resonse
|
||||
//buffer = Encoding.UTF8.GetBytes("STARTTLS 2\r\n");
|
||||
buffer = Encoding.UTF8.GetBytes("STARTTLS 2" + Environment.NewLine);
|
||||
networkStream.Write(buffer, 0, buffer.Length);
|
||||
networkStream.Flush();
|
||||
buffer = new byte[256];
|
||||
bytesRead = networkStream.ReadAsync(buffer, 0, buffer.Length);
|
||||
receivedData = Encoding.UTF8.GetString(buffer, 0, bytesRead.Result);
|
||||
//expected message: 220 Ready to start TLS
|
||||
if (receivedData.Substring(0, 3) != "220")
|
||||
continue;
|
||||
|
||||
|
||||
SslStream sslStream = new SslStream(tcpClient.GetStream(), false, ValidateServerCertificate,
|
||||
null);
|
||||
|
||||
X509CertificateCollection certs = GetCertificateCertificateCollection(Options.ClientServer,
|
||||
StoreName.My,
|
||||
StoreLocation.LocalMachine);
|
||||
try
|
||||
{
|
||||
Task authResult = sslStream.AuthenticateAsClientAsync(Options.CosignServer, certs, SslProtocols.Tls, false);
|
||||
authResult.GetAwaiter().GetResult();
|
||||
}
|
||||
catch (AuthenticationException e)
|
||||
{
|
||||
logger.WriteError(e.Message);
|
||||
if (e.InnerException != null)
|
||||
{
|
||||
logger.WriteError(string.Format("Inner exception: {0}", e.InnerException.Message));
|
||||
}
|
||||
logger.WriteError("Authentication failed - closing the connection.");
|
||||
tcpClient.Close();
|
||||
continue;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.WriteError(ex.Message);
|
||||
tcpClient.Close();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!sslStream.IsEncrypted || !sslStream.IsSigned || !sslStream.IsMutuallyAuthenticated)
|
||||
continue;
|
||||
// The server name must match the name on the server certificate.
|
||||
if (!sslStream.IsAuthenticated)
|
||||
continue;
|
||||
|
||||
|
||||
buffer = new byte[256];
|
||||
bytesRead = sslStream.ReadAsync(buffer, 0, buffer.Length);
|
||||
receivedData = Encoding.UTF8.GetString(buffer, 0, bytesRead.Result);
|
||||
//expected message: 220 2 Collaborative Web Single Sign-On [COSIGNv3 FACTORS=5 REKEY]
|
||||
if (receivedData.Substring(0, 3) != "220")
|
||||
continue;
|
||||
|
||||
byte[] data =
|
||||
Encoding.UTF8.GetBytes("CHECK " + "cosign-" + Options.ClientServer + "=" +
|
||||
serviceCookieValue + Environment.NewLine);
|
||||
|
||||
sslStream.Write(data, 0, data.Length);
|
||||
sslStream.Flush();
|
||||
buffer = new byte[256];
|
||||
|
||||
bytesRead = sslStream.ReadAsync(buffer, 0, buffer.Length);
|
||||
receivedData = Encoding.UTF8.GetString(buffer, 0, bytesRead.Result);
|
||||
|
||||
|
||||
switch (receivedData.Substring(0, 1))
|
||||
{
|
||||
case "2":
|
||||
//Success
|
||||
logger.WriteInformation("Cosign authenticaion handler. 2-Response from Server: Success.");
|
||||
var context = new CosignAuthenticatedContext(Context, receivedData)
|
||||
{
|
||||
Identity = new ClaimsIdentity(
|
||||
Options.AuthenticationType,
|
||||
ClaimsIdentity.DefaultNameClaimType,
|
||||
ClaimsIdentity.DefaultRoleClaimType)
|
||||
};
|
||||
|
||||
|
||||
var identity = new ClaimsIdentity(Options.SignInAsAuthenticationType);
|
||||
if (!string.IsNullOrEmpty(context.Id))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.Id,
|
||||
XmlSchemaString, Options.AuthenticationType));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(context.UserId))
|
||||
{
|
||||
identity.AddClaim(new Claim("UserId", context.UserId, XmlSchemaString,
|
||||
Options.AuthenticationType));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(context.IpAddress))
|
||||
{
|
||||
identity.AddClaim(new Claim("IpAddress", context.IpAddress, XmlSchemaString,
|
||||
Options.AuthenticationType));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(context.Realm))
|
||||
{
|
||||
identity.AddClaim(new Claim("Realm", context.Realm, XmlSchemaString,
|
||||
Options.AuthenticationType));
|
||||
}
|
||||
|
||||
context.Properties = properties;
|
||||
|
||||
return Task.FromResult(new AuthenticationTicket(identity, properties));
|
||||
|
||||
|
||||
case "4":
|
||||
//Logged out
|
||||
logger.WriteInformation("Cosign authenticaion handler. Response from Server: 4-Logged out.");
|
||||
break;
|
||||
case "5":
|
||||
//Try a different server
|
||||
logger.WriteInformation("Cosign authenticaion handler. Response from Server: 5-Try different server.");
|
||||
break;
|
||||
default:
|
||||
logger.WriteInformation("Cosign authenticaion handler. Response from Server: Undefined.");
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.WriteError(ex.Message);
|
||||
}
|
||||
|
||||
|
||||
return Task.FromResult(new AuthenticationTicket(null, properties));
|
||||
}
|
||||
|
||||
protected override Task ApplyResponseChallengeAsync()
|
||||
{
|
||||
if (Response.StatusCode == 401)
|
||||
{
|
||||
var challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode);
|
||||
|
||||
// Only react to 401 if there is an authentication challenge for the authentication
|
||||
// type of this handler.
|
||||
if (challenge != null)
|
||||
{
|
||||
var state = challenge.Properties;
|
||||
|
||||
if (string.IsNullOrEmpty(state.RedirectUri))
|
||||
{
|
||||
state.RedirectUri = Request.Uri.ToString();
|
||||
}
|
||||
|
||||
var stateString = Options.StateDataFormat.Protect(state);
|
||||
|
||||
string loginUrl =
|
||||
"https://" + Options.CosignServer + "/?cosign-" + Options.ClientServer +
|
||||
"&state=" + Uri.EscapeDataString(stateString) +
|
||||
"&core=" + Options.IdentityServerHostInstance;
|
||||
logger.WriteInformation("Cosign authenticaion handler. Redirecting to cosign. " + loginUrl);
|
||||
Response.Redirect(loginUrl);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
|
||||
public override async Task<bool> InvokeAsync()
|
||||
{
|
||||
// This is always invoked on each request. For passive middleware, only do anything if this is
|
||||
// for our callback path when the user is redirected back from the authentication provider.
|
||||
if (Options.CallbackPath.HasValue && Options.CallbackPath == Request.Path)
|
||||
{
|
||||
var ticket = await AuthenticateAsync();
|
||||
|
||||
if (ticket != null)
|
||||
{
|
||||
Context.Authentication.SignIn(ticket.Properties, ticket.Identity);
|
||||
|
||||
Response.Redirect(ticket.Properties.RedirectUri);
|
||||
|
||||
// Prevent further processing by the owin pipeline.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Let the rest of the pipeline run.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static X509CertificateCollection GetCertificateCertificateCollection(string subjectName,
|
||||
StoreName storeName,
|
||||
StoreLocation storeLocation)
|
||||
{
|
||||
// The following code gets the cert from the keystore
|
||||
X509Store store = new X509Store(storeName, storeLocation);
|
||||
store.Open(OpenFlags.ReadOnly);
|
||||
X509Certificate2Collection certCollection =
|
||||
store.Certificates.Find(X509FindType.FindBySubjectName,
|
||||
subjectName,
|
||||
false);
|
||||
return certCollection;
|
||||
}
|
||||
|
||||
private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain,
|
||||
SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
if (sslPolicyErrors == SslPolicyErrors.None)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net.Http;
|
||||
using Microsoft.Owin;
|
||||
using Microsoft.Owin.Logging;
|
||||
using Microsoft.Owin.Security;
|
||||
using Microsoft.Owin.Security.DataHandler;
|
||||
using Microsoft.Owin.Security.DataProtection;
|
||||
using Microsoft.Owin.Security.Infrastructure;
|
||||
using Owin.Security.Providers.Cosign.Provider;
|
||||
using Owin.Security.Providers.Properties;
|
||||
|
||||
namespace Owin.Security.Providers.Cosign
|
||||
{
|
||||
public class CosignAuthenticationMiddleware : AuthenticationMiddleware<CosignAuthenticationOptions>
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
public CosignAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, CosignAuthenticationOptions options)
|
||||
: base(next, options)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(Options.ClientServer))
|
||||
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
|
||||
Resources.Exception_OptionMustBeProvided, "ClientServer"));
|
||||
if (String.IsNullOrWhiteSpace(Options.CosignServer))
|
||||
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
|
||||
Resources.Exception_OptionMustBeProvided, "CosignServer"));
|
||||
if ((Options.CosignServicePort==0))
|
||||
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
|
||||
Resources.Exception_OptionMustBeProvided, "CosignServicePort"));
|
||||
|
||||
|
||||
logger = app.CreateLogger<CosignAuthenticationMiddleware>();
|
||||
logger.WriteInformation("CosignAthenticationMiddleware has been created");
|
||||
if (Options.Provider == null)
|
||||
Options.Provider = new CosignAuthenticationProvider();
|
||||
|
||||
if (string.IsNullOrEmpty(Options.SignInAsAuthenticationType))
|
||||
{
|
||||
options.SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType();
|
||||
}
|
||||
if (options.StateDataFormat == null)
|
||||
{
|
||||
var dataProtector = app.CreateDataProtector(typeof (CosignAuthenticationMiddleware).FullName,
|
||||
options.AuthenticationType);
|
||||
|
||||
options.StateDataFormat = new PropertiesDataFormat(dataProtector);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected override AuthenticationHandler<CosignAuthenticationOptions> CreateHandler()
|
||||
{
|
||||
return new CosignAuthenticationHandler(logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Net.Http;
|
||||
using Microsoft.Owin;
|
||||
using Microsoft.Owin.Security;
|
||||
using Owin.Security.Providers.Cosign.Provider;
|
||||
|
||||
namespace Owin.Security.Providers.Cosign
|
||||
{
|
||||
public class CosignAuthenticationOptions : AuthenticationOptions
|
||||
{
|
||||
|
||||
|
||||
//full login url https://weblogin.umich.edu/?cosign-www.kbv.law.umich.edu&http://www.kbv.law.umich.edu/IdentityServer/core1
|
||||
//return url https://www.kbv.law.umich.edu/cosign/valid?cosign-www.kbv.law.umich.edu=COSIGN_VALUE&http://www.kbv.law.umich.edu/IdentityServer/core1
|
||||
//private const string LoginEndPoint = "https://weblogin.umich.edu/?";
|
||||
//private const string cosignServer = "weblogin.umich.edu";
|
||||
//private const string clientServer = "www.kbv.law.umich.edu";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The request path within the application's base path where the user-agent will be returned.
|
||||
/// The middleware will process this request when it arrives.
|
||||
/// Default value is "/signin-cosign".
|
||||
/// </summary>
|
||||
public PathString CallbackPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Cosgin server
|
||||
/// </summary>
|
||||
public string CosignServer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the instance of Identity Server Host
|
||||
/// </summary>
|
||||
public string IdentityServerHostInstance { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Cosign service name
|
||||
/// </summary>
|
||||
public string ClientServer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Cosign service port
|
||||
/// </summary>
|
||||
public int CosignServicePort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ICosignAuthenticationProvider" /> used in the authentication events
|
||||
/// </summary>
|
||||
public ICosignAuthenticationProvider Provider { get; set; }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of another authentication middleware which will be responsible for actually issuing a user
|
||||
/// <see cref="System.Security.Claims.ClaimsIdentity" />.
|
||||
/// </summary>
|
||||
public string SignInAsAuthenticationType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type used to secure data handled by the middleware.
|
||||
/// </summary>
|
||||
public ISecureDataFormat<AuthenticationProperties> StateDataFormat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="CosignAuthenticationOptions" />
|
||||
/// </summary>
|
||||
public CosignAuthenticationOptions(): base("Cosign")
|
||||
{
|
||||
//CosignServer = cosignServer;
|
||||
//ClientServer = clientServer;
|
||||
Description.Caption = Constants.DefaultAuthenticationType;
|
||||
CallbackPath = new PathString("/signin-cosign");
|
||||
AuthenticationMode = AuthenticationMode.Passive;
|
||||
IdentityServerHostInstance = "";
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Microsoft.Owin;
|
||||
using Microsoft.Owin.Security;
|
||||
using Microsoft.Owin.Security.Provider;
|
||||
|
||||
namespace Owin.Security.Providers.Cosign.Provider
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.
|
||||
/// </summary>
|
||||
public class CosignAuthenticatedContext : BaseContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="CosignAuthenticatedContext"/>
|
||||
/// </summary>
|
||||
/// <param name="context">The OWIN environment</param>
|
||||
/// <param name="cosignResponse">Response from Cosign server</param>
|
||||
public CosignAuthenticatedContext(IOwinContext context, string cosignResponse): base(context)
|
||||
{
|
||||
CosignResposponse = cosignResponse;
|
||||
string[] returnedData = CosignResposponse.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
|
||||
Id = TryGetValue(returnedData, "id");
|
||||
UserId = TryGetValue(returnedData, "userid");
|
||||
IpAddress = TryGetValue(returnedData, "ipaddress");
|
||||
Realm = TryGetValue(returnedData, "realm");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Cosign response
|
||||
/// </summary>
|
||||
public string CosignResposponse { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Cosign ID
|
||||
/// </summary>
|
||||
public string Id { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Cosign userId
|
||||
/// </summary>
|
||||
public string UserId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ClaimsIdentity"/> representing the user identity
|
||||
/// </summary>
|
||||
public ClaimsIdentity Identity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IpAddress"/> representing the user ipaddress
|
||||
/// </summary>
|
||||
public string IpAddress { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Realm"/> representing the user realm
|
||||
/// </summary>
|
||||
public string Realm { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a property bag for common authentication properties
|
||||
/// </summary>
|
||||
public AuthenticationProperties Properties { get; set; }
|
||||
|
||||
private static string TryGetValue(string[] cosignData, string propertyName)
|
||||
{
|
||||
|
||||
switch (propertyName.ToLower())
|
||||
{
|
||||
case "ipaddress":
|
||||
if (cosignData.GetUpperBound(0)>=0)
|
||||
return cosignData[1];
|
||||
return "";
|
||||
case "userid":
|
||||
if (cosignData.GetUpperBound(0) >= 1)
|
||||
return cosignData[2];
|
||||
return "";
|
||||
case "id":
|
||||
if (cosignData.GetUpperBound(0) >= 1)
|
||||
return sha256_hash( cosignData[2]);
|
||||
return "";
|
||||
case "realm":
|
||||
if (cosignData.GetUpperBound(0) >=2)
|
||||
return cosignData[3].Trim(new char[]{ Environment.NewLine.ToCharArray()[0]} );
|
||||
return "";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static string sha256_hash(string value)
|
||||
{
|
||||
StringBuilder Sb = new StringBuilder();
|
||||
|
||||
using (SHA256 hash = SHA256.Create())
|
||||
{
|
||||
Encoding enc = Encoding.UTF8;
|
||||
byte[] result = hash.ComputeHash(enc.GetBytes(value));
|
||||
|
||||
foreach (byte b in result)
|
||||
Sb.Append(b.ToString("x2"));
|
||||
}
|
||||
|
||||
return Sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Owin.Security.Providers.Cosign.Provider
|
||||
{
|
||||
/// <summary>
|
||||
/// Default <see cref="ICosignAuthenticationProvider"/> implementation.
|
||||
/// </summary>
|
||||
public class CosignAuthenticationProvider : ICosignAuthenticationProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="CosignAuthenticationProvider"/>
|
||||
/// </summary>
|
||||
public CosignAuthenticationProvider()
|
||||
{
|
||||
OnAuthenticated = context => Task.FromResult<object>(null);
|
||||
OnReturnEndpoint = context => Task.FromResult<object>(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the function that is invoked when the Authenticated method is invoked.
|
||||
/// </summary>
|
||||
public Func<CosignAuthenticatedContext, Task> OnAuthenticated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked.
|
||||
/// </summary>
|
||||
public Func<CosignReturnEndpointContext, Task> OnReturnEndpoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked whenever Cosign succesfully authenticates a user
|
||||
/// </summary>
|
||||
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
public virtual Task Authenticated(CosignAuthenticatedContext context)
|
||||
{
|
||||
return OnAuthenticated(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked prior to the <see cref="System.Security.Claims.ClaimsIdentity"/> being saved in a local cookie and the browser being redirected to the originally requested URL.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
public virtual Task ReturnEndpoint(CosignReturnEndpointContext context)
|
||||
{
|
||||
return OnReturnEndpoint(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Owin;
|
||||
using Microsoft.Owin.Security;
|
||||
using Microsoft.Owin.Security.Provider;
|
||||
|
||||
namespace Owin.Security.Providers.Cosign.Provider
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides context information to middleware providers.
|
||||
/// </summary>
|
||||
public class CosignReturnEndpointContext : ReturnEndpointContext
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="context">OWIN environment</param>
|
||||
/// <param name="ticket">The authentication ticket</param>
|
||||
public CosignReturnEndpointContext(
|
||||
IOwinContext context,
|
||||
AuthenticationTicket ticket)
|
||||
: base(context, ticket)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Owin.Security.Providers.Cosign.Provider
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies callback methods which the <see cref="CosignAuthenticationMiddleware"></see> invokes to enable developer control over the authentication process. />
|
||||
/// </summary>
|
||||
public interface ICosignAuthenticationProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked whenever Cosign succesfully authenticates a user
|
||||
/// </summary>
|
||||
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
Task Authenticated(CosignAuthenticatedContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked prior to the <see cref="System.Security.Claims.ClaimsIdentity"/> being saved in a local cookie and the browser being redirected to the originally requested URL.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
Task ReturnEndpoint(CosignReturnEndpointContext context);
|
||||
}
|
||||
}
|
||||
@@ -274,6 +274,16 @@ namespace OwinOAuthProvidersDemo
|
||||
|
||||
//app.UseBacklogAuthentication(options);
|
||||
|
||||
//var cosignOptions = new CosignAuthenticationOptions
|
||||
//{
|
||||
// AuthenticationType = "Cosign",
|
||||
// SignInAsAuthenticationType = signInAsType,
|
||||
// CosignServer = "weblogin.umich.edu",
|
||||
// CosignServicePort = 6663,
|
||||
// IdentityServerHostInstance = "core1",
|
||||
// ClientServer = "cosignservername"
|
||||
//};
|
||||
//app.UseCosignAuthentication(cosignOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user