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