diff --git a/.gitignore b/.gitignore index ad4f621..d9908de 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,9 @@ build/ [Bb]in/ [Oo]bj/ +# Visual Studo 2015 cache/options directory +.vs/ + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* diff --git a/Owin.Security.Providers/Gitter/Constants.cs b/Owin.Security.Providers/Gitter/Constants.cs new file mode 100644 index 0000000..9a58d89 --- /dev/null +++ b/Owin.Security.Providers/Gitter/Constants.cs @@ -0,0 +1,7 @@ +namespace Owin.Security.Providers.Gitter +{ + internal static class Constants + { + public const string DefaultAuthenticationType = "Gitter"; + } +} \ No newline at end of file diff --git a/Owin.Security.Providers/Gitter/GitterAuthenticationExtensions.cs b/Owin.Security.Providers/Gitter/GitterAuthenticationExtensions.cs new file mode 100644 index 0000000..68f823c --- /dev/null +++ b/Owin.Security.Providers/Gitter/GitterAuthenticationExtensions.cs @@ -0,0 +1,29 @@ +using System; + +namespace Owin.Security.Providers.Gitter +{ + public static class GitterAuthenticationExtensions + { + public static IAppBuilder UseGitterAuthentication(this IAppBuilder app, + GitterAuthenticationOptions options) + { + if (app == null) + throw new ArgumentNullException("app"); + if (options == null) + throw new ArgumentNullException("options"); + + app.Use(typeof(GitterAuthenticationMiddleware), app, options); + + return app; + } + + public static IAppBuilder UseGitterAuthentication(this IAppBuilder app, string clientId, string clientSecret) + { + return app.UseGitterAuthentication(new GitterAuthenticationOptions + { + ClientId = clientId, + ClientSecret = clientSecret + }); + } + } +} \ No newline at end of file diff --git a/Owin.Security.Providers/Gitter/GitterAuthenticationHandler.cs b/Owin.Security.Providers/Gitter/GitterAuthenticationHandler.cs new file mode 100644 index 0000000..950b8a3 --- /dev/null +++ b/Owin.Security.Providers/Gitter/GitterAuthenticationHandler.cs @@ -0,0 +1,264 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.Owin; +using Microsoft.Owin.Infrastructure; +using Microsoft.Owin.Logging; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.Infrastructure; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Owin.Security.Providers.Gitter.Provider; + +namespace Owin.Security.Providers.Gitter +{ + public class GitterAuthenticationHandler : AuthenticationHandler + { + private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string"; + private const string TokenEndpoint = "https://gitter.im/login/oauth/token"; + private const string UserInfoEndpoint = "https://api.gitter.im/v1/user"; + private const string AuthorizeEndpoint = "https://gitter.im/login/oauth/authorize"; + private const string Host = "api.gitter.im"; + + private readonly ILogger logger; + private readonly HttpClient httpClient; + + public GitterAuthenticationHandler(HttpClient httpClient, ILogger logger) + { + this.httpClient = httpClient; + this.logger = logger; + } + + protected override async Task AuthenticateCoreAsync() + { + AuthenticationProperties properties = null; + + try + { + string code = null; + string state = null; + + IReadableStringCollection query = Request.Query; + IList values = query.GetValues("code"); + if (values != null && values.Count == 1) + { + code = values[0]; + } + values = query.GetValues("state"); + if (values != null && values.Count == 1) + { + state = values[0]; + } + + properties = Options.StateDataFormat.Unprotect(state); + if (properties == null) + { + return null; + } + + if (!ValidateCorrelationId(properties, logger)) + { + return new AuthenticationTicket(null, properties); + } + + string requestPrefix = Request.Scheme + "://" + Request.Host; + string redirectUri = requestPrefix + Request.PathBase + Options.CallbackPath; + + // Build up the body for the token request + var body = new List>(); + body.Add(new KeyValuePair("grant_type", "authorization_code")); + body.Add(new KeyValuePair("code", code)); + body.Add(new KeyValuePair("redirect_uri", redirectUri)); + body.Add(new KeyValuePair("client_id", Options.ClientId)); + body.Add(new KeyValuePair("client_secret", Options.ClientSecret)); + + // Request the token + HttpResponseMessage tokenResponse = await httpClient.PostAsync(TokenEndpoint, new FormUrlEncodedContent(body)); + tokenResponse.EnsureSuccessStatusCode(); + string text = await tokenResponse.Content.ReadAsStringAsync(); + + // Deserializes the token response + dynamic response = JsonConvert.DeserializeObject(text); + string accessToken = (string)response.access_token; + string token_type = (string)response.token_type; + + // Get the Gitter user + HttpRequestMessage userRequest = new HttpRequestMessage(HttpMethod.Get, UserInfoEndpoint + "?token=" + Uri.EscapeDataString(accessToken)); + userRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + userRequest.Headers.Authorization = new AuthenticationHeaderValue(token_type, accessToken); + userRequest.Headers.Host = Host; + + HttpResponseMessage userResponse = await httpClient.SendAsync(userRequest, Request.CallCancelled); + userResponse.EnsureSuccessStatusCode(); + text = await userResponse.Content.ReadAsStringAsync(); + JArray userArray = JArray.Parse(text); + JObject user = JObject.Parse(userArray[0].ToString()); + + var context = new GitterAuthenticatedContext(Context, user, accessToken, token_type) + { + Identity = new ClaimsIdentity( + Options.AuthenticationType, + ClaimsIdentity.DefaultNameClaimType, + ClaimsIdentity.DefaultRoleClaimType) + }; + + if (!string.IsNullOrEmpty(context.UserId)) + { + context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.UserId, XmlSchemaString, Options.AuthenticationType)); + } + if (!string.IsNullOrEmpty(context.Username)) + { + context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.Username, XmlSchemaString, Options.AuthenticationType)); + } + if (!string.IsNullOrEmpty(context.UserDisplayName)) + { + context.Identity.AddClaim(new Claim("urn:gitter:displayName", context.UserDisplayName, XmlSchemaString, Options.AuthenticationType)); + } + if (!string.IsNullOrEmpty(context.UserGV)) + { + context.Identity.AddClaim(new Claim("urn:gitter:gv", context.UserAvatarUrlMedium, XmlSchemaString, Options.AuthenticationType)); + } + if (!string.IsNullOrEmpty(context.UserUrl)) + { + context.Identity.AddClaim(new Claim(ClaimTypes.Webpage, context.UserUrl, XmlSchemaString, Options.AuthenticationType)); + } + if (!string.IsNullOrEmpty(context.UserAvatarUrlSmall)) + { + context.Identity.AddClaim(new Claim(ClaimTypes.Webpage, context.UserAvatarUrlSmall, XmlSchemaString, Options.AuthenticationType)); + } + if (!string.IsNullOrEmpty(context.UserAvatarUrlMedium)) + { + context.Identity.AddClaim(new Claim(ClaimTypes.Webpage, context.UserAvatarUrlMedium, XmlSchemaString, Options.AuthenticationType)); + } + if (!string.IsNullOrEmpty(context.UserAvatarUrlMedium)) + { + context.Identity.AddClaim(new Claim(ClaimTypes.Webpage, context.UserAvatarUrlMedium, XmlSchemaString, Options.AuthenticationType)); + } + + context.Properties = properties; + + await Options.Provider.Authenticated(context); + + return new AuthenticationTicket(context.Identity, context.Properties); + + } + catch (Exception ex) + { + logger.WriteError(ex.Message); + } + + return new AuthenticationTicket(null, properties); + } + + protected override Task ApplyResponseChallengeAsync() + { + if (Response.StatusCode != 401) + { + return Task.FromResult(null); + } + + AuthenticationResponseChallenge challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode); + + if (challenge != null) + { + string baseUri = + Request.Scheme + + Uri.SchemeDelimiter + + Request.Host + + Request.PathBase; + + string currentUri = + baseUri + + Request.Path + + Request.QueryString; + + string redirectUri = + baseUri + + Options.CallbackPath; + + AuthenticationProperties properties = challenge.Properties; + if (string.IsNullOrEmpty(properties.RedirectUri)) + { + properties.RedirectUri = currentUri; + } + + // OAuth2 10.12 CSRF + GenerateCorrelationId(properties); + + // comma separated + string scope = string.Join(",", Options.Scope); + + string state = Options.StateDataFormat.Protect(properties); + + string authorizationEndpoint = + AuthorizeEndpoint + + "?response_type=code" + + "&client_id=" + Uri.EscapeDataString(Options.ClientId) + + "&redirect_uri=" + Uri.EscapeDataString(redirectUri) + + "&scope=" + Uri.EscapeDataString(scope) + + "&state=" + Uri.EscapeDataString(state); + + Response.Redirect(authorizationEndpoint); + } + + return Task.FromResult(null); + } + + public override async Task InvokeAsync() + { + return await InvokeReplyPathAsync(); + } + + private async Task InvokeReplyPathAsync() + { + if (Options.CallbackPath.HasValue && Options.CallbackPath == Request.Path) + { + + AuthenticationTicket ticket = await AuthenticateAsync(); + if (ticket == null) + { + logger.WriteWarning("Invalid return state, unable to redirect."); + Response.StatusCode = 500; + return true; + } + + var context = new GitterReturnEndpointContext(Context, ticket) + { + SignInAsAuthenticationType = Options.SignInAsAuthenticationType, + RedirectUri = ticket.Properties.RedirectUri + }; + + await Options.Provider.ReturnEndpoint(context); + + if (context.SignInAsAuthenticationType != null && + context.Identity != null) + { + ClaimsIdentity grantIdentity = context.Identity; + if (!string.Equals(grantIdentity.AuthenticationType, context.SignInAsAuthenticationType, StringComparison.Ordinal)) + { + grantIdentity = new ClaimsIdentity(grantIdentity.Claims, context.SignInAsAuthenticationType, grantIdentity.NameClaimType, grantIdentity.RoleClaimType); + } + Context.Authentication.SignIn(context.Properties, grantIdentity); + } + + if (!context.IsRequestCompleted && context.RedirectUri != null) + { + string redirectUri = context.RedirectUri; + if (context.Identity == null) + { + // add a redirect hint that sign-in failed in some way + redirectUri = WebUtilities.AddQueryString(redirectUri, "error", "access_denied"); + } + Response.Redirect(redirectUri); + context.RequestCompleted(); + } + + return context.IsRequestCompleted; + } + return false; + } + } +} \ No newline at end of file diff --git a/Owin.Security.Providers/Gitter/GitterAuthenticationMiddleware.cs b/Owin.Security.Providers/Gitter/GitterAuthenticationMiddleware.cs new file mode 100644 index 0000000..4dcb9b5 --- /dev/null +++ b/Owin.Security.Providers/Gitter/GitterAuthenticationMiddleware.cs @@ -0,0 +1,86 @@ +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.Gitter.Provider; +using Owin.Security.Providers.Properties; + +namespace Owin.Security.Providers.Gitter +{ + public class GitterAuthenticationMiddleware : AuthenticationMiddleware + { + private readonly HttpClient httpClient; + private readonly ILogger logger; + + public GitterAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, + GitterAuthenticationOptions options) + : base(next, options) + { + if (String.IsNullOrWhiteSpace(Options.ClientId)) + throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, + Resources.Exception_OptionMustBeProvided, "ClientId")); + if (String.IsNullOrWhiteSpace(Options.ClientSecret)) + throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, + Resources.Exception_OptionMustBeProvided, "ClientSecret")); + + logger = app.CreateLogger(); + + if (Options.Provider == null) + Options.Provider = new GitterAuthenticationProvider(); + + if (Options.StateDataFormat == null) + { + IDataProtector dataProtector = app.CreateDataProtector( + typeof(GitterAuthenticationMiddleware).FullName, + Options.AuthenticationType, "v1"); + Options.StateDataFormat = new PropertiesDataFormat(dataProtector); + } + + if (String.IsNullOrEmpty(Options.SignInAsAuthenticationType)) + Options.SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType(); + + httpClient = new HttpClient(ResolveHttpMessageHandler(Options)) + { + Timeout = Options.BackchannelTimeout, + MaxResponseContentBufferSize = 1024 * 1024 * 10 + }; + } + + private HttpMessageHandler ResolveHttpMessageHandler(GitterAuthenticationOptions options) + { + HttpMessageHandler handler = options.BackchannelHttpHandler ?? new WebRequestHandler(); + + // If they provided a validator, apply it or fail. + if (options.BackchannelCertificateValidator != null) + { + // Set the cert validate callback + var webRequestHandler = handler as WebRequestHandler; + if (webRequestHandler == null) + { + throw new InvalidOperationException(Resources.Exception_ValidatorHandlerMismatch); + } + webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate; + } + + return handler; + } + + /// + /// Provides the object for processing + /// authentication-related requests. + /// + /// + /// An configured with the + /// supplied to the constructor. + /// + protected override AuthenticationHandler CreateHandler() + { + return new GitterAuthenticationHandler(httpClient, logger); + } + } +} \ No newline at end of file diff --git a/Owin.Security.Providers/Gitter/GitterAuthenticationOptions.cs b/Owin.Security.Providers/Gitter/GitterAuthenticationOptions.cs new file mode 100644 index 0000000..5b2e223 --- /dev/null +++ b/Owin.Security.Providers/Gitter/GitterAuthenticationOptions.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using Microsoft.Owin; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.DataHandler; +using Owin.Security.Providers.Gitter.Provider; + +namespace Owin.Security.Providers.Gitter +{ + public class GitterAuthenticationOptions : AuthenticationOptions + { + /// + /// Initializes a new + /// + public GitterAuthenticationOptions() + : base("Gitter") + { + Caption = Constants.DefaultAuthenticationType; + AuthenticationMode = AuthenticationMode.Passive; + CallbackPath = new PathString("/signin-gitter"); + BackchannelTimeout = TimeSpan.FromSeconds(60); + Scope = new List(); + } + + /// + /// Get or sets the text that the user can display on a sign in user interface. + /// + public string Caption + { + get { return Description.Caption; } + set { Description.Caption = value; } + } + + /// + /// Determines the permissions granted from the application to Gitter. + /// Options include: identify, read, post, client, and/or admin + /// + public IList Scope { get; set; } + + /// + /// Gets or sets the Google supplied Client ID + /// + public string ClientId { get; set; } + + /// + /// Gets or sets the Google supplied Client Secret + /// + public string ClientSecret { get; set; } + + /// + /// Gets or sets the used in the authentication events + /// + public IGitterAuthenticationProvider Provider { get; set; } + + /// + /// Gets or sets the type used to secure data handled by the middleware. + /// + public PropertiesDataFormat StateDataFormat { 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 timeout value in milliseconds for back channel communications with Google+. + /// + /// + /// The back channel timeout in milliseconds. + /// + public TimeSpan BackchannelTimeout { get; set; } + + /// + /// The HttpMessageHandler used to communicate with Gitter. + /// 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 the a pinned certificate validator to use to validate the endpoints used + /// in back channel communications belong to PayPal. + /// + /// + /// The pinned certificate validator. + /// + /// + /// If this property is null then the default certificate checks are performed, + /// validating the subject name and if the signing chain is a trusted party. + /// + public ICertificateValidator BackchannelCertificateValidator { get; set; } + + /// + /// The request path within the application's base path where the user-agent will be returned. + /// The middleware will process this request when it arrives. + /// Default value is "/signin-gitter". + /// + public PathString CallbackPath { get; set; } + + } +} \ No newline at end of file diff --git a/Owin.Security.Providers/Gitter/Provider/GitterAuthenticatedContext.cs b/Owin.Security.Providers/Gitter/Provider/GitterAuthenticatedContext.cs new file mode 100644 index 0000000..e153ce1 --- /dev/null +++ b/Owin.Security.Providers/Gitter/Provider/GitterAuthenticatedContext.cs @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Security.Claims; +using Microsoft.Owin; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.Provider; +using Newtonsoft.Json.Linq; + +namespace Owin.Security.Providers.Gitter.Provider +{ + /// + /// Contains information about the login session as well as the user . + /// + public class GitterAuthenticatedContext : BaseContext + { + /// + /// Initializes a + /// + /// The OWIN environment + /// The JSON-serialized user + /// Gitter Access token + /// Indicates access level of application + public GitterAuthenticatedContext(IOwinContext context, JObject user, string accessToken, string token_type) + : base(context) + { + User = user; + AccessToken = accessToken; + TokenType = token_type; + + UserId = TryGetValue(user, "id"); + Username = TryGetValue(user, "username"); + UserDisplayName = TryGetValue(user, "displayName"); + UserUrl = TryGetValue(user, "url"); + UserAvatarUrlSmall = TryGetValue(user, "avatarUrlSmall"); + UserAvatarUrlMedium = TryGetValue(user, "avatarUrlMedium"); + UserGV = TryGetValue(user, "gv"); + } + + /// + /// The user GV + /// + public string UserGV { get; set; } + + /// + /// URL to the medium size avatar + /// + public string UserAvatarUrlMedium { get; set; } + + /// + /// URL to the small size avatar + /// + public string UserAvatarUrlSmall { get; set; } + + /// + /// URL to the user + /// + public string UserUrl { get; set; } + + /// + /// User display name + /// + public string UserDisplayName { get; set; } + + /// + /// Username + /// + public string Username { get; set; } + + /// + /// User unique ID + /// + public string UserId { get; set; } + + /// + /// Gets the Gitter token type + /// + public string TokenType { get; set; } + + /// + /// Gets the JSON-serialized user + /// + /// + /// Contains the Gitter user + /// + public JObject User { get; set; } + + /// + /// Gets the Gitter access token + /// + public string AccessToken { get; private set; } + + /// + /// Gets the representing the user + /// + public ClaimsIdentity Identity { get; set; } + + /// + /// Gets or sets a property bag for common authentication properties + /// + public AuthenticationProperties Properties { get; set; } + + private static string TryGetValue(JObject user, string propertyName) + { + JToken value; + return user.TryGetValue(propertyName, out value) ? value.ToString() : null; + } + } +} diff --git a/Owin.Security.Providers/Gitter/Provider/GitterAuthenticationProvider.cs b/Owin.Security.Providers/Gitter/Provider/GitterAuthenticationProvider.cs new file mode 100644 index 0000000..ef443fe --- /dev/null +++ b/Owin.Security.Providers/Gitter/Provider/GitterAuthenticationProvider.cs @@ -0,0 +1,50 @@ +using System; +using System.Threading.Tasks; + +namespace Owin.Security.Providers.Gitter.Provider +{ + /// + /// Default implementation. + /// + public class GitterAuthenticationProvider : IGitterAuthenticationProvider + { + /// + /// Initializes a + /// + public GitterAuthenticationProvider() + { + OnAuthenticated = context => Task.FromResult(null); + OnReturnEndpoint = context => Task.FromResult(null); + } + + /// + /// Gets or sets the function that is invoked when the Authenticated method is invoked. + /// + public Func OnAuthenticated { get; set; } + + /// + /// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked. + /// + public Func OnReturnEndpoint { get; set; } + + /// + /// Invoked whenever Google+ succesfully authenticates a user + /// + /// Contains information about the login session as well as the user . + /// A representing the completed operation. + public virtual Task Authenticated(GitterAuthenticatedContext context) + { + return OnAuthenticated(context); + } + + /// + /// Invoked prior to the being saved in a local cookie and the browser being redirected to the originally requested URL. + /// + /// + /// A representing the completed operation. + public virtual Task ReturnEndpoint(GitterReturnEndpointContext context) + { + return OnReturnEndpoint(context); + } + } +} \ No newline at end of file diff --git a/Owin.Security.Providers/Gitter/Provider/GitterReturnEndpointContext.cs b/Owin.Security.Providers/Gitter/Provider/GitterReturnEndpointContext.cs new file mode 100644 index 0000000..006e737 --- /dev/null +++ b/Owin.Security.Providers/Gitter/Provider/GitterReturnEndpointContext.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.Owin; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.Provider; + +namespace Owin.Security.Providers.Gitter.Provider +{ + /// + /// Provides context information to middleware providers. + /// + public class GitterReturnEndpointContext : ReturnEndpointContext + { + /// + /// + /// + /// OWIN environment + /// The authentication ticket + public GitterReturnEndpointContext( + IOwinContext context, + AuthenticationTicket ticket) + : base(context, ticket) + { + } + } +} diff --git a/Owin.Security.Providers/Gitter/Provider/IGitterAuthenticationProvider.cs b/Owin.Security.Providers/Gitter/Provider/IGitterAuthenticationProvider.cs new file mode 100644 index 0000000..f9e8a83 --- /dev/null +++ b/Owin.Security.Providers/Gitter/Provider/IGitterAuthenticationProvider.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; + +namespace Owin.Security.Providers.Gitter.Provider +{ + /// + /// Specifies callback methods which the invokes to enable developer control over the authentication process. /> + /// + public interface IGitterAuthenticationProvider + { + /// + /// Invoked whenever Google+ succesfully authenticates a user + /// + /// Contains information about the login session as well as the user . + /// A representing the completed operation. + Task Authenticated(GitterAuthenticatedContext context); + + /// + /// Invoked prior to the being saved in a local cookie and the browser being redirected to the originally requested URL. + /// + /// + /// A representing the completed operation. + Task ReturnEndpoint(GitterReturnEndpointContext context); + } +} \ No newline at end of file diff --git a/Owin.Security.Providers/Owin.Security.Providers.csproj b/Owin.Security.Providers/Owin.Security.Providers.csproj index e03c577..1e6063d 100644 --- a/Owin.Security.Providers/Owin.Security.Providers.csproj +++ b/Owin.Security.Providers/Owin.Security.Providers.csproj @@ -144,6 +144,15 @@ + + + + + + + + + diff --git a/OwinOAuthProviders.sln b/OwinOAuthProviders.sln index 0f05d64..ff3552c 100644 --- a/OwinOAuthProviders.sln +++ b/OwinOAuthProviders.sln @@ -1,12 +1,18 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.30723.0 +# Visual Studio 14 +VisualStudioVersion = 14.0.22823.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OwinOAuthProvidersDemo", "OwinOAuthProvidersDemo\OwinOAuthProvidersDemo.csproj", "{5A438007-0C90-4DAC-BAA1-54A32164067F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers", "Owin.Security.Providers\Owin.Security.Providers.csproj", "{6AD9BA00-1330-426D-8BAE-2D3BC0D976E4}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BF6144CD-2B61-456D-9F7E-A899F6DD850A}" + ProjectSection(SolutionItems) = preProject + .gitignore = .gitignore + README.md = README.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs b/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs index 8704146..d38f8d3 100755 --- a/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs +++ b/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs @@ -14,6 +14,7 @@ using Owin.Security.Providers.EveOnline; using Owin.Security.Providers.Flickr; using Owin.Security.Providers.Foursquare; using Owin.Security.Providers.GitHub; +using Owin.Security.Providers.Gitter; using Owin.Security.Providers.GooglePlus; using Owin.Security.Providers.GooglePlus.Provider; using Owin.Security.Providers.HealthGraph; @@ -238,10 +239,15 @@ namespace OwinOAuthProvidersDemo //{ // ClientId = "", // ClientSecret = "", - // TeamId = "" // optional + // TeamId = "IntelliAgent" // optional //}; //options.Scope.Add("identify"); //app.UseSlackAuthentication(options); + + //app.UseGitterAuthentication( + // clientId: "", + // clientSecret: "" + //); } } } \ No newline at end of file diff --git a/README.md b/README.md index 8b33d65..fb20fdd 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Provides a set of extra authentication providers for OWIN ([Project Katana](http - Flickr - Foursquare - GitHub + - Gitter - Google+ - HealthGraph - Instagram