diff --git a/Owin.Security.Providers/Imgur/ImgurAuthenticationDefaults.cs b/Owin.Security.Providers/Imgur/ImgurAuthenticationDefaults.cs
new file mode 100644
index 0000000..8aaa4ba
--- /dev/null
+++ b/Owin.Security.Providers/Imgur/ImgurAuthenticationDefaults.cs
@@ -0,0 +1,87 @@
+namespace Owin.Security.Providers.Imgur
+{
+ /// Configuration strings for the imgur provider.
+ internal static class ImgurAuthenticationDefaults
+ {
+ /// The error message for user authentication failure.
+ internal const string AccessDeniedErrorMessage = "access_denied";
+
+ /// The name of the access token property in the imgur authentication response.
+ internal const string AccessTokenPropertyName = "access_token";
+
+ /// The name of the account id property in the imgur authentication response.
+ internal const string AccountIdPropertyName = "account_id";
+
+ /// The name of the account username property in the imgur authentication response.
+ internal const string AccountUsernamePropertyName = "account_username";
+
+ /// The name of the provider.
+ internal const string AuthenticationType = "Imgur";
+
+ /// The grant type to be used.
+ internal const string AuthorizationCodeGrantType = "authorization_code";
+
+ /// The user authorization endpoint URL.
+ internal const string AuthorizationUrl = "https://api.imgur.com/oauth2/authorize";
+
+ /// The default callback path.
+ internal const string CallbackPath = "/signin-imgur";
+
+ /// The name of the application client id parameter.
+ internal const string ClientIdParameter = "client_id";
+
+ /// The name of the application client secret parameter.
+ internal const string ClientSecretParameter = "client_secret";
+
+ /// The name of the response code parameter.
+ internal const string CodeParameter = "code";
+
+ /// The code type of the authentication response.
+ internal const string CodeResponseType = "code";
+
+ /// The message for the communication failure error.
+ internal const string CommunicationFailureMessage = "An error occurred while talking with imgur's server.";
+
+ /// The message for the authentication response deserialization failure error.
+ internal const string DeserializationFailureMessage = "The deserialization of the imgur's response failed. Perhaps imgur changed the response format?";
+
+ /// The name of the error parameter.
+ internal const string ErrorParameter = "error";
+
+ /// The name of the access token duration property in the imgur authentication response.
+ internal const string ExpiresInPropertyName = "expires_in";
+
+ /// The name of the grant type parameter.
+ internal const string GrantTypeParameter = "grant_type";
+
+ /// The format to use to stringify s.
+ internal const string Int32Format = "D";
+
+ /// The message for the invalid authentication ticket error.
+ internal const string InvalidAuthenticationTicketMessage = "Invalid authentication ticket.";
+
+ /// The name of the refresh token property in the imgur authentication response.
+ internal const string RefreshInPropertyName = "refresh_token";
+
+ /// The name of the response type parameter.
+ internal const string ResponseTypeParameter = "response_type";
+
+ /// The name of the scope property in the imgur authentication response.
+ internal const string ScopePropertyName = "scope";
+
+ /// The name of the state parameter.
+ internal const string StateParameter = "state";
+
+ /// The name of the token type property in the imgur authentication response.
+ internal const string TokenTypePropertyName = "token_type";
+
+ /// The token exchange endpoint URL.
+ internal const string TokenUrl = "https://api.imgur.com/oauth2/token";
+
+ /// The version of the provider.
+ internal const string Version = "v1";
+
+ /// The string value type for s.
+ internal const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
+ }
+}
diff --git a/Owin.Security.Providers/Imgur/ImgurAuthenticationExtensions.cs b/Owin.Security.Providers/Imgur/ImgurAuthenticationExtensions.cs
new file mode 100644
index 0000000..0a29314
--- /dev/null
+++ b/Owin.Security.Providers/Imgur/ImgurAuthenticationExtensions.cs
@@ -0,0 +1,15 @@
+namespace Owin.Security.Providers.Imgur
+{
+ /// imgur extensions for the .
+ public static class ImgurAuthenticationExtensions
+ {
+ /// Configures the to use to authenticate user.
+ /// The OWIN to be configured.
+ /// The with the settings to be used by the .
+ /// The configured .
+ public static IAppBuilder UseImgurAuthentication(this IAppBuilder appBuilder, ImgurAuthenticationOptions options)
+ {
+ return appBuilder.Use(appBuilder, options);
+ }
+ }
+}
diff --git a/Owin.Security.Providers/Imgur/ImgurAuthenticationHandler.cs b/Owin.Security.Providers/Imgur/ImgurAuthenticationHandler.cs
new file mode 100644
index 0000000..dd9f289
--- /dev/null
+++ b/Owin.Security.Providers/Imgur/ImgurAuthenticationHandler.cs
@@ -0,0 +1,387 @@
+namespace Owin.Security.Providers.Imgur
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.IO;
+ using System.Net.Http;
+ using System.Security.Claims;
+ using System.Threading.Tasks;
+
+ using Microsoft.Owin.Infrastructure;
+ using Microsoft.Owin.Logging;
+ using Microsoft.Owin.Security;
+ using Microsoft.Owin.Security.Infrastructure;
+ using Microsoft.Owin.Security.Provider;
+
+ using Newtonsoft.Json;
+
+ using Owin.Security.Providers.Imgur.Provider;
+
+ ///
+ public class ImgurAuthenticationHandler : AuthenticationHandler
+ {
+ private readonly HttpClient httpClient;
+ private readonly ILogger logger;
+
+ /// Creates a new .
+ /// The to be used for back channel calls.
+ /// The to be used by the .
+ public ImgurAuthenticationHandler(HttpClient httpClient, ILogger logger)
+ {
+ if (httpClient == null)
+ {
+ throw new ArgumentNullException("httpClient");
+ }
+
+ if (logger == null)
+ {
+ throw new ArgumentNullException("logger");
+ }
+
+ this.httpClient = httpClient;
+ this.logger = logger;
+ }
+
+ /// Is called once by common code after initialization.
+ /// Return true if the request is handled by this , returns false if the request should be passed to the next .
+ public override async Task InvokeAsync()
+ {
+ if (!this.Options.CallbackPath.HasValue)
+ {
+ return false;
+ }
+
+ if (!this.Options.CallbackPath.Value.Equals(this.Request.Path.Value, StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ var ticket = await this.AuthenticateAsync();
+
+ if (ticket == null)
+ {
+ throw new Exception(ImgurAuthenticationDefaults.InvalidAuthenticationTicketMessage);
+ }
+
+ var context = this.GetImgurReturnEndpointContext(ticket);
+
+ await this.Options.Provider.ReturnEndpoint(context);
+
+ this.SignIn(context);
+
+ if (context.IsRequestCompleted || context.RedirectUri == null)
+ {
+ return context.IsRequestCompleted;
+ }
+
+ var location = GetRedirectLocation(context);
+
+ this.Response.Redirect(location);
+
+ context.RequestCompleted();
+
+ return context.IsRequestCompleted;
+ }
+
+ /// Handles authentication challenges by intercepting 401 responses.
+ /// A representing the completed operation.
+ protected override Task ApplyResponseChallengeAsync()
+ {
+ if (this.Response.StatusCode != 401)
+ {
+ return Task.FromResult(null);
+ }
+
+ var challenge = this.Helper.LookupChallenge(this.Options.AuthenticationType, this.Options.AuthenticationMode);
+
+ if (challenge == null)
+ {
+ return Task.FromResult(null);
+ }
+
+ if (string.IsNullOrWhiteSpace(challenge.Properties.RedirectUri))
+ {
+ challenge.Properties.RedirectUri = this.Request.Uri.AbsoluteUri;
+ }
+
+ this.GenerateCorrelationId(challenge.Properties);
+
+ var state = this.Options.StateDataFormat.Protect(challenge.Properties);
+ var authorizationUri = this.GetAuthorizationUri(state);
+
+ this.Response.Redirect(authorizationUri);
+
+ return Task.FromResult(null);
+ }
+
+ /// The core authentication logic which must be provided by the .
+ /// The ticket data provided by the authentication logic.
+ /// Will be invoked at most once per request. Do not call directly, call the wrapping Authenticate method instead.
+ protected override async Task AuthenticateCoreAsync()
+ {
+ if (this.Request.Query.Get(ImgurAuthenticationDefaults.ErrorParameter) != null)
+ {
+ return new AuthenticationTicket(null, null);
+ }
+
+ var code = this.Request.Query.Get(ImgurAuthenticationDefaults.CodeParameter);
+ var state = this.Request.Query.Get(ImgurAuthenticationDefaults.StateParameter);
+ var properties = this.Options.StateDataFormat.Unprotect(state);
+
+ if (properties == null)
+ {
+ return new AuthenticationTicket(null, null);
+ }
+
+ if (!this.ValidateCorrelationId(properties, this.logger))
+ {
+ return new AuthenticationTicket(null, properties);
+ }
+
+ var authenticationResponse = await this.GetAuthenticationResponseAsync(code);
+
+ if (authenticationResponse == null)
+ {
+ throw new Exception(ImgurAuthenticationDefaults.DeserializationFailureMessage);
+ }
+
+ var identity = this.GetIdentity(authenticationResponse);
+ var context = this.GetImgurAuthenticatedContext(authenticationResponse, identity, properties);
+
+ await this.Options.Provider.Authenticated(context);
+
+ return new AuthenticationTicket(context.Identity, context.Properties);
+ }
+
+ /// Gets the payload for the back channel authentication request.
+ /// The authorization code supplied by imgur.
+ /// The with the payload for the back channel authentication request.
+ private HttpContent GetAuthenticationRequestContent(string code)
+ {
+ return
+ new FormUrlEncodedContent(
+ new[]
+ {
+ new KeyValuePair(
+ ImgurAuthenticationDefaults.ClientIdParameter,
+ this.Options.ClientId),
+
+ new KeyValuePair(
+ ImgurAuthenticationDefaults.ClientSecretParameter,
+ this.Options.ClientSecret),
+
+ new KeyValuePair(
+ ImgurAuthenticationDefaults.GrantTypeParameter,
+ ImgurAuthenticationDefaults.AuthorizationCodeGrantType),
+
+ new KeyValuePair(
+ ImgurAuthenticationDefaults.CodeParameter,
+ code)
+ });
+ }
+
+ /// Gets the from imgur.
+ /// The authorization code supplied by imgur.
+ /// The from imgur.
+ private async Task GetAuthenticationResponseAsync(string code)
+ {
+ using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, ImgurAuthenticationDefaults.TokenUrl))
+ {
+ httpRequestMessage.Content = this.GetAuthenticationRequestContent(code);
+
+ using (var httpResponseMessage = await this.httpClient.SendAsync(httpRequestMessage, this.Request.CallCancelled))
+ {
+ if (!httpResponseMessage.IsSuccessStatusCode)
+ {
+ throw new Exception(ImgurAuthenticationDefaults.CommunicationFailureMessage);
+ }
+
+ using (var stream = await httpResponseMessage.Content.ReadAsStreamAsync())
+ {
+ var jsonSerializer = new JsonSerializer();
+
+ using (var streamReader = new StreamReader(stream))
+ {
+ using (var jsonTextReader = new JsonTextReader(streamReader))
+ {
+ return jsonSerializer.Deserialize(jsonTextReader);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /// Gets the authorization URL for the back channel call.
+ /// The encrypted for the current authentication session.
+ /// The authorization URL for the back channel call.
+ private string GetAuthorizationUri(string state)
+ {
+ var authorizationUri = ImgurAuthenticationDefaults.AuthorizationUrl;
+
+ authorizationUri =
+ WebUtilities.AddQueryString(
+ authorizationUri,
+ ImgurAuthenticationDefaults.ClientIdParameter,
+ Uri.EscapeDataString(this.Options.ClientId));
+
+ authorizationUri =
+ WebUtilities.AddQueryString(
+ authorizationUri,
+ ImgurAuthenticationDefaults.ResponseTypeParameter,
+ ImgurAuthenticationDefaults.CodeResponseType);
+
+ authorizationUri =
+ WebUtilities.AddQueryString(
+ authorizationUri,
+ ImgurAuthenticationDefaults.StateParameter,
+ Uri.EscapeDataString(state));
+
+ return authorizationUri;
+ }
+
+ /// Gets the for the identity of the user.
+ /// The returned by imgur.
+ /// The for the identity of the user.
+ private ClaimsIdentity GetIdentity(AuthenticationResponse authenticationResponse)
+ {
+ var identity =
+ new ClaimsIdentity(
+ this.Options.AuthenticationType,
+ ClaimsIdentity.DefaultNameClaimType,
+ ClaimsIdentity.DefaultRoleClaimType);
+
+ identity.AddClaim(
+ new Claim(
+ ClaimTypes.Name,
+ authenticationResponse.AccountUsername,
+ ImgurAuthenticationDefaults.XmlSchemaString,
+ this.Options.AuthenticationType));
+
+ identity.AddClaim(
+ new Claim(
+ ClaimTypes.NameIdentifier,
+ authenticationResponse.AccountId.ToString(ImgurAuthenticationDefaults.Int32Format, CultureInfo.InvariantCulture),
+ ImgurAuthenticationDefaults.XmlSchemaString,
+ this.Options.AuthenticationType));
+
+ identity.AddClaim(
+ new Claim(
+ ClaimsIdentity.DefaultNameClaimType,
+ authenticationResponse.AccountUsername,
+ ImgurAuthenticationDefaults.XmlSchemaString,
+ this.Options.AuthenticationType));
+
+ return identity;
+ }
+
+ /// Gets the for the current authentication session.
+ /// The returned by imgur.
+ /// The for the identity of the user.
+ /// The for the current authentication session.
+ /// The for the current authentication session.
+ private ImgurAuthenticatedContext GetImgurAuthenticatedContext(AuthenticationResponse authenticationResponse, ClaimsIdentity identity, AuthenticationProperties properties)
+ {
+ var context = new ImgurAuthenticatedContext(this.Context, this.Options);
+ context.AccessToken = authenticationResponse.AccessToken;
+ context.AccountId = authenticationResponse.AccountId;
+ context.AccountUsername = authenticationResponse.AccountUsername;
+ context.ExpiresIn = authenticationResponse.ExpiresIn;
+ context.Identity = identity;
+ context.Properties = properties;
+ context.RefreshToken = authenticationResponse.RefreshToken;
+ context.Scope = authenticationResponse.Scope;
+ context.TokenType = authenticationResponse.TokenType;
+
+ return context;
+ }
+
+ /// Gets the for the current authentication session.
+ /// The for the current authentication session.
+ /// The for the current authentication session.
+ private ImgurReturnEndpointContext GetImgurReturnEndpointContext(AuthenticationTicket ticket)
+ {
+ var context = new ImgurReturnEndpointContext(this.Context, ticket);
+ context.SignInAsAuthenticationType = this.Options.SignInAsAuthenticationType;
+ context.RedirectUri = ticket.Properties.RedirectUri;
+
+ return context;
+ }
+
+ /// Adds authentication information to the OWIN context to let the appropriate authenticate the user.
+ /// The for the current authentication session.
+ private void SignIn(ReturnEndpointContext context)
+ {
+ if (context.SignInAsAuthenticationType == null || context.Identity == null)
+ {
+ return;
+ }
+
+ var identity = context.Identity;
+
+ if (!identity.AuthenticationType.Equals(context.SignInAsAuthenticationType, StringComparison.OrdinalIgnoreCase))
+ {
+ identity =
+ new ClaimsIdentity(
+ identity.Claims,
+ context.SignInAsAuthenticationType,
+ identity.NameClaimType,
+ identity.RoleClaimType);
+ }
+
+ this.Context.Authentication.SignIn(context.Properties, identity);
+ }
+
+ /// Gets the URL where the user should be redirect to.
+ /// The for the current authentication session.
+ /// The URL where the user should be redirect to.
+ private static string GetRedirectLocation(ReturnEndpointContext context)
+ {
+ var location = context.RedirectUri;
+
+ if (context.Identity == null)
+ {
+ location =
+ WebUtilities.AddQueryString(
+ location,
+ ImgurAuthenticationDefaults.ErrorParameter,
+ ImgurAuthenticationDefaults.AccessDeniedErrorMessage);
+ }
+
+ return location;
+ }
+
+ /// Response payload returned by imgur with the information of the authenticated user.
+ private class AuthenticationResponse
+ {
+ /// Gets or sets the access token for the authenticated user.
+ [JsonProperty(PropertyName = ImgurAuthenticationDefaults.AccessTokenPropertyName)]
+ public string AccessToken { get; set; }
+
+ /// Gets or sets the account id of the authenticated user.
+ [JsonProperty(PropertyName = ImgurAuthenticationDefaults.AccountIdPropertyName)]
+ public int AccountId { get; set; }
+
+ /// Gets or sets the account username of the authenticated user.
+ [JsonProperty(PropertyName = ImgurAuthenticationDefaults.AccountUsernamePropertyName)]
+ public string AccountUsername { get; set; }
+
+ /// Gets or sets the duration of the access token.
+ [JsonProperty(PropertyName = ImgurAuthenticationDefaults.ExpiresInPropertyName)]
+ public int ExpiresIn { get; set; }
+
+ /// Gets or sets the refresh token for the authenticated user.
+ [JsonProperty(PropertyName = ImgurAuthenticationDefaults.RefreshInPropertyName)]
+ public string RefreshToken { get; set; }
+
+ /// Gets or sets the scope of the access token.
+ [JsonProperty(PropertyName = ImgurAuthenticationDefaults.ScopePropertyName)]
+ public string Scope { get; set; }
+
+ /// Gets or sets the type of the access token.
+ [JsonProperty(PropertyName = ImgurAuthenticationDefaults.TokenTypePropertyName)]
+ public string TokenType { get; set; }
+ }
+ }
+}
diff --git a/Owin.Security.Providers/Imgur/ImgurAuthenticationMiddleware.cs b/Owin.Security.Providers/Imgur/ImgurAuthenticationMiddleware.cs
new file mode 100644
index 0000000..94c89f4
--- /dev/null
+++ b/Owin.Security.Providers/Imgur/ImgurAuthenticationMiddleware.cs
@@ -0,0 +1,160 @@
+namespace Owin.Security.Providers.Imgur
+{
+ 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.Imgur.Provider;
+ using Owin.Security.Providers.Properties;
+
+ /// OWIN authentication middleware for imgur.
+ public class ImgurAuthenticationMiddleware : AuthenticationMiddleware
+ {
+ private readonly HttpClient httpClient;
+ private readonly ILogger logger;
+
+ private readonly static string TypeFullName = typeof(ImgurAuthenticationMiddleware).FullName;
+
+ /// Creates a new .
+ /// The next in the configuration chain.
+ /// The OWIN being configured.
+ /// The to be used to set up the .
+ public ImgurAuthenticationMiddleware(OwinMiddleware next, IAppBuilder appBuilder, ImgurAuthenticationOptions options)
+ : base(next, options)
+ {
+ if (appBuilder == null)
+ {
+ throw new ArgumentNullException("appBuilder");
+ }
+
+ if (options == null)
+ {
+ throw new ArgumentNullException("options");
+ }
+
+ this.CheckClientId();
+ this.CheckClientSecret();
+ this.SetProvider();
+ this.SetSignInAsAuthenticationType(appBuilder);
+ this.SetStateDataFormat(appBuilder);
+
+ var httpMessageHandler = ResolveHttpMessageHandler(this.Options);
+
+ this.httpClient = new HttpClient(httpMessageHandler);
+ this.logger = appBuilder.CreateLogger();
+ }
+
+ /// Creates the to be used by the .
+ /// The to be used by the .
+ protected override AuthenticationHandler CreateHandler()
+ {
+ return new ImgurAuthenticationHandler(this.httpClient, this.logger);
+ }
+
+ /// Checks that the imgur application client id has been set.
+ private void CheckClientId()
+ {
+ if (!string.IsNullOrWhiteSpace(this.Options.ClientId))
+ {
+ return;
+ }
+
+ var message =
+ string.Format(
+ CultureInfo.InvariantCulture,
+ Resources.Exception_OptionMustBeProvided,
+ "ClientId");
+
+ throw new ArgumentException(message, "options");
+ }
+
+ /// Checks that the imgur application client secret has been set.
+ private void CheckClientSecret()
+ {
+ if (!string.IsNullOrWhiteSpace(this.Options.ClientSecret))
+ {
+ return;
+ }
+
+ var message =
+ string.Format(
+ CultureInfo.InvariantCulture,
+ Resources.Exception_OptionMustBeProvided,
+ "ClientSecret");
+
+ throw new ArgumentException(message, "options");
+ }
+
+ /// Sets the provider to if it hasn't been set.
+ private void SetProvider()
+ {
+ if (this.Options.Provider != null)
+ {
+ return;
+ }
+
+ this.Options.Provider = new ImgurAuthenticationProvider();
+ }
+
+ /// Sets the name authentication middleware responsible for signing in the user if it hasn't been set.
+ /// The OWIN being configured.
+ private void SetSignInAsAuthenticationType(IAppBuilder appBuilder)
+ {
+ if (!string.IsNullOrWhiteSpace(this.Options.SignInAsAuthenticationType))
+ {
+ return;
+ }
+
+ this.Options.SignInAsAuthenticationType = appBuilder.GetDefaultSignInAsAuthenticationType();
+ }
+
+ /// Sets the data protector to if it hasn't been set.
+ /// The OWIN being configured.
+ private void SetStateDataFormat(IAppBuilder appBuilder)
+ {
+ if (this.Options.StateDataFormat != null)
+ {
+ return;
+ }
+
+ var dataProtector =
+ appBuilder.CreateDataProtector(
+ TypeFullName,
+ this.Options.AuthenticationType,
+ ImgurAuthenticationDefaults.Version);
+
+ this.Options.StateDataFormat = new PropertiesDataFormat(dataProtector);
+ }
+
+ /// Gets the to be used for the back channel calls.
+ /// The used to configure the .
+ /// The to be used for the back channel calls.
+ private static HttpMessageHandler ResolveHttpMessageHandler(ImgurAuthenticationOptions options)
+ {
+ var httpMessageHandler = options.BackchannelHttpHandler ?? new WebRequestHandler();
+
+ if (options.BackchannelCertificateValidator == null)
+ {
+ return httpMessageHandler;
+ }
+
+ var webRequestHandler = httpMessageHandler as WebRequestHandler;
+
+ if (webRequestHandler == null)
+ {
+ throw new InvalidOperationException(Resources.Exception_ValidatorHandlerMismatch);
+ }
+
+ webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate;
+
+ return webRequestHandler;
+ }
+ }
+}
diff --git a/Owin.Security.Providers/Imgur/ImgurAuthenticationOptions.cs b/Owin.Security.Providers/Imgur/ImgurAuthenticationOptions.cs
new file mode 100644
index 0000000..dbace7b
--- /dev/null
+++ b/Owin.Security.Providers/Imgur/ImgurAuthenticationOptions.cs
@@ -0,0 +1,68 @@
+namespace Owin.Security.Providers.Imgur
+{
+ using System;
+ using System.Net.Http;
+
+ using Microsoft.Owin;
+ using Microsoft.Owin.Security;
+
+ using Owin.Security.Providers.Imgur.Provider;
+
+ /// Configuration options for the .
+ public class ImgurAuthenticationOptions : AuthenticationOptions
+ {
+ /// Creates a new .
+ public ImgurAuthenticationOptions()
+ : base(ImgurAuthenticationDefaults.AuthenticationType)
+ {
+ this.AuthenticationMode = AuthenticationMode.Passive;
+ this.BackchannelTimeout = TimeSpan.FromSeconds(60);
+ this.CallbackPath = new PathString(ImgurAuthenticationDefaults.CallbackPath);
+ this.Caption = ImgurAuthenticationDefaults.AuthenticationType;
+ }
+
+ /// Gets or sets the a pinned certificate validator to use to validate the endpoints used in back channel communications belong to StackExchange.
+ /// 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 imgur.
+ 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.
+ /// The default value is "/signin-imgur".
+ 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 this.Description.Caption;
+ }
+
+ set
+ {
+ this.Description.Caption = value;
+ }
+ }
+
+ /// Gets or sets the imgur application client id.
+ public string ClientId { get; set; }
+
+ /// Gets or sets the imgur application client secret.
+ public string ClientSecret { get; set; }
+
+ /// Gets or sets the used in the authentication events.
+ public IImgurAuthenticationProvider Provider { get; 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 the data handled by the middleware.
+ public ISecureDataFormat StateDataFormat { get; set; }
+ }
+}
diff --git a/Owin.Security.Providers/Imgur/Provider/IImgurAuthenticationProvider.cs b/Owin.Security.Providers/Imgur/Provider/IImgurAuthenticationProvider.cs
new file mode 100644
index 0000000..6a79d2f
--- /dev/null
+++ b/Owin.Security.Providers/Imgur/Provider/IImgurAuthenticationProvider.cs
@@ -0,0 +1,18 @@
+namespace Owin.Security.Providers.Imgur.Provider
+{
+ using System.Threading.Tasks;
+
+ /// Specifies callback methods which the invokes to enable developers control over the authentication process.
+ public interface IImgurAuthenticationProvider
+ {
+ /// Invoked whenever imgur succesfully authenticates a user.
+ /// The that contains information about the login session and the user's .
+ /// A representing the completed operation.
+ Task Authenticated(ImgurAuthenticatedContext context);
+
+ /// Invoked prior to the being saved in a local cookie and the browser being redirected to the originally requested URL.
+ /// The of the authentication request.
+ /// A representing the completed operation.
+ Task ReturnEndpoint(ImgurReturnEndpointContext context);
+ }
+}
diff --git a/Owin.Security.Providers/Imgur/Provider/ImgurAuthenticatedContext.cs b/Owin.Security.Providers/Imgur/Provider/ImgurAuthenticatedContext.cs
new file mode 100644
index 0000000..38c8edd
--- /dev/null
+++ b/Owin.Security.Providers/Imgur/Provider/ImgurAuthenticatedContext.cs
@@ -0,0 +1,47 @@
+namespace Owin.Security.Providers.Imgur.Provider
+{
+ using System.Security.Claims;
+
+ using Microsoft.Owin;
+ using Microsoft.Owin.Security;
+ using Microsoft.Owin.Security.Provider;
+
+ /// Contains information about the login session and the user's .
+ public class ImgurAuthenticatedContext : BaseContext
+ {
+ /// Creates a new .
+ /// The OWIN context of the autentication request.
+ /// The used to set up the .
+ public ImgurAuthenticatedContext(IOwinContext context, ImgurAuthenticationOptions options)
+ : base(context, options)
+ {
+ }
+
+ /// Gets or sets the access token for the authenticated user.
+ public string AccessToken { get; set; }
+
+ /// Gets or sets the account id of the authenticated user.
+ public int AccountId { get; set; }
+
+ /// Gets or sets the account username of the authenticated user.
+ public string AccountUsername { get; set; }
+
+ /// Gets or sets the duration of the access token.
+ public int ExpiresIn { get; set; }
+
+ /// Gets or sets the for the authenticated user.
+ public ClaimsIdentity Identity { get; set; }
+
+ /// Gets or sets the of the authentication request.
+ public AuthenticationProperties Properties { get; set; }
+
+ /// Gets or sets the refresh token for the authenticated user.
+ public string RefreshToken { get; set; }
+
+ /// Gets or sets the scope of the access token.
+ public string Scope { get; set; }
+
+ /// Gets or sets the type of the access token.
+ public string TokenType { get; set; }
+ }
+}
diff --git a/Owin.Security.Providers/Imgur/Provider/ImgurAuthenticationProvider.cs b/Owin.Security.Providers/Imgur/Provider/ImgurAuthenticationProvider.cs
new file mode 100644
index 0000000..ff41b26
--- /dev/null
+++ b/Owin.Security.Providers/Imgur/Provider/ImgurAuthenticationProvider.cs
@@ -0,0 +1,38 @@
+namespace Owin.Security.Providers.Imgur.Provider
+{
+ using System;
+ using System.Threading.Tasks;
+
+ /// Default implementation.
+ public class ImgurAuthenticationProvider : IImgurAuthenticationProvider
+ {
+ /// Creates a new .
+ public ImgurAuthenticationProvider()
+ {
+ this.OnAuthenticated = context => Task.FromResult(null);
+ this.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 imgur succesfully authenticates a user.
+ /// The that contains information about the login session and the user's .
+ /// A representing the completed operation.
+ public Task Authenticated(ImgurAuthenticatedContext context)
+ {
+ return this.OnAuthenticated(context);
+ }
+
+ /// Invoked prior to the being saved in a local cookie and the browser being redirected to the originally requested URL.
+ /// The of the authentication request.
+ /// A representing the completed operation.
+ public Task ReturnEndpoint(ImgurReturnEndpointContext context)
+ {
+ return this.OnReturnEndpoint(context);
+ }
+ }
+}
diff --git a/Owin.Security.Providers/Imgur/Provider/ImgurReturnEndpointContext.cs b/Owin.Security.Providers/Imgur/Provider/ImgurReturnEndpointContext.cs
new file mode 100644
index 0000000..d4c59d0
--- /dev/null
+++ b/Owin.Security.Providers/Imgur/Provider/ImgurReturnEndpointContext.cs
@@ -0,0 +1,18 @@
+namespace Owin.Security.Providers.Imgur.Provider
+{
+ using Microsoft.Owin;
+ using Microsoft.Owin.Security;
+ using Microsoft.Owin.Security.Provider;
+
+ /// Provide context information to the middleware provider.
+ public class ImgurReturnEndpointContext : ReturnEndpointContext
+ {
+ /// Creates a new .
+ /// The OWIN context of the authentication request.
+ /// The of the authentication request.
+ public ImgurReturnEndpointContext(IOwinContext context, AuthenticationTicket ticket)
+ : base(context, ticket)
+ {
+ }
+ }
+}
diff --git a/Owin.Security.Providers/Owin.Security.Providers.csproj b/Owin.Security.Providers/Owin.Security.Providers.csproj
index 1e6063d..5dacbd8 100644
--- a/Owin.Security.Providers/Owin.Security.Providers.csproj
+++ b/Owin.Security.Providers/Owin.Security.Providers.csproj
@@ -178,6 +178,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs b/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs
index dc533c4..9781535 100755
--- a/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs
+++ b/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs
@@ -18,6 +18,7 @@ using Owin.Security.Providers.Gitter;
using Owin.Security.Providers.GooglePlus;
using Owin.Security.Providers.GooglePlus.Provider;
using Owin.Security.Providers.HealthGraph;
+using Owin.Security.Providers.Imgur;
using Owin.Security.Providers.Instagram;
using Owin.Security.Providers.LinkedIn;
using Owin.Security.Providers.OpenID;
@@ -248,6 +249,13 @@ namespace OwinOAuthProvidersDemo
// clientId: "",
// clientSecret: ""
//);
+
+ //app.UseImgurAuthentication(
+ // new ImgurAuthenticationOptions
+ // {
+ // ClientId = "",
+ // ClientSecret = ""
+ // });
}
}
}