Merge branch 'kappa7194-imgur'

This commit is contained in:
Jerrie Pelser
2015-07-17 10:06:01 +07:00
11 changed files with 855 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
namespace Owin.Security.Providers.Imgur
{
/// <summary>Configuration strings for the imgur provider.</summary>
internal static class ImgurAuthenticationDefaults
{
/// <summary>The error message for user authentication failure.</summary>
internal const string AccessDeniedErrorMessage = "access_denied";
/// <summary>The name of the access token property in the imgur authentication response.</summary>
internal const string AccessTokenPropertyName = "access_token";
/// <summary>The name of the account id property in the imgur authentication response.</summary>
internal const string AccountIdPropertyName = "account_id";
/// <summary>The name of the account username property in the imgur authentication response.</summary>
internal const string AccountUsernamePropertyName = "account_username";
/// <summary>The name of the provider.</summary>
internal const string AuthenticationType = "Imgur";
/// <summary>The grant type to be used.</summary>
internal const string AuthorizationCodeGrantType = "authorization_code";
/// <summary>The user authorization endpoint URL.</summary>
internal const string AuthorizationUrl = "https://api.imgur.com/oauth2/authorize";
/// <summary>The default callback path.</summary>
internal const string CallbackPath = "/signin-imgur";
/// <summary>The name of the application client id parameter.</summary>
internal const string ClientIdParameter = "client_id";
/// <summary>The name of the application client secret parameter.</summary>
internal const string ClientSecretParameter = "client_secret";
/// <summary>The name of the response code parameter.</summary>
internal const string CodeParameter = "code";
/// <summary>The code type of the authentication response.</summary>
internal const string CodeResponseType = "code";
/// <summary>The message for the communication failure error.</summary>
internal const string CommunicationFailureMessage = "An error occurred while talking with imgur's server.";
/// <summary>The message for the authentication response deserialization failure error.</summary>
internal const string DeserializationFailureMessage = "The deserialization of the imgur's response failed. Perhaps imgur changed the response format?";
/// <summary>The name of the error parameter.</summary>
internal const string ErrorParameter = "error";
/// <summary>The name of the access token duration property in the imgur authentication response.</summary>
internal const string ExpiresInPropertyName = "expires_in";
/// <summary>The name of the grant type parameter.</summary>
internal const string GrantTypeParameter = "grant_type";
/// <summary>The format to use to stringify <see cref="System.Int32"/>s.</summary>
internal const string Int32Format = "D";
/// <summary>The message for the invalid authentication ticket error.</summary>
internal const string InvalidAuthenticationTicketMessage = "Invalid authentication ticket.";
/// <summary>The name of the refresh token property in the imgur authentication response.</summary>
internal const string RefreshInPropertyName = "refresh_token";
/// <summary>The name of the response type parameter.</summary>
internal const string ResponseTypeParameter = "response_type";
/// <summary>The name of the scope property in the imgur authentication response.</summary>
internal const string ScopePropertyName = "scope";
/// <summary>The name of the state parameter.</summary>
internal const string StateParameter = "state";
/// <summary>The name of the token type property in the imgur authentication response.</summary>
internal const string TokenTypePropertyName = "token_type";
/// <summary>The token exchange endpoint URL.</summary>
internal const string TokenUrl = "https://api.imgur.com/oauth2/token";
/// <summary>The version of the provider.</summary>
internal const string Version = "v1";
/// <summary>The string value type for <see cref="System.Security.Claims.Claim"/>s.</summary>
internal const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
}
}

View File

@@ -0,0 +1,15 @@
namespace Owin.Security.Providers.Imgur
{
/// <summary>imgur extensions for the <see cref="IAppBuilder"/>.</summary>
public static class ImgurAuthenticationExtensions
{
/// <summary>Configures the <see cref="IAppBuilder"/> to use <see cref="ImgurAuthenticationMiddleware"/> to authenticate user.</summary>
/// <param name="appBuilder">The OWIN <see cref="IAppBuilder"/> to be configured.</param>
/// <param name="options">The <see cref="ImgurAuthenticationOptions"/> with the settings to be used by the <see cref="ImgurAuthenticationMiddleware"/>.</param>
/// <returns>The configured <see cref="IAppBuilder"/>.</returns>
public static IAppBuilder UseImgurAuthentication(this IAppBuilder appBuilder, ImgurAuthenticationOptions options)
{
return appBuilder.Use<ImgurAuthenticationMiddleware>(appBuilder, options);
}
}
}

View File

@@ -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;
/// <summary></summary>
public class ImgurAuthenticationHandler : AuthenticationHandler<ImgurAuthenticationOptions>
{
private readonly HttpClient httpClient;
private readonly ILogger logger;
/// <summary>Creates a new <see cref="ImgurAuthenticationHandler"/>.</summary>
/// <param name="httpClient">The <see cref="HttpClient"/> to be used for back channel calls.</param>
/// <param name="logger">The <see cref="ILogger"/> to be used by the <see cref="ImgurAuthenticationHandler"/>.</param>
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;
}
/// <summary>Is called once by common code after initialization.</summary>
/// <returns>Return true if the request is handled by this <see cref="AuthenticationMiddleware{TOptions}"/>, returns false if the request should be passed to the next <see cref="AuthenticationMiddleware{TOptions}"/>.</returns>
public override async Task<bool> 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;
}
/// <summary>Handles authentication challenges by intercepting 401 responses.</summary>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
protected override Task ApplyResponseChallengeAsync()
{
if (this.Response.StatusCode != 401)
{
return Task.FromResult<object>(null);
}
var challenge = this.Helper.LookupChallenge(this.Options.AuthenticationType, this.Options.AuthenticationMode);
if (challenge == null)
{
return Task.FromResult<object>(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<object>(null);
}
/// <summary>The core authentication logic which must be provided by the <see cref="AuthenticationHandler{TOptions}"/>.</summary>
/// <returns>The ticket data provided by the authentication logic.</returns>
/// <remarks>Will be invoked at most once per request. Do not call directly, call the wrapping Authenticate method instead.</remarks>
protected override async Task<AuthenticationTicket> 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);
}
/// <summary>Gets the payload for the back channel authentication request.</summary>
/// <param name="code">The authorization code supplied by imgur.</param>
/// <returns>The <see cref="HttpContent"/> with the payload for the back channel authentication request.</returns>
private HttpContent GetAuthenticationRequestContent(string code)
{
return
new FormUrlEncodedContent(
new[]
{
new KeyValuePair<string, string>(
ImgurAuthenticationDefaults.ClientIdParameter,
this.Options.ClientId),
new KeyValuePair<string, string>(
ImgurAuthenticationDefaults.ClientSecretParameter,
this.Options.ClientSecret),
new KeyValuePair<string, string>(
ImgurAuthenticationDefaults.GrantTypeParameter,
ImgurAuthenticationDefaults.AuthorizationCodeGrantType),
new KeyValuePair<string, string>(
ImgurAuthenticationDefaults.CodeParameter,
code)
});
}
/// <summary>Gets the <see cref="AuthenticationResponse"/> from imgur.</summary>
/// <param name="code">The authorization code supplied by imgur.</param>
/// <returns>The <see cref="AuthenticationResponse"/> from imgur.</returns>
private async Task<AuthenticationResponse> 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<AuthenticationResponse>(jsonTextReader);
}
}
}
}
}
}
/// <summary>Gets the authorization URL for the back channel call.</summary>
/// <param name="state">The encrypted <see cref="AuthenticationProperties"/> for the current authentication session.</param>
/// <returns>The authorization URL for the back channel call.</returns>
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;
}
/// <summary>Gets the <see cref="ClaimsIdentity"/> for the identity of the user.</summary>
/// <param name="authenticationResponse">The <see cref="AuthenticationResponse"/> returned by imgur.</param>
/// <returns>The <see cref="ClaimsIdentity"/> for the identity of the user.</returns>
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;
}
/// <summary>Gets the <see cref="ImgurAuthenticatedContext"/> for the current authentication session.</summary>
/// <param name="authenticationResponse">The <see cref="AuthenticationResponse"/> returned by imgur.</param>
/// <param name="identity">The <see cref="ClaimsIdentity"/> for the identity of the user.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/> for the current authentication session.</param>
/// <returns>The <see cref="ImgurAuthenticatedContext"/> for the current authentication session.</returns>
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;
}
/// <summary>Gets the <see cref="ImgurReturnEndpointContext"/> for the current authentication session.</summary>
/// <param name="ticket">The <see cref="AuthenticationTicket"/> for the current authentication session.</param>
/// <returns>The <see cref="ImgurReturnEndpointContext"/> for the current authentication session.</returns>
private ImgurReturnEndpointContext GetImgurReturnEndpointContext(AuthenticationTicket ticket)
{
var context = new ImgurReturnEndpointContext(this.Context, ticket);
context.SignInAsAuthenticationType = this.Options.SignInAsAuthenticationType;
context.RedirectUri = ticket.Properties.RedirectUri;
return context;
}
/// <summary>Adds authentication information to the OWIN context to let the appropriate <see cref="AuthenticationMiddleware{TOptions}"/> authenticate the user.</summary>
/// <param name="context">The <see cref="ReturnEndpointContext"/> for the current authentication session.</param>
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);
}
/// <summary>Gets the URL where the user should be redirect to.</summary>
/// <param name="context">The <see cref="ReturnEndpointContext"/> for the current authentication session.</param>
/// <returns>The URL where the user should be redirect to.</returns>
private static string GetRedirectLocation(ReturnEndpointContext context)
{
var location = context.RedirectUri;
if (context.Identity == null)
{
location =
WebUtilities.AddQueryString(
location,
ImgurAuthenticationDefaults.ErrorParameter,
ImgurAuthenticationDefaults.AccessDeniedErrorMessage);
}
return location;
}
/// <summary>Response payload returned by imgur with the information of the authenticated user.</summary>
private class AuthenticationResponse
{
/// <summary>Gets or sets the access token for the authenticated user.</summary>
[JsonProperty(PropertyName = ImgurAuthenticationDefaults.AccessTokenPropertyName)]
public string AccessToken { get; set; }
/// <summary>Gets or sets the account id of the authenticated user.</summary>
[JsonProperty(PropertyName = ImgurAuthenticationDefaults.AccountIdPropertyName)]
public int AccountId { get; set; }
/// <summary>Gets or sets the account username of the authenticated user.</summary>
[JsonProperty(PropertyName = ImgurAuthenticationDefaults.AccountUsernamePropertyName)]
public string AccountUsername { get; set; }
/// <summary>Gets or sets the duration of the access token.</summary>
[JsonProperty(PropertyName = ImgurAuthenticationDefaults.ExpiresInPropertyName)]
public int ExpiresIn { get; set; }
/// <summary>Gets or sets the refresh token for the authenticated user.</summary>
[JsonProperty(PropertyName = ImgurAuthenticationDefaults.RefreshInPropertyName)]
public string RefreshToken { get; set; }
/// <summary>Gets or sets the scope of the access token.</summary>
[JsonProperty(PropertyName = ImgurAuthenticationDefaults.ScopePropertyName)]
public string Scope { get; set; }
/// <summary>Gets or sets the type of the access token.</summary>
[JsonProperty(PropertyName = ImgurAuthenticationDefaults.TokenTypePropertyName)]
public string TokenType { get; set; }
}
}
}

View File

@@ -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;
/// <summary>OWIN authentication middleware for imgur.</summary>
public class ImgurAuthenticationMiddleware : AuthenticationMiddleware<ImgurAuthenticationOptions>
{
private readonly HttpClient httpClient;
private readonly ILogger logger;
private readonly static string TypeFullName = typeof(ImgurAuthenticationMiddleware).FullName;
/// <summary>Creates a new <see cref="ImgurAuthenticationMiddleware"/>.</summary>
/// <param name="next">The next <see cref="OwinMiddleware"/> in the configuration chain.</param>
/// <param name="appBuilder">The OWIN <see cref="IAppBuilder"/> being configured.</param>
/// <param name="options">The <see cref="ImgurAuthenticationOptions"/> to be used to set up the <see cref="ImgurAuthenticationMiddleware"/>.</param>
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<ImgurAuthenticationMiddleware>();
}
/// <summary>Creates the <see cref="AuthenticationHandler{TOptions}"/> to be used by the <see cref="ImgurAuthenticationMiddleware"/>.</summary>
/// <returns>The <see cref="AuthenticationHandler{TOptions}"/> to be used by the <see cref="ImgurAuthenticationMiddleware"/>.</returns>
protected override AuthenticationHandler<ImgurAuthenticationOptions> CreateHandler()
{
return new ImgurAuthenticationHandler(this.httpClient, this.logger);
}
/// <summary>Checks that the imgur application client id has been set.</summary>
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");
}
/// <summary>Checks that the imgur application client secret has been set.</summary>
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");
}
/// <summary>Sets the provider to <see cref="ImgurAuthenticationProvider"/> if it hasn't been set.</summary>
private void SetProvider()
{
if (this.Options.Provider != null)
{
return;
}
this.Options.Provider = new ImgurAuthenticationProvider();
}
/// <summary>Sets the name authentication middleware responsible for signing in the user if it hasn't been set.</summary>
/// <param name="appBuilder">The OWIN <see cref="IAppBuilder"/> being configured.</param>
private void SetSignInAsAuthenticationType(IAppBuilder appBuilder)
{
if (!string.IsNullOrWhiteSpace(this.Options.SignInAsAuthenticationType))
{
return;
}
this.Options.SignInAsAuthenticationType = appBuilder.GetDefaultSignInAsAuthenticationType();
}
/// <summary>Sets the data protector to <see cref="PropertiesDataFormat"/> if it hasn't been set.</summary>
/// <param name="appBuilder">The OWIN <see cref="IAppBuilder"/> being configured.</param>
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);
}
/// <summary>Gets the <see cref="HttpMessageHandler"/> to be used for the back channel calls.</summary>
/// <param name="options">The <see cref="ImgurAuthenticationOptions"/> used to configure the <see cref="ImgurAuthenticationMiddleware"/>.</param>
/// <returns>The <see cref="HttpMessageHandler"/> to be used for the back channel calls.</returns>
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;
}
}
}

View File

@@ -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;
/// <summary>Configuration options for the <see cref="ImgurAuthenticationMiddleware"/>.</summary>
public class ImgurAuthenticationOptions : AuthenticationOptions
{
/// <summary>Creates a new <see cref="ImgurAuthenticationOptions"/>.</summary>
public ImgurAuthenticationOptions()
: base(ImgurAuthenticationDefaults.AuthenticationType)
{
this.AuthenticationMode = AuthenticationMode.Passive;
this.BackchannelTimeout = TimeSpan.FromSeconds(60);
this.CallbackPath = new PathString(ImgurAuthenticationDefaults.CallbackPath);
this.Caption = ImgurAuthenticationDefaults.AuthenticationType;
}
/// <summary>Gets or sets the a pinned certificate validator to use to validate the endpoints used in back channel communications belong to StackExchange.</summary>
/// <remarks>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.</remarks>
public ICertificateValidator BackchannelCertificateValidator { get; set; }
/// <summary>The HttpMessageHandler used to communicate with StackExchange.</summary>
/// <remarks>This cannot be set at the same time as BackchannelCertificateValidator unless the value can be downcast to a WebRequestHandler.</remarks>
public HttpMessageHandler BackchannelHttpHandler { get; set; }
/// <summary>Gets or sets timeout value in milliseconds for back channel communications with imgur.</summary>
public TimeSpan BackchannelTimeout { get; set; }
/// <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.</summary>
/// <remarks>The default value is "/signin-imgur".</remarks>
public PathString CallbackPath { get; set; }
/// <summary>Get or sets the text that the user can display on a sign in user interface.</summary>
public string Caption
{
get
{
return this.Description.Caption;
}
set
{
this.Description.Caption = value;
}
}
/// <summary>Gets or sets the imgur application client id.</summary>
public string ClientId { get; set; }
/// <summary>Gets or sets the imgur application client secret.</summary>
public string ClientSecret { get; set; }
/// <summary>Gets or sets the <see cref="IImgurAuthenticationProvider" /> used in the authentication events.</summary>
public IImgurAuthenticationProvider Provider { get; set; }
/// <summary>Gets or sets the name of another authentication middleware which will be responsible for actually issuing a user.</summary>
public string SignInAsAuthenticationType { get; set; }
/// <summary> Gets or sets the type used to secure the data handled by the middleware.</summary>
public ISecureDataFormat<AuthenticationProperties> StateDataFormat { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
namespace Owin.Security.Providers.Imgur.Provider
{
using System.Threading.Tasks;
/// <summary>Specifies callback methods which the <see cref="ImgurAuthenticationMiddleware"/> invokes to enable developers control over the authentication process.</summary>
public interface IImgurAuthenticationProvider
{
/// <summary>Invoked whenever imgur succesfully authenticates a user.</summary>
/// <param name="context">The <see cref="ImgurAuthenticatedContext"/> that contains information about the login session and the user's <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
Task Authenticated(ImgurAuthenticatedContext 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">The <see cref="ImgurReturnEndpointContext"/> of the authentication request.</param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
Task ReturnEndpoint(ImgurReturnEndpointContext context);
}
}

View File

@@ -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;
/// <summary>Contains information about the login session and the user's <see cref="System.Security.Claims.ClaimsIdentity"/>.</summary>
public class ImgurAuthenticatedContext : BaseContext<ImgurAuthenticationOptions>
{
/// <summary>Creates a new <see cref="ImgurAuthenticatedContext"/>.</summary>
/// <param name="context">The OWIN context of the autentication request.</param>
/// <param name="options">The <see cref="ImgurAuthenticationOptions"/> used to set up the <see cref="ImgurAuthenticationMiddleware"/>.</param>
public ImgurAuthenticatedContext(IOwinContext context, ImgurAuthenticationOptions options)
: base(context, options)
{
}
/// <summary>Gets or sets the access token for the authenticated user.</summary>
public string AccessToken { get; set; }
/// <summary>Gets or sets the account id of the authenticated user.</summary>
public int AccountId { get; set; }
/// <summary>Gets or sets the account username of the authenticated user.</summary>
public string AccountUsername { get; set; }
/// <summary>Gets or sets the duration of the access token.</summary>
public int ExpiresIn { get; set; }
/// <summary>Gets or sets the <see cref="ClaimsIdentity"/> for the authenticated user.</summary>
public ClaimsIdentity Identity { get; set; }
/// <summary>Gets or sets the <see cref="AuthenticationProperties"/> of the authentication request.</summary>
public AuthenticationProperties Properties { get; set; }
/// <summary>Gets or sets the refresh token for the authenticated user.</summary>
public string RefreshToken { get; set; }
/// <summary>Gets or sets the scope of the access token.</summary>
public string Scope { get; set; }
/// <summary>Gets or sets the type of the access token.</summary>
public string TokenType { get; set; }
}
}

View File

@@ -0,0 +1,38 @@
namespace Owin.Security.Providers.Imgur.Provider
{
using System;
using System.Threading.Tasks;
/// <summary>Default <see cref="IImgurAuthenticationProvider"/> implementation.</summary>
public class ImgurAuthenticationProvider : IImgurAuthenticationProvider
{
/// <summary>Creates a new <see cref="ImgurAuthenticationProvider"/>.</summary>
public ImgurAuthenticationProvider()
{
this.OnAuthenticated = context => Task.FromResult<object>(null);
this.OnReturnEndpoint = context => Task.FromResult<object>(null);
}
/// <summary>Gets or sets the function that is invoked when the Authenticated method is invoked.</summary>
public Func<ImgurAuthenticatedContext, Task> OnAuthenticated { get; set; }
/// <summary>Gets or sets the function that is invoked when the ReturnEndpoint method is invoked.</summary>
public Func<ImgurReturnEndpointContext, Task> OnReturnEndpoint { get; set; }
/// <summary>Invoked whenever imgur succesfully authenticates a user.</summary>
/// <param name="context">The <see cref="ImgurAuthenticatedContext"/> that contains information about the login session and the user's <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
public Task Authenticated(ImgurAuthenticatedContext context)
{
return this.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">The <see cref="ImgurReturnEndpointContext"/> of the authentication request.</param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
public Task ReturnEndpoint(ImgurReturnEndpointContext context)
{
return this.OnReturnEndpoint(context);
}
}
}

View File

@@ -0,0 +1,18 @@
namespace Owin.Security.Providers.Imgur.Provider
{
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Provider;
/// <summary>Provide context information to the middleware provider.</summary>
public class ImgurReturnEndpointContext : ReturnEndpointContext
{
/// <summary>Creates a new <see cref="ImgurReturnEndpointContext"/>.</summary>
/// <param name="context">The OWIN context of the authentication request.</param>
/// <param name="ticket">The <see cref="AuthenticationTicket" /> of the authentication request.</param>
public ImgurReturnEndpointContext(IOwinContext context, AuthenticationTicket ticket)
: base(context, ticket)
{
}
}
}

View File

@@ -178,6 +178,15 @@
<Compile Include="HealthGraph\Provider\HealthGraphAuthenticationProvider.cs" />
<Compile Include="HealthGraph\Provider\HealthGraphReturnEndpointContext.cs" />
<Compile Include="HealthGraph\Provider\IHealthGraphAuthenticationProvider.cs" />
<Compile Include="Imgur\ImgurAuthenticationDefaults.cs" />
<Compile Include="Imgur\ImgurAuthenticationExtensions.cs" />
<Compile Include="Imgur\ImgurAuthenticationHandler.cs" />
<Compile Include="Imgur\ImgurAuthenticationMiddleware.cs" />
<Compile Include="Imgur\ImgurAuthenticationOptions.cs" />
<Compile Include="Imgur\Provider\IImgurAuthenticationProvider.cs" />
<Compile Include="Imgur\Provider\ImgurAuthenticationProvider.cs" />
<Compile Include="Imgur\Provider\ImgurReturnEndpointContext.cs" />
<Compile Include="Imgur\Provider\ImgurAuthenticatedContext.cs" />
<Compile Include="Instagram\Constants.cs" />
<Compile Include="Instagram\InstagramAuthenticationExtensions.cs" />
<Compile Include="Instagram\InstagramAuthenticationHandler.cs" />

View File

@@ -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 = ""
// });
}
}
}