diff --git a/Owin.Security.Providers/Owin.Security.Providers.csproj b/Owin.Security.Providers/Owin.Security.Providers.csproj
index 9c0a55c..caeb775 100644
--- a/Owin.Security.Providers/Owin.Security.Providers.csproj
+++ b/Owin.Security.Providers/Owin.Security.Providers.csproj
@@ -124,6 +124,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Owin.Security.Providers/StackExchange/Constants.cs b/Owin.Security.Providers/StackExchange/Constants.cs
new file mode 100644
index 0000000..d186665
--- /dev/null
+++ b/Owin.Security.Providers/StackExchange/Constants.cs
@@ -0,0 +1,7 @@
+namespace Owin.Security.Providers.StackExchange
+{
+ internal static class Constants
+ {
+ public const string DefaultAuthenticationType = "StackExchange";
+ }
+}
\ No newline at end of file
diff --git a/Owin.Security.Providers/StackExchange/Provider/IStackExchangeAuthenticationProvider.cs b/Owin.Security.Providers/StackExchange/Provider/IStackExchangeAuthenticationProvider.cs
new file mode 100644
index 0000000..a99ee19
--- /dev/null
+++ b/Owin.Security.Providers/StackExchange/Provider/IStackExchangeAuthenticationProvider.cs
@@ -0,0 +1,24 @@
+using System.Threading.Tasks;
+
+namespace Owin.Security.Providers.StackExchange
+{
+ ///
+ /// Specifies callback methods which the invokes to enable developer control over the authentication process. />
+ ///
+ public interface IStackExchangeAuthenticationProvider
+ {
+ ///
+ /// Invoked whenever StackExchange succesfully authenticates a user
+ ///
+ /// Contains information about the login session as well as the user .
+ /// A representing the completed operation.
+ Task Authenticated(StackExchangeAuthenticatedContext context);
+
+ ///
+ /// Invoked prior to the being saved in a local cookie and the browser being redirected to the originally requested URL.
+ ///
+ ///
+ /// A representing the completed operation.
+ Task ReturnEndpoint(StackExchangeReturnEndpointContext context);
+ }
+}
\ No newline at end of file
diff --git a/Owin.Security.Providers/StackExchange/Provider/StackExchangeAuthenticatedContext.cs b/Owin.Security.Providers/StackExchange/Provider/StackExchangeAuthenticatedContext.cs
new file mode 100644
index 0000000..1291325
--- /dev/null
+++ b/Owin.Security.Providers/StackExchange/Provider/StackExchangeAuthenticatedContext.cs
@@ -0,0 +1,105 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+using System;
+using System.Globalization;
+using System.Security.Claims;
+using Microsoft.Owin;
+using Microsoft.Owin.Security;
+using Microsoft.Owin.Security.Provider;
+using Newtonsoft.Json.Linq;
+
+namespace Owin.Security.Providers.StackExchange
+{
+ ///
+ /// Contains information about the login session as well as the user .
+ ///
+ public class StackExchangeAuthenticatedContext : BaseContext
+ {
+ ///
+ /// Initializes a
+ ///
+ /// The OWIN environment
+ /// The JSON-serialized user
+ /// StackExchange Access token
+ /// Seconds until expiration
+ public StackExchangeAuthenticatedContext(IOwinContext context, JObject user, string accessToken, string expires)
+ : base(context)
+ {
+ User = user;
+ AccessToken = accessToken;
+
+ int expiresValue;
+ if (Int32.TryParse(expires, NumberStyles.Integer, CultureInfo.InvariantCulture, out expiresValue))
+ {
+ ExpiresIn = TimeSpan.FromSeconds(expiresValue);
+ }
+
+ Id = TryGetValue(user, "user_id");
+ Name = TryGetValue(user, "display_name");
+ Link = TryGetValue(user, "link");
+ UserName = TryGetValue(user, "display_name").Replace(" ", "");
+ ProfileImage = TryGetValue(user, "profile_image");
+ }
+
+ ///
+ /// Gets the JSON-serialized user
+ ///
+ ///
+ /// The endpoint https://api.stackexchange.com/2.2/me?order=desc&sort=reputation&site=stackoverflow&key={{key}}&access_token={{access_token}} returns a list of user accounts. This represents the first user account in that list.
+ /// The user object schema can be found here - http://api.stackexchange.com/docs/types/user
+ /// Sample user objects can be viewed in the response of /users API here - http://api.stackexchange.com/docs/users#order=desc&sort=reputation&filter=default&site=stackoverflow&run=true
+ ///
+ public JObject User { get; private set; }
+
+ ///
+ /// Gets the StackExchange access token
+ ///
+ public string AccessToken { get; private set; }
+
+ ///
+ /// Gets the StackExchange access token expiration time
+ ///
+ public TimeSpan? ExpiresIn { get; set; }
+
+ ///
+ /// Gets the StackExchange user ID
+ ///
+ public string Id { get; private set; }
+
+ ///
+ /// Gets the user's name
+ ///
+ public string Name { get; private set; }
+
+ ///
+ /// Gets the link to the StackExchange profile
+ ///
+ public string Link { get; private set; }
+
+ ///
+ /// Gets the StackExchange username
+ ///
+ public string UserName { get; private set; }
+
+ ///
+ /// Gets the StackExchange profile image URL
+ ///
+ public string ProfileImage { get; private set; }
+
+ ///
+ /// Gets the representing the user
+ ///
+ public ClaimsIdentity Identity { get; set; }
+
+ ///
+ /// Gets or sets a property bag for common authentication properties
+ ///
+ public AuthenticationProperties Properties { get; set; }
+
+ private static string TryGetValue(JObject user, string propertyName)
+ {
+ JToken value;
+ return user != null && user.TryGetValue(propertyName, out value) ? value.ToString() : null;
+ }
+ }
+}
diff --git a/Owin.Security.Providers/StackExchange/Provider/StackExchangeAuthenticationProvider.cs b/Owin.Security.Providers/StackExchange/Provider/StackExchangeAuthenticationProvider.cs
new file mode 100644
index 0000000..ce4fe91
--- /dev/null
+++ b/Owin.Security.Providers/StackExchange/Provider/StackExchangeAuthenticationProvider.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Threading.Tasks;
+
+namespace Owin.Security.Providers.StackExchange
+{
+ ///
+ /// Default implementation.
+ ///
+ public class StackExchangeAuthenticationProvider : IStackExchangeAuthenticationProvider
+ {
+ ///
+ /// Initializes a
+ ///
+ public StackExchangeAuthenticationProvider()
+ {
+ OnAuthenticated = context => Task.FromResult(null);
+ OnReturnEndpoint = context => Task.FromResult(null);
+ }
+
+ ///
+ /// Gets or sets the function that is invoked when the Authenticated method is invoked.
+ ///
+ public Func OnAuthenticated { get; set; }
+
+ ///
+ /// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked.
+ ///
+ public Func OnReturnEndpoint { get; set; }
+
+ ///
+ /// Invoked whenever StackExchange succesfully authenticates a user
+ ///
+ /// Contains information about the login session as well as the user .
+ /// A representing the completed operation.
+ public virtual Task Authenticated(StackExchangeAuthenticatedContext context)
+ {
+ return OnAuthenticated(context);
+ }
+
+ ///
+ /// Invoked prior to the being saved in a local cookie and the browser being redirected to the originally requested URL.
+ ///
+ ///
+ /// A representing the completed operation.
+ public virtual Task ReturnEndpoint(StackExchangeReturnEndpointContext context)
+ {
+ return OnReturnEndpoint(context);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Owin.Security.Providers/StackExchange/Provider/StackExchangeReturnEndpointContext.cs b/Owin.Security.Providers/StackExchange/Provider/StackExchangeReturnEndpointContext.cs
new file mode 100644
index 0000000..48c39a3
--- /dev/null
+++ b/Owin.Security.Providers/StackExchange/Provider/StackExchangeReturnEndpointContext.cs
@@ -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.StackExchange
+{
+ ///
+ /// Provides context information to middleware providers.
+ ///
+ public class StackExchangeReturnEndpointContext : ReturnEndpointContext
+ {
+ ///
+ ///
+ ///
+ /// OWIN environment
+ /// The authentication ticket
+ public StackExchangeReturnEndpointContext(
+ IOwinContext context,
+ AuthenticationTicket ticket)
+ : base(context, ticket)
+ {
+ }
+ }
+}
diff --git a/Owin.Security.Providers/StackExchange/StackExchangeAuthenticationExtensions.cs b/Owin.Security.Providers/StackExchange/StackExchangeAuthenticationExtensions.cs
new file mode 100644
index 0000000..79eb38e
--- /dev/null
+++ b/Owin.Security.Providers/StackExchange/StackExchangeAuthenticationExtensions.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace Owin.Security.Providers.StackExchange
+{
+ public static class StackExchangeAuthenticationExtensions
+ {
+ public static IAppBuilder UseStackExchangeAuthentication(this IAppBuilder app,
+ StackExchangeAuthenticationOptions options)
+ {
+ if (app == null)
+ throw new ArgumentNullException("app");
+ if (options == null)
+ throw new ArgumentNullException("options");
+
+ app.Use(typeof(StackExchangeAuthenticationMiddleware), app, options);
+
+ return app;
+ }
+
+ public static IAppBuilder UseStackExchangeAuthentication(this IAppBuilder app, string clientId, string clientSecret, string key)
+ {
+ return app.UseStackExchangeAuthentication(new StackExchangeAuthenticationOptions
+ {
+ ClientId = clientId,
+ ClientSecret = clientSecret,
+ Key = key
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/Owin.Security.Providers/StackExchange/StackExchangeAuthenticationHandler.cs b/Owin.Security.Providers/StackExchange/StackExchangeAuthenticationHandler.cs
new file mode 100644
index 0000000..4f690c1
--- /dev/null
+++ b/Owin.Security.Providers/StackExchange/StackExchangeAuthenticationHandler.cs
@@ -0,0 +1,260 @@
+using System.IO;
+using System.IO.Compression;
+using Microsoft.Owin;
+using Microsoft.Owin.Infrastructure;
+using Microsoft.Owin.Logging;
+using Microsoft.Owin.Security;
+using Microsoft.Owin.Security.Infrastructure;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Security.Claims;
+using System.Threading.Tasks;
+using System.Web;
+
+namespace Owin.Security.Providers.StackExchange
+{
+ public class StackExchangeAuthenticationHandler : AuthenticationHandler
+ {
+ private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
+ private const string TokenEndpoint = "https://stackexchange.com/oauth/access_token";
+ private const string UserInfoEndpoint = "https://api.stackexchange.com/2.2/me?order=desc&sort=reputation&site=stackoverflow";
+
+ private readonly ILogger logger;
+ private readonly HttpClient httpClient;
+
+ public StackExchangeAuthenticationHandler(HttpClient httpClient, ILogger logger)
+ {
+ this.httpClient = httpClient;
+ this.logger = logger;
+ }
+
+ protected override async Task AuthenticateCoreAsync()
+ {
+ AuthenticationProperties properties = null;
+
+ try
+ {
+ string code = null;
+ string state = null;
+
+ IReadableStringCollection query = Request.Query;
+ IList values = query.GetValues("code");
+ if (values != null && values.Count == 1)
+ {
+ code = values[0];
+ }
+ values = query.GetValues("state");
+ if (values != null && values.Count == 1)
+ {
+ state = values[0];
+ }
+
+ properties = Options.StateDataFormat.Unprotect(state);
+ if (properties == null)
+ {
+ return null;
+ }
+
+ // OAuth2 10.12 CSRF
+ if (!ValidateCorrelationId(properties, logger))
+ {
+ return new AuthenticationTicket(null, properties);
+ }
+
+ string requestPrefix = Request.Scheme + "://" + Request.Host;
+ string redirectUri = requestPrefix + Request.PathBase + Options.CallbackPath;
+
+ // Build up the body for the token request
+ var body = new List>();
+ body.Add(new KeyValuePair("code", code));
+ body.Add(new KeyValuePair("redirect_uri", redirectUri));
+ body.Add(new KeyValuePair("client_id", Options.ClientId));
+ body.Add(new KeyValuePair("client_secret", Options.ClientSecret));
+
+ // Request the token
+ HttpResponseMessage tokenResponse =
+ await httpClient.PostAsync(TokenEndpoint, new FormUrlEncodedContent(body));
+ tokenResponse.EnsureSuccessStatusCode();
+ string text = await tokenResponse.Content.ReadAsStringAsync();
+
+ // Deserializes the token response
+ //dynamic response = JsonConvert.DeserializeObject(text);
+ var responseTokens = HttpUtility.ParseQueryString(text);
+ string accessToken = responseTokens["access_token"];
+ string expires = responseTokens["expires"];
+
+ // Get the StackExchange user
+ HttpRequestMessage userRequest = new HttpRequestMessage(HttpMethod.Get, UserInfoEndpoint + "&access_token=" + Uri.EscapeDataString(accessToken) + "&key=" + Uri.EscapeDataString(Options.Key));
+ HttpResponseMessage graphResponse = await httpClient.SendAsync(userRequest, Request.CallCancelled);
+ graphResponse.EnsureSuccessStatusCode();
+
+ //According to http://api.stackexchange.com/docs/compression -
+ //Responses would always be compressed
+ //If Accept-Encoding request header is not set, compression would be GZIP
+ //If Content-Encoding is not specified in response header, assume GZIP
+ using(var stream = new GZipStream(await graphResponse.Content.ReadAsStreamAsync(), CompressionMode.Decompress))
+ {
+ var sr = new StreamReader(stream);
+ text= sr.ReadToEnd();
+ }
+
+ JObject userResponse = JObject.Parse(text);
+ JToken userAccounts;
+ JObject user = userResponse.TryGetValue("items", out userAccounts)
+ ? userAccounts.First as JObject
+ : null;
+
+ var context = new StackExchangeAuthenticatedContext(Context, user, accessToken, expires)
+ {
+ Identity = new ClaimsIdentity(
+ Options.AuthenticationType,
+ ClaimsIdentity.DefaultNameClaimType,
+ ClaimsIdentity.DefaultRoleClaimType)
+ };
+
+ if (!string.IsNullOrEmpty(context.Id))
+ {
+ context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.Id, XmlSchemaString, Options.AuthenticationType));
+ }
+ if (!string.IsNullOrEmpty(context.UserName))
+ {
+ context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.UserName, XmlSchemaString, Options.AuthenticationType));
+ }
+ if (!string.IsNullOrEmpty(context.Name))
+ {
+ context.Identity.AddClaim(new Claim("urn:stackexchange:name", context.Name, XmlSchemaString, Options.AuthenticationType));
+ }
+ if (!string.IsNullOrEmpty(context.Link))
+ {
+ context.Identity.AddClaim(new Claim("urn:stackexchange:url", context.Link, XmlSchemaString, Options.AuthenticationType));
+ }
+ if (!string.IsNullOrEmpty(context.AccessToken))
+ {
+ context.Identity.AddClaim(new Claim("urn:stackexchange:accesstoken", context.AccessToken, XmlSchemaString, Options.AuthenticationType));
+ }
+ if (!string.IsNullOrEmpty(context.ProfileImage))
+ {
+ context.Identity.AddClaim(new Claim("urn:stackexchange:profileimage", context.ProfileImage, XmlSchemaString, Options.AuthenticationType));
+ }
+ context.Properties = properties;
+
+ await Options.Provider.Authenticated(context);
+
+ return new AuthenticationTicket(context.Identity, context.Properties);
+ }
+ catch (Exception ex)
+ {
+ logger.WriteError(ex.Message);
+ }
+ return new AuthenticationTicket(null, properties);
+ }
+
+ protected override Task ApplyResponseChallengeAsync()
+ {
+ if (Response.StatusCode != 401)
+ {
+ return Task.FromResult(null);
+ }
+
+ AuthenticationResponseChallenge challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode);
+
+ if (challenge != null)
+ {
+ string baseUri =
+ Request.Scheme +
+ Uri.SchemeDelimiter +
+ Request.Host +
+ Request.PathBase;
+
+ string currentUri =
+ baseUri +
+ Request.Path +
+ Request.QueryString;
+
+ string redirectUri =
+ baseUri +
+ Options.CallbackPath;
+
+ AuthenticationProperties properties = challenge.Properties;
+ if (string.IsNullOrEmpty(properties.RedirectUri))
+ {
+ properties.RedirectUri = currentUri;
+ }
+
+ // OAuth2 10.12 CSRF
+ GenerateCorrelationId(properties);
+
+ // comma separated
+ string scope = string.Join(",", Options.Scope);
+
+ string state = Options.StateDataFormat.Protect(properties);
+
+ string authorizationEndpoint =
+ "https://stackexchange.com/oauth" +
+ "?client_id=" + Uri.EscapeDataString(Options.ClientId) +
+ "&redirect_uri=" + Uri.EscapeDataString(redirectUri) +
+ "&scope=" + Uri.EscapeDataString(scope) +
+ "&state=" + Uri.EscapeDataString(state);
+
+ Response.Redirect(authorizationEndpoint);
+ }
+
+ return Task.FromResult(null);
+ }
+
+ public override async Task InvokeAsync()
+ {
+ return await InvokeReplyPathAsync();
+ }
+
+ private async Task InvokeReplyPathAsync()
+ {
+ if (Options.CallbackPath.HasValue && Options.CallbackPath == Request.Path)
+ {
+ // TODO: error responses
+
+ AuthenticationTicket ticket = await AuthenticateAsync();
+ if (ticket == null)
+ {
+ logger.WriteWarning("Invalid return state, unable to redirect.");
+ Response.StatusCode = 500;
+ return true;
+ }
+
+ var context = new StackExchangeReturnEndpointContext(Context, ticket);
+ context.SignInAsAuthenticationType = Options.SignInAsAuthenticationType;
+ context.RedirectUri = ticket.Properties.RedirectUri;
+
+ await Options.Provider.ReturnEndpoint(context);
+
+ if (context.SignInAsAuthenticationType != null &&
+ context.Identity != null)
+ {
+ ClaimsIdentity grantIdentity = context.Identity;
+ if (!string.Equals(grantIdentity.AuthenticationType, context.SignInAsAuthenticationType, StringComparison.Ordinal))
+ {
+ grantIdentity = new ClaimsIdentity(grantIdentity.Claims, context.SignInAsAuthenticationType, grantIdentity.NameClaimType, grantIdentity.RoleClaimType);
+ }
+ Context.Authentication.SignIn(context.Properties, grantIdentity);
+ }
+
+ if (!context.IsRequestCompleted && context.RedirectUri != null)
+ {
+ string redirectUri = context.RedirectUri;
+ if (context.Identity == null)
+ {
+ // add a redirect hint that sign-in failed in some way
+ redirectUri = WebUtilities.AddQueryString(redirectUri, "error", "access_denied");
+ }
+ Response.Redirect(redirectUri);
+ context.RequestCompleted();
+ }
+
+ return context.IsRequestCompleted;
+ }
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Owin.Security.Providers/StackExchange/StackExchangeAuthenticationMiddleware.cs b/Owin.Security.Providers/StackExchange/StackExchangeAuthenticationMiddleware.cs
new file mode 100644
index 0000000..1e187bb
--- /dev/null
+++ b/Owin.Security.Providers/StackExchange/StackExchangeAuthenticationMiddleware.cs
@@ -0,0 +1,88 @@
+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.Properties;
+
+namespace Owin.Security.Providers.StackExchange
+{
+ public class StackExchangeAuthenticationMiddleware : AuthenticationMiddleware
+ {
+ private readonly HttpClient httpClient;
+ private readonly ILogger logger;
+
+ public StackExchangeAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app,
+ StackExchangeAuthenticationOptions options)
+ : base(next, options)
+ {
+ if (String.IsNullOrWhiteSpace(Options.ClientId))
+ throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
+ Resources.Exception_OptionMustBeProvided, "ClientId"));
+ if (String.IsNullOrWhiteSpace(Options.ClientSecret))
+ throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
+ Resources.Exception_OptionMustBeProvided, "ClientSecret"));
+ if (String.IsNullOrWhiteSpace(Options.Key))
+ throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
+ Resources.Exception_OptionMustBeProvided, "Key"));
+
+ logger = app.CreateLogger();
+
+ if (Options.Provider == null)
+ Options.Provider = new StackExchangeAuthenticationProvider();
+
+ if (Options.StateDataFormat == null)
+ {
+ IDataProtector dataProtector = app.CreateDataProtector(
+ typeof (StackExchangeAuthenticationMiddleware).FullName,
+ Options.AuthenticationType, "v1");
+ Options.StateDataFormat = new PropertiesDataFormat(dataProtector);
+ }
+
+ if (String.IsNullOrEmpty(Options.SignInAsAuthenticationType))
+ Options.SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType();
+
+ httpClient = new HttpClient(ResolveHttpMessageHandler(Options))
+ {
+ Timeout = Options.BackchannelTimeout,
+ MaxResponseContentBufferSize = 1024*1024*10
+ };
+ }
+
+ ///
+ /// Provides the object for processing
+ /// authentication-related requests.
+ ///
+ ///
+ /// An configured with the
+ /// supplied to the constructor.
+ ///
+ protected override AuthenticationHandler CreateHandler()
+ {
+ return new StackExchangeAuthenticationHandler(httpClient, logger);
+ }
+
+ private HttpMessageHandler ResolveHttpMessageHandler(StackExchangeAuthenticationOptions options)
+ {
+ HttpMessageHandler handler = options.BackchannelHttpHandler ?? new WebRequestHandler();
+
+ // If they provided a validator, apply it or fail.
+ if (options.BackchannelCertificateValidator != null)
+ {
+ // Set the cert validate callback
+ var webRequestHandler = handler as WebRequestHandler;
+ if (webRequestHandler == null)
+ {
+ throw new InvalidOperationException(Resources.Exception_ValidatorHandlerMismatch);
+ }
+ webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate;
+ }
+
+ return handler;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Owin.Security.Providers/StackExchange/StackExchangeAuthenticationOptions.cs b/Owin.Security.Providers/StackExchange/StackExchangeAuthenticationOptions.cs
new file mode 100644
index 0000000..8c008e8
--- /dev/null
+++ b/Owin.Security.Providers/StackExchange/StackExchangeAuthenticationOptions.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using Microsoft.Owin;
+using Microsoft.Owin.Security;
+
+namespace Owin.Security.Providers.StackExchange
+{
+ public class StackExchangeAuthenticationOptions : AuthenticationOptions
+ {
+ ///
+ /// Gets or sets the a pinned certificate validator to use to validate the endpoints used
+ /// in back channel communications belong to StackExchange.
+ ///
+ ///
+ /// The pinned certificate validator.
+ ///
+ ///
+ /// If this property is null then the default certificate checks are performed,
+ /// validating the subject name and if the signing chain is a trusted party.
+ ///
+ public ICertificateValidator BackchannelCertificateValidator { get; set; }
+
+ ///
+ /// The HttpMessageHandler used to communicate with StackExchange.
+ /// This cannot be set at the same time as BackchannelCertificateValidator unless the value
+ /// can be downcast to a WebRequestHandler.
+ ///
+ public HttpMessageHandler BackchannelHttpHandler { get; set; }
+
+ ///
+ /// Gets or sets timeout value in milliseconds for back channel communications with StackExchange.
+ ///
+ ///
+ /// The back channel timeout in milliseconds.
+ ///
+ public TimeSpan BackchannelTimeout { get; set; }
+
+ ///
+ /// 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-linkedin".
+ ///
+ public PathString CallbackPath { get; set; }
+
+ ///
+ /// Get or sets the text that the user can display on a sign in user interface.
+ ///
+ public string Caption
+ {
+ get { return Description.Caption; }
+ set { Description.Caption = value; }
+ }
+
+ ///
+ /// Gets or sets the StackExchange supplied Client ID
+ ///
+ public string ClientId { get; set; }
+
+ ///
+ /// Gets or sets the StackExchange supplied Client Secret
+ ///
+ public string ClientSecret { get; set; }
+
+ ///
+ /// Gets or sets the StackExchange supplied Key. This is different from Client Id and Secret.
+ ///
+ public string Key { get; set; }
+
+ ///
+ /// Gets or sets the used in the authentication events
+ ///
+ public IStackExchangeAuthenticationProvider Provider { get; set; }
+
+ ///
+ /// A list of permissions to request.
+ ///
+ public IList Scope { get; private set; }
+
+ ///
+ /// Gets or sets the name of another authentication middleware which will be responsible for actually issuing a user
+ /// .
+ ///
+ public string SignInAsAuthenticationType { get; set; }
+
+ ///
+ /// Gets or sets the type used to secure data handled by the middleware.
+ ///
+ public ISecureDataFormat StateDataFormat { get; set; }
+
+ ///
+ /// Initializes a new
+ ///
+ public StackExchangeAuthenticationOptions()
+ : base("StackExchange")
+ {
+ Caption = Constants.DefaultAuthenticationType;
+ CallbackPath = new PathString("/signin-stackexchange");
+ AuthenticationMode = AuthenticationMode.Passive;
+ Scope = new List();
+ BackchannelTimeout = TimeSpan.FromSeconds(60);
+ }
+ }
+}
\ No newline at end of file