├── Microsoft.Owin.Security.WeChat ├── nuget.exe ├── Microsoft.Owin.Security.WeChat.1.0.0.0.nupkg ├── Microsoft.Owin.Security.WeChat.1.0.0.1.nupkg ├── Provider │ ├── IWeChatAuthenticationProvider.cs │ ├── WeChatReturnEndpointContext.cs │ ├── WeChatAuthenticationProvider.cs │ └── WeChatAuthenticatedContext.cs ├── Microsoft.Owin.Security.WeChat.nuspec ├── WeChatAuthenticationExtensions.cs ├── WeChatAuthenticationOptions.cs ├── Properties │ └── AssemblyInfo.cs ├── WeChatAuthenticationMiddleware.cs ├── Microsoft.Owin.Security.WeChat.csproj └── WeChatAccountAuthenticationHandler.cs ├── Microsoft.Owin.Security.QQ ├── packages.config ├── Provider │ ├── IQQConnectAuthenticationProvider.cs │ ├── QQConnectReturnEndpointContext.cs │ ├── QQConnectAuthenticationProvider.cs │ └── QQConnectAuthenticatedContext.cs ├── Properties │ └── AssemblyInfo.cs ├── QQConnectAuthenticationExtensions.cs ├── QQConnectAuthenticationOptions.cs ├── QQConnectAuthenticationMiddleware.cs ├── Microsoft.Owin.Security.QQ.csproj └── QQConnectAccountAuthenticationHandler.cs ├── README.md ├── Microsoft.Owin.Security.Solution.sln ├── .gitattributes └── .gitignore /Microsoft.Owin.Security.WeChat/nuget.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momyh/Microsoft.Owin.Security.Solution/HEAD/Microsoft.Owin.Security.WeChat/nuget.exe -------------------------------------------------------------------------------- /Microsoft.Owin.Security.WeChat/Microsoft.Owin.Security.WeChat.1.0.0.0.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momyh/Microsoft.Owin.Security.Solution/HEAD/Microsoft.Owin.Security.WeChat/Microsoft.Owin.Security.WeChat.1.0.0.0.nupkg -------------------------------------------------------------------------------- /Microsoft.Owin.Security.WeChat/Microsoft.Owin.Security.WeChat.1.0.0.1.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momyh/Microsoft.Owin.Security.Solution/HEAD/Microsoft.Owin.Security.WeChat/Microsoft.Owin.Security.WeChat.1.0.0.1.nupkg -------------------------------------------------------------------------------- /Microsoft.Owin.Security.QQ/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.WeChat/Provider/IWeChatAuthenticationProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Microsoft.Owin.Security.WeChat 8 | { 9 | public interface IWeChatAuthenticationProvider 10 | { 11 | Task Authenticated(WeChatAuthenticatedContext context); 12 | Task ReturnEndpoint(WeChatReturnEndpointContext context); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Microsoft.Owin.Security.Solution is the asp.net mvc5 solution extentions for china developer 2 | 3 | this solution is the Microsoft.Owin.Security extention,is extention the china qq and weixin login with asp.net identity 4 | 5 | How to use: 6 | Add below code to asp.net mvc5 project App_Start/Startup.Auth.cs 7 | 8 | app.UseQQConnectAuthentication(appId: "***",appSecret: "*****"); 9 | 10 | app.UseWeChatAuthentication(appId: "***",appSecret: "*****"); 11 | 12 | 这个工程系对asp.net identity的一个扩展,在asp.net mvc5的基础上增加了中国国内的QQ和微信登陆,用于解决新网站集成这两种登陆方式的麻烦,欢迎其他人员贡献代码。 13 | 14 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.WeChat/Provider/WeChatReturnEndpointContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.Owin; 6 | using Microsoft.Owin.Security; 7 | using Microsoft.Owin.Security.Provider; 8 | 9 | namespace Microsoft.Owin.Security.WeChat 10 | { 11 | public class WeChatReturnEndpointContext : ReturnEndpointContext 12 | { 13 | public WeChatReturnEndpointContext( 14 | IOwinContext context, 15 | AuthenticationTicket ticket) 16 | : base(context, ticket) 17 | { 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.WeChat/Provider/WeChatAuthenticationProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Microsoft.Owin.Security.WeChat 8 | { 9 | public class WeChatAuthenticationProvider : IWeChatAuthenticationProvider 10 | { 11 | public WeChatAuthenticationProvider() 12 | { 13 | onAuthenticated = (c) => Task.FromResult(null); 14 | onReturnEndpoint = (c) => Task.FromResult(null); 15 | } 16 | 17 | public Func onAuthenticated { get; set; } 18 | public Func onReturnEndpoint { get; set; } 19 | 20 | public Task Authenticated(WeChatAuthenticatedContext context) 21 | { 22 | return onAuthenticated(context); 23 | } 24 | 25 | public Task ReturnEndpoint(WeChatReturnEndpointContext context) 26 | { 27 | return onReturnEndpoint(context); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.QQ/Provider/IQQConnectAuthenticationProvider.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Feifan Tang. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | using System; 17 | using System.Collections.Generic; 18 | using System.Linq; 19 | using System.Text; 20 | using System.Threading.Tasks; 21 | 22 | namespace Microsoft.Owin.Security.WeChat 23 | { 24 | public interface IQQConnectAuthenticationProvider 25 | { 26 | Task Authenticated(QQConnectAuthenticatedContext context); 27 | Task ReturnEndpoint(QQConnectReturnEndpointContext context); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.WeChat/Microsoft.Owin.Security.WeChat.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.Owin.Security.WeChat 5 | 1.0.0.1 6 | Momy 7 | Momy 8 | http://www.yn-s.com 9 | https://github.com/momyh/Microsoft.Owin.Security.Solution 10 | http://www.yn-s.com 11 | false 12 | Middleware that enables an application to support WeChat/QQ OAuth 2.0 authentication workflow. 13 | ASP.NET MVC 5 OAuth.0 Login 14 | Copyright 2015 15 | Owin Weixin Wechat Microsoft.Owin.Security.WeChat Microsoft.Owin.Security.Weixin 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.WeChat/WeChatAuthenticationExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Owin.Security.WeChat; 3 | using Microsoft.Owin.Security; 4 | 5 | namespace Owin 6 | { 7 | public static class WeChatAuthenticationExtensions 8 | { 9 | public static void UseWeChatAuthentication(this IAppBuilder app, WeChatAuthenticationOptions options) 10 | { 11 | if (app == null) 12 | { 13 | throw new ArgumentNullException("app"); 14 | } 15 | if (options == null) 16 | { 17 | throw new ArgumentNullException("options"); 18 | } 19 | 20 | app.Use(typeof(WeChatAuthenticationMiddleware), app, options); 21 | } 22 | 23 | public static void UseWeChatAuthentication(this IAppBuilder app, string appId, string appSecret) 24 | { 25 | UseWeChatAuthentication(app, new WeChatAuthenticationOptions() 26 | { 27 | AppId = appId, 28 | AppSecret = appSecret, 29 | SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType() 30 | }); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.QQ/Provider/QQConnectReturnEndpointContext.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Feifan Tang. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | using System; 17 | using System.Collections.Generic; 18 | using System.Linq; 19 | using System.Text; 20 | using Microsoft.Owin; 21 | using Microsoft.Owin.Security; 22 | using Microsoft.Owin.Security.Provider; 23 | 24 | namespace Microsoft.Owin.Security.WeChat 25 | { 26 | public class QQConnectReturnEndpointContext : ReturnEndpointContext 27 | { 28 | public QQConnectReturnEndpointContext( 29 | IOwinContext context, 30 | AuthenticationTicket ticket) 31 | : base(context, ticket) 32 | { 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.WeChat/Provider/WeChatAuthenticatedContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Claims; 5 | using System.Text; 6 | using Microsoft.Owin; 7 | using Microsoft.Owin.Security; 8 | using Microsoft.Owin.Security.Provider; 9 | using Newtonsoft.Json.Linq; 10 | 11 | namespace Microsoft.Owin.Security.WeChat 12 | { 13 | public class WeChatAuthenticatedContext : BaseContext 14 | { 15 | public WeChatAuthenticatedContext(IOwinContext context,string openId, JObject user, string accessToken) 16 | :base(context) 17 | { 18 | IDictionary userAsDictionary = user; 19 | 20 | User = user; 21 | AccessToken = accessToken; 22 | 23 | Id = openId; 24 | Name = PropertyValueIfExists("nickname", userAsDictionary); 25 | } 26 | 27 | public JObject User { get; private set; } 28 | public string AccessToken { get; private set; } 29 | 30 | public string Id { get; private set; } 31 | public string Name { get; private set; } 32 | 33 | public ClaimsIdentity Identity { get; set; } 34 | public AuthenticationProperties Properties { get; set; } 35 | 36 | private static string PropertyValueIfExists(string property, IDictionary dictionary) 37 | { 38 | return dictionary.ContainsKey(property) ? dictionary[property].ToString() : null; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.Solution.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Owin.Security.WeChat", "Microsoft.Owin.Security.WeChat\Microsoft.Owin.Security.WeChat.csproj", "{A9A0BA07-5273-4D93-A398-E5F94177CC38}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Owin.Security.QQ", "Microsoft.Owin.Security.QQ\Microsoft.Owin.Security.QQ.csproj", "{2B96497A-C363-473A-BDD4-CC390B7ADB96}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {A9A0BA07-5273-4D93-A398-E5F94177CC38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {A9A0BA07-5273-4D93-A398-E5F94177CC38}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {A9A0BA07-5273-4D93-A398-E5F94177CC38}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {A9A0BA07-5273-4D93-A398-E5F94177CC38}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {2B96497A-C363-473A-BDD4-CC390B7ADB96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {2B96497A-C363-473A-BDD4-CC390B7ADB96}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {2B96497A-C363-473A-BDD4-CC390B7ADB96}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {2B96497A-C363-473A-BDD4-CC390B7ADB96}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.WeChat/WeChatAuthenticationOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using Microsoft.Owin.Security; 5 | 6 | namespace Microsoft.Owin.Security.WeChat 7 | { 8 | public class WeChatAuthenticationOptions : AuthenticationOptions 9 | { 10 | public const string AUTHENTICATION_TYPE = "WeChat"; 11 | public WeChatAuthenticationOptions() 12 | : base(AUTHENTICATION_TYPE) 13 | { 14 | Caption = "微信账号"; 15 | ReturnEndpointPath = "/signin-wechatconnect"; 16 | AuthenticationMode = AuthenticationMode.Passive; 17 | Scope = new List { "get_user_info" }; 18 | BackchannelTimeout = TimeSpan.FromSeconds(60); 19 | } 20 | 21 | public ISecureDataFormat StateDataFormat { get; set; } 22 | 23 | public TimeSpan BackchannelTimeout { get; set; } 24 | 25 | public WebRequestHandler BackchannelHttpHandler { get; set; } 26 | 27 | public IWeChatAuthenticationProvider Provider { get; set; } 28 | 29 | public ICertificateValidator BackchannelCertificateValidator { get; set; } 30 | 31 | public IList Scope { get; private set; } 32 | 33 | public string ReturnEndpointPath { get; set; } 34 | 35 | public string SignInAsAuthenticationType { get; set; } 36 | 37 | public string Caption 38 | { 39 | get { return Description.Caption; } 40 | set { Description.Caption = value; } 41 | } 42 | 43 | public string AppId { get; set; } 44 | 45 | public string AppSecret { get; set; } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.QQ/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Microsoft.Owin.Security.QQ")] 9 | [assembly: AssemblyDescription("Middleware that enables an application to support QQConnect OAuth 2.0 authentication workflow.")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("momy")] 12 | [assembly: AssemblyProduct("Microsoft.Owin.Security.QQ")] 13 | [assembly: AssemblyCopyright("Copyright © momy 2013")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("3b4e1164-4e9b-423e-b900-efa4996dd2c9")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.WeChat/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Microsoft.Owin.Security.WeChat")] 9 | [assembly: AssemblyDescription("Middleware that enables an application to support WeChat OAuth 2.0 authentication workflow.")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("momy")] 12 | [assembly: AssemblyProduct("Microsoft.Owin.Security.WeChat")] 13 | [assembly: AssemblyCopyright("Copyright © momy 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("68107e44-ab82-4b66-b651-add2cad0c0ed")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.1")] 36 | [assembly: AssemblyFileVersion("1.0.0.1")] 37 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.QQ/Provider/QQConnectAuthenticationProvider.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Feifan Tang. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | using System; 17 | using System.Collections.Generic; 18 | using System.Linq; 19 | using System.Text; 20 | using System.Threading.Tasks; 21 | 22 | namespace Microsoft.Owin.Security.WeChat 23 | { 24 | public class QQConnectAuthenticationProvider : IQQConnectAuthenticationProvider 25 | { 26 | public QQConnectAuthenticationProvider() 27 | { 28 | onAuthenticated = (c) => Task.FromResult(null); 29 | onReturnEndpoint = (c) => Task.FromResult(null); 30 | } 31 | 32 | public Func onAuthenticated { get; set; } 33 | public Func onReturnEndpoint { get; set; } 34 | 35 | public Task Authenticated(QQConnectAuthenticatedContext context) 36 | { 37 | return onAuthenticated(context); 38 | } 39 | 40 | public Task ReturnEndpoint(QQConnectReturnEndpointContext context) 41 | { 42 | return onReturnEndpoint(context); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.QQ/QQConnectAuthenticationExtensions.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Feifan Tang. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | using System; 17 | using Microsoft.Owin.Security.WeChat; 18 | using Microsoft.Owin.Security; 19 | 20 | namespace Owin 21 | { 22 | public static class QQConnectAuthenticationExtensions 23 | { 24 | public static void UseQQConnectAuthentication(this IAppBuilder app, QQConnectAuthenticationOptions options) 25 | { 26 | if (app == null) 27 | { 28 | throw new ArgumentNullException("app"); 29 | } 30 | if (options == null) 31 | { 32 | throw new ArgumentNullException("options"); 33 | } 34 | 35 | app.Use(typeof(QQConnectAuthenticationMiddleware), app, options); 36 | } 37 | 38 | public static void UseQQConnectAuthentication(this IAppBuilder app, string appId, string appSecret) 39 | { 40 | UseQQConnectAuthentication(app, new QQConnectAuthenticationOptions() 41 | { 42 | AppId = appId, 43 | AppSecret = appSecret, 44 | SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType() 45 | }); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.QQ/Provider/QQConnectAuthenticatedContext.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Feifan Tang. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | using System; 17 | using System.Collections.Generic; 18 | using System.Linq; 19 | using System.Security.Claims; 20 | using System.Text; 21 | using Microsoft.Owin; 22 | using Microsoft.Owin.Security; 23 | using Microsoft.Owin.Security.Provider; 24 | using Newtonsoft.Json.Linq; 25 | 26 | namespace Microsoft.Owin.Security.WeChat 27 | { 28 | public class QQConnectAuthenticatedContext : BaseContext 29 | { 30 | public QQConnectAuthenticatedContext(IOwinContext context,string openId, JObject user, string accessToken) 31 | :base(context) 32 | { 33 | IDictionary userAsDictionary = user; 34 | 35 | User = user; 36 | AccessToken = accessToken; 37 | 38 | Id = openId; 39 | Name = PropertyValueIfExists("nickname", userAsDictionary); 40 | } 41 | 42 | public JObject User { get; private set; } 43 | public string AccessToken { get; private set; } 44 | 45 | public string Id { get; private set; } 46 | public string Name { get; private set; } 47 | 48 | public ClaimsIdentity Identity { get; set; } 49 | public AuthenticationProperties Properties { get; set; } 50 | 51 | private static string PropertyValueIfExists(string property, IDictionary dictionary) 52 | { 53 | return dictionary.ContainsKey(property) ? dictionary[property].ToString() : null; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.QQ/QQConnectAuthenticationOptions.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Feifan Tang. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | using System; 17 | using System.Collections.Generic; 18 | using System.Net.Http; 19 | using Microsoft.Owin.Security; 20 | 21 | namespace Microsoft.Owin.Security.WeChat 22 | { 23 | public class QQConnectAuthenticationOptions : AuthenticationOptions 24 | { 25 | public const string AUTHENTICATION_TYPE = "QQConnect"; 26 | public QQConnectAuthenticationOptions() 27 | : base(AUTHENTICATION_TYPE) 28 | { 29 | Caption = "QQ账号"; 30 | ReturnEndpointPath = "/signin-qqconnect"; 31 | AuthenticationMode = AuthenticationMode.Passive; 32 | Scope = new List { "get_user_info" }; 33 | BackchannelTimeout = TimeSpan.FromSeconds(60); 34 | } 35 | 36 | public ISecureDataFormat StateDataFormat { get; set; } 37 | 38 | public TimeSpan BackchannelTimeout { get; set; } 39 | 40 | public WebRequestHandler BackchannelHttpHandler { get; set; } 41 | 42 | public IQQConnectAuthenticationProvider Provider { get; set; } 43 | 44 | public ICertificateValidator BackchannelCertificateValidator { get; set; } 45 | 46 | public IList Scope { get; private set; } 47 | 48 | public string ReturnEndpointPath { get; set; } 49 | 50 | public string SignInAsAuthenticationType { get; set; } 51 | 52 | public string Caption 53 | { 54 | get { return Description.Caption; } 55 | set { Description.Caption = value; } 56 | } 57 | 58 | public string AppId { get; set; } 59 | 60 | public string AppSecret { get; set; } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.WeChat/WeChatAuthenticationMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using Microsoft.Owin; 4 | using Microsoft.Owin.Logging; 5 | using Microsoft.Owin.Security.DataHandler; 6 | using Microsoft.Owin.Security.DataProtection; 7 | using Microsoft.Owin.Security.Infrastructure; 8 | using Owin; 9 | 10 | namespace Microsoft.Owin.Security.WeChat 11 | { 12 | public class WeChatAuthenticationMiddleware :AuthenticationMiddleware 13 | { 14 | private readonly ILogger _logger; 15 | private readonly HttpClient _httpClient; 16 | 17 | public WeChatAuthenticationMiddleware( 18 | OwinMiddleware next, 19 | IAppBuilder app, 20 | WeChatAuthenticationOptions options) 21 | : base(next, options) 22 | { 23 | _logger = app.CreateLogger(); 24 | 25 | if (Options.Provider == null) 26 | { 27 | Options.Provider = new WeChatAuthenticationProvider(); 28 | } 29 | if (Options.StateDataFormat == null) 30 | { 31 | var dataProtecter = app.CreateDataProtector( 32 | typeof(WeChatAuthenticationMiddleware).FullName, 33 | Options.AuthenticationType, "v1"); 34 | Options.StateDataFormat = new PropertiesDataFormat(dataProtecter); 35 | } 36 | 37 | _httpClient = new HttpClient(ResolveHttpMessageHandler(Options)); 38 | _httpClient.Timeout = Options.BackchannelTimeout; 39 | _httpClient.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB 40 | } 41 | 42 | protected override AuthenticationHandler CreateHandler() 43 | { 44 | return new WeChatAccountAuthenticationHandler(_httpClient, _logger); 45 | } 46 | 47 | private static HttpMessageHandler ResolveHttpMessageHandler(WeChatAuthenticationOptions options) 48 | { 49 | HttpMessageHandler handler = options.BackchannelHttpHandler ?? new WebRequestHandler(); 50 | 51 | // If they provided a validator, apply it or fail. 52 | if (options.BackchannelCertificateValidator != null) 53 | { 54 | // Set the cert validate callback 55 | WebRequestHandler webRequestHandler = handler as WebRequestHandler; 56 | if (webRequestHandler == null) 57 | { 58 | throw new InvalidOperationException("An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler."); 59 | } 60 | webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate; 61 | } 62 | 63 | return handler; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.QQ/QQConnectAuthenticationMiddleware.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Feifan Tang. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | using System; 17 | using System.Net.Http; 18 | using Microsoft.Owin; 19 | using Microsoft.Owin.Logging; 20 | using Microsoft.Owin.Security.DataHandler; 21 | using Microsoft.Owin.Security.DataProtection; 22 | using Microsoft.Owin.Security.Infrastructure; 23 | using Owin; 24 | 25 | namespace Microsoft.Owin.Security.WeChat 26 | { 27 | public class QQConnectAuthenticationMiddleware :AuthenticationMiddleware 28 | { 29 | private readonly ILogger _logger; 30 | private readonly HttpClient _httpClient; 31 | 32 | public QQConnectAuthenticationMiddleware( 33 | OwinMiddleware next, 34 | IAppBuilder app, 35 | QQConnectAuthenticationOptions options) 36 | : base(next, options) 37 | { 38 | _logger = app.CreateLogger(); 39 | 40 | if (Options.Provider == null) 41 | { 42 | Options.Provider = new QQConnectAuthenticationProvider(); 43 | } 44 | if (Options.StateDataFormat == null) 45 | { 46 | var dataProtecter = app.CreateDataProtector( 47 | typeof(QQConnectAuthenticationMiddleware).FullName, 48 | Options.AuthenticationType, "v1"); 49 | Options.StateDataFormat = new PropertiesDataFormat(dataProtecter); 50 | } 51 | 52 | _httpClient = new HttpClient(ResolveHttpMessageHandler(Options)); 53 | _httpClient.Timeout = Options.BackchannelTimeout; 54 | _httpClient.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB 55 | } 56 | 57 | protected override AuthenticationHandler CreateHandler() 58 | { 59 | return new QQConnectAccountAuthenticationHandler(_httpClient, _logger); 60 | } 61 | 62 | private static HttpMessageHandler ResolveHttpMessageHandler(QQConnectAuthenticationOptions options) 63 | { 64 | HttpMessageHandler handler = options.BackchannelHttpHandler ?? new WebRequestHandler(); 65 | 66 | // If they provided a validator, apply it or fail. 67 | if (options.BackchannelCertificateValidator != null) 68 | { 69 | // Set the cert validate callback 70 | WebRequestHandler webRequestHandler = handler as WebRequestHandler; 71 | if (webRequestHandler == null) 72 | { 73 | throw new InvalidOperationException("An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler."); 74 | } 75 | webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate; 76 | } 77 | 78 | return handler; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | 19 | # Roslyn cache directories 20 | *.ide/ 21 | 22 | # MSTest test Results 23 | [Tt]est[Rr]esult*/ 24 | [Bb]uild[Ll]og.* 25 | 26 | #NUNIT 27 | *.VisualState.xml 28 | TestResult.xml 29 | 30 | # Build Results of an ATL Project 31 | [Dd]ebugPS/ 32 | [Rr]eleasePS/ 33 | dlldata.c 34 | 35 | *_i.c 36 | *_p.c 37 | *_i.h 38 | *.ilk 39 | *.meta 40 | *.obj 41 | *.pch 42 | *.pdb 43 | *.pgc 44 | *.pgd 45 | *.rsp 46 | *.sbr 47 | *.tlb 48 | *.tli 49 | *.tlh 50 | *.tmp 51 | *.tmp_proj 52 | *.log 53 | *.vspscc 54 | *.vssscc 55 | .builds 56 | *.pidb 57 | *.svclog 58 | *.scc 59 | 60 | # Chutzpah Test files 61 | _Chutzpah* 62 | 63 | # Visual C++ cache files 64 | ipch/ 65 | *.aps 66 | *.ncb 67 | *.opensdf 68 | *.sdf 69 | *.cachefile 70 | 71 | # Visual Studio profiler 72 | *.psess 73 | *.vsp 74 | *.vspx 75 | 76 | # TFS 2012 Local Workspace 77 | $tf/ 78 | 79 | # Guidance Automation Toolkit 80 | *.gpState 81 | 82 | # ReSharper is a .NET coding add-in 83 | _ReSharper*/ 84 | *.[Rr]e[Ss]harper 85 | *.DotSettings.user 86 | 87 | # JustCode is a .NET coding addin-in 88 | .JustCode 89 | 90 | # TeamCity is a build add-in 91 | _TeamCity* 92 | 93 | # DotCover is a Code Coverage Tool 94 | *.dotCover 95 | 96 | # NCrunch 97 | _NCrunch_* 98 | .*crunch*.local.xml 99 | 100 | # MightyMoose 101 | *.mm.* 102 | AutoTest.Net/ 103 | 104 | # Web workbench (sass) 105 | .sass-cache/ 106 | 107 | # Installshield output folder 108 | [Ee]xpress/ 109 | 110 | # DocProject is a documentation generator add-in 111 | DocProject/buildhelp/ 112 | DocProject/Help/*.HxT 113 | DocProject/Help/*.HxC 114 | DocProject/Help/*.hhc 115 | DocProject/Help/*.hhk 116 | DocProject/Help/*.hhp 117 | DocProject/Help/Html2 118 | DocProject/Help/html 119 | 120 | # Click-Once directory 121 | publish/ 122 | 123 | # Publish Web Output 124 | *.[Pp]ublish.xml 125 | *.azurePubxml 126 | ## TODO: Comment the next line if you want to checkin your 127 | ## web deploy settings but do note that will include unencrypted 128 | ## passwords 129 | #*.pubxml 130 | 131 | # NuGet Packages Directory 132 | packages/* 133 | ## TODO: If the tool you use requires repositories.config 134 | ## uncomment the next line 135 | #!packages/repositories.config 136 | 137 | # Enable "build/" folder in the NuGet Packages folder since 138 | # NuGet packages use it for MSBuild targets. 139 | # This line needs to be after the ignore of the build folder 140 | # (and the packages folder if the line above has been uncommented) 141 | !packages/build/ 142 | *.nuspec 143 | *.nupkg 144 | nuget.exe 145 | 146 | # Windows Azure Build Output 147 | csx/ 148 | *.build.csdef 149 | 150 | # Windows Store app package directory 151 | AppPackages/ 152 | 153 | # Others 154 | sql/ 155 | *.Cache 156 | ClientBin/ 157 | [Ss]tyle[Cc]op.* 158 | ~$* 159 | *~ 160 | *.dbmdl 161 | *.dbproj.schemaview 162 | *.pfx 163 | *.publishsettings 164 | node_modules/ 165 | 166 | # RIA/Silverlight projects 167 | Generated_Code/ 168 | 169 | # Backup & report files from converting an old project file 170 | # to a newer Visual Studio version. Backup files are not needed, 171 | # because we have git ;-) 172 | _UpgradeReport_Files/ 173 | Backup*/ 174 | UpgradeLog*.XML 175 | UpgradeLog*.htm 176 | 177 | # SQL Server files 178 | *.mdf 179 | *.ldf 180 | 181 | # Business Intelligence projects 182 | *.rdl.data 183 | *.bim.layout 184 | *.bim_*.settings 185 | 186 | # Microsoft Fakes 187 | FakesAssemblies/ 188 | 189 | # LightSwitch generated files 190 | GeneratedArtifacts/ 191 | _Pvt_Extensions/ 192 | ModelManifest.xml -------------------------------------------------------------------------------- /Microsoft.Owin.Security.WeChat/Microsoft.Owin.Security.WeChat.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {A9A0BA07-5273-4D93-A398-E5F94177CC38} 8 | Library 9 | Properties 10 | Microsoft.Owin.Security.WeChat 11 | Microsoft.Owin.Security.WeChat 12 | v4.5 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | false 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | false 34 | 35 | 36 | 37 | ..\packages\Microsoft.Owin.2.0.2\lib\net45\Microsoft.Owin.dll 38 | 39 | 40 | ..\packages\Microsoft.Owin.Security.2.0.2\lib\net45\Microsoft.Owin.Security.dll 41 | 42 | 43 | False 44 | ..\packages\Newtonsoft.Json.5.0.8\lib\net45\Newtonsoft.Json.dll 45 | 46 | 47 | ..\packages\Owin.1.0\lib\net40\Owin.dll 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 74 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.QQ/Microsoft.Owin.Security.QQ.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {2B96497A-C363-473A-BDD4-CC390B7ADB96} 8 | Library 9 | Properties 10 | Microsoft.Owin.Security.WeChat 11 | Microsoft.Owin.Security.QQ 12 | v4.5 13 | 512 14 | ..\ 15 | true 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | 28 | 29 | none 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | false 36 | 37 | 38 | 39 | False 40 | ..\packages\Microsoft.Owin.2.0.2\lib\net45\Microsoft.Owin.dll 41 | 42 | 43 | False 44 | ..\packages\Microsoft.Owin.Security.2.0.2\lib\net45\Microsoft.Owin.Security.dll 45 | 46 | 47 | False 48 | ..\..\LinliboyWebsite\packages\Newtonsoft.Json.5.0.8\lib\net45\Newtonsoft.Json.dll 49 | 50 | 51 | False 52 | ..\packages\Owin.1.0\lib\net40\Owin.dll 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 83 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.WeChat/WeChatAccountAuthenticationHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Security.Claims; 5 | using System.Threading.Tasks; 6 | using Microsoft.Owin; 7 | using Microsoft.Owin.Logging; 8 | using Microsoft.Owin.Security; 9 | using Microsoft.Owin.Security.Infrastructure; 10 | using Newtonsoft.Json; 11 | using Newtonsoft.Json.Linq; 12 | 13 | namespace Microsoft.Owin.Security.WeChat 14 | { 15 | internal class WeChatAccountAuthenticationHandler : AuthenticationHandler 16 | { 17 | private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string"; 18 | private const string AuthorizationEndpoint = "https://open.weixin.qq.com/connect/qrconnect"; 19 | private const string TokenEndpoint = "https://api.weixin.qq.com/sns/oauth2/access_token"; 20 | private const string UserInfoEndpoint = "https://api.weixin.qq.com/sns/userinfo"; 21 | private const string OpenIDEndpoint = "https://api.weixin.qq.com/sns/oauth2"; 22 | 23 | private readonly HttpClient _httpClient; 24 | private readonly ILogger _logger; 25 | 26 | public WeChatAccountAuthenticationHandler(HttpClient httpClient, ILogger logger) 27 | { 28 | this._httpClient = httpClient; 29 | this._logger = logger; 30 | } 31 | 32 | public override async Task InvokeAsync() 33 | { 34 | if (Options.ReturnEndpointPath != null && 35 | String.Equals(Options.ReturnEndpointPath, Request.Path.Value, StringComparison.OrdinalIgnoreCase)) 36 | { 37 | return await InvokeReturnPathAsync(); 38 | } 39 | return false; 40 | } 41 | 42 | private async Task InvokeReturnPathAsync() 43 | { 44 | _logger.WriteVerbose("InvokeReturnPath"); 45 | 46 | var model = await AuthenticateAsync(); 47 | 48 | var context = new WeChatReturnEndpointContext(Context, model); 49 | context.SignInAsAuthenticationType = Options.SignInAsAuthenticationType; 50 | context.RedirectUri = model.Properties.RedirectUri; 51 | model.Properties.RedirectUri = null; 52 | 53 | await Options.Provider.ReturnEndpoint(context); 54 | 55 | if (context.SignInAsAuthenticationType != null && context.Identity != null) 56 | { 57 | ClaimsIdentity signInIdentity = context.Identity; 58 | if (!string.Equals(signInIdentity.AuthenticationType, context.SignInAsAuthenticationType, StringComparison.Ordinal)) 59 | { 60 | signInIdentity = new ClaimsIdentity(signInIdentity.Claims, context.SignInAsAuthenticationType, signInIdentity.NameClaimType, signInIdentity.RoleClaimType); 61 | } 62 | Context.Authentication.SignIn(context.Properties, signInIdentity); 63 | } 64 | 65 | if (!context.IsRequestCompleted && context.RedirectUri != null) 66 | { 67 | Response.Redirect(context.RedirectUri); 68 | context.RequestCompleted(); 69 | } 70 | 71 | return context.IsRequestCompleted; 72 | } 73 | 74 | protected override async Task AuthenticateCoreAsync() 75 | { 76 | _logger.WriteVerbose("AuthenticateCore"); 77 | 78 | AuthenticationProperties properties = null; 79 | 80 | try 81 | { 82 | string code = null; 83 | string state = null; 84 | 85 | IReadableStringCollection query = Request.Query; 86 | IList values = query.GetValues("code"); 87 | if (values != null && values.Count == 1) 88 | { 89 | code = values[0]; 90 | } 91 | values = query.GetValues("state"); 92 | if (values != null && values.Count == 1) 93 | { 94 | state = values[0]; 95 | } 96 | 97 | properties = Options.StateDataFormat.Unprotect(state); 98 | if (properties == null) 99 | { 100 | return null; 101 | } 102 | 103 | // OAuth2 10.12 CSRF 104 | if (!ValidateCorrelationId(properties, _logger)) 105 | { 106 | return new AuthenticationTicket(null, properties); 107 | } 108 | 109 | var tokenRequestParameters = new List>() 110 | { 111 | new KeyValuePair("appid", Options.AppId), 112 | new KeyValuePair("secret", Options.AppSecret), 113 | new KeyValuePair("code", code), 114 | new KeyValuePair("grant_type", "authorization_code"), 115 | }; 116 | 117 | FormUrlEncodedContent requestContent = new FormUrlEncodedContent(tokenRequestParameters); 118 | 119 | HttpResponseMessage response = await _httpClient.PostAsync(TokenEndpoint, requestContent, Request.CallCancelled); 120 | response.EnsureSuccessStatusCode(); 121 | string oauthTokenResponse = await response.Content.ReadAsStringAsync(); 122 | JsonSerializer js = new JsonSerializer(); 123 | AccessTokenResult tokenResult= js.Deserialize(new JsonTextReader(new System.IO.StringReader(oauthTokenResponse))); 124 | if (tokenResult == null || tokenResult.access_token == null) 125 | { 126 | _logger.WriteWarning("Access token was not found"); 127 | return new AuthenticationTicket(null, properties); 128 | } 129 | 130 | string userInfoUri = UserInfoEndpoint + 131 | "?access_token=" + Uri.EscapeDataString(tokenResult.access_token) + 132 | "&openid=" + Uri.EscapeDataString(tokenResult.openid); 133 | HttpResponseMessage userInfoResponse = await _httpClient.GetAsync(userInfoUri, Request.CallCancelled); 134 | userInfoResponse.EnsureSuccessStatusCode(); 135 | string userInfoString = await userInfoResponse.Content.ReadAsStringAsync(); 136 | JObject userInfo = JObject.Parse(userInfoString); 137 | 138 | var context = new WeChatAuthenticatedContext(Context, tokenResult.openid, userInfo, tokenResult.access_token); 139 | context.Identity = new ClaimsIdentity(new[]{ 140 | new Claim(ClaimTypes.NameIdentifier, context.Id,XmlSchemaString,Options.AuthenticationType), 141 | new Claim(ClaimsIdentity.DefaultNameClaimType, context.Name,XmlSchemaString,Options.AuthenticationType), 142 | new Claim("urn:wechatconnect:id", context.Id,XmlSchemaString,Options.AuthenticationType), 143 | new Claim("urn:wechatconnect:name", context.Name,XmlSchemaString,Options.AuthenticationType), 144 | }); 145 | 146 | await Options.Provider.Authenticated(context); 147 | 148 | context.Properties = properties; 149 | 150 | return new AuthenticationTicket(context.Identity, context.Properties); 151 | } 152 | catch (Exception ex) 153 | { 154 | _logger.WriteError(ex.Message); 155 | } 156 | 157 | return new AuthenticationTicket(null, properties); 158 | } 159 | 160 | protected override Task ApplyResponseChallengeAsync() 161 | { 162 | _logger.WriteVerbose("ApplyResponseChallenge"); 163 | 164 | if (Response.StatusCode != 401) 165 | { 166 | return Task.FromResult(null); 167 | } 168 | 169 | AuthenticationResponseChallenge challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode); 170 | 171 | if (challenge != null) 172 | { 173 | string requestPrefix = Request.Scheme + "://" + Request.Host; 174 | string currentQueryString = Request.QueryString.Value; 175 | string currentUri = string.IsNullOrEmpty(currentQueryString) 176 | ? requestPrefix + Request.PathBase + Request.Path 177 | : requestPrefix + Request.PathBase + Request.Path + "?" + currentQueryString; 178 | 179 | string redirectUri = requestPrefix + Request.PathBase + Options.ReturnEndpointPath; 180 | 181 | AuthenticationProperties properties = challenge.Properties; 182 | 183 | if (string.IsNullOrEmpty(properties.RedirectUri)) 184 | { 185 | properties.RedirectUri = currentUri; 186 | } 187 | 188 | // OAuth2 10.12 CSRF 189 | GenerateCorrelationId(properties); 190 | 191 | // comma separated 192 | string scope = string.Join(",", Options.Scope); 193 | 194 | string state = Options.StateDataFormat.Protect(properties); 195 | 196 | string authorizationEndpoint = 197 | AuthorizationEndpoint + 198 | "?appid=" + Uri.EscapeDataString(Options.AppId ?? string.Empty) + 199 | "&redirect_uri=" + Uri.EscapeDataString(redirectUri) + 200 | "&scope=" + Uri.EscapeDataString(scope) + 201 | "&state=" + Uri.EscapeDataString(state) + 202 | "&response_type=code"; 203 | 204 | Response.Redirect(authorizationEndpoint); 205 | } 206 | 207 | return Task.FromResult(null); 208 | } 209 | 210 | private string GenerateRedirectUri() 211 | { 212 | string requestPrefix = Request.Scheme + "://" + Request.Host; 213 | 214 | string redirectUri = requestPrefix + RequestPathBase + Options.ReturnEndpointPath; // + "?state=" + Uri.EscapeDataString(Options.StateDataFormat.Protect(state)); 215 | return redirectUri; 216 | } 217 | 218 | [Serializable] 219 | public class AccessTokenResult 220 | { 221 | public string errcode { get; set; } 222 | 223 | public string errmsg { get; set; } 224 | 225 | public string access_token { get; set; } 226 | 227 | public string expires_in { get; set; } 228 | 229 | public string refresh_token { get; set; } 230 | 231 | public string openid { get; set; } 232 | 233 | public string scope { get; set; } 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /Microsoft.Owin.Security.QQ/QQConnectAccountAuthenticationHandler.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Feifan Tang. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | using System; 17 | using System.Collections.Generic; 18 | using System.Net.Http; 19 | using System.Security.Claims; 20 | using System.Threading.Tasks; 21 | using Microsoft.Owin; 22 | using Microsoft.Owin.Logging; 23 | using Microsoft.Owin.Security; 24 | using Microsoft.Owin.Security.Infrastructure; 25 | using Newtonsoft.Json.Linq; 26 | 27 | namespace Microsoft.Owin.Security.WeChat 28 | { 29 | internal class QQConnectAccountAuthenticationHandler : AuthenticationHandler 30 | { 31 | private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string"; 32 | private const string AuthorizationEndpoint = "https://graph.qq.com/oauth2.0/authorize"; 33 | private const string TokenEndpoint = "https://graph.qq.com/oauth2.0/token"; 34 | private const string UserInfoEndpoint = "https://openmobile.qq.com/user/get_simple_userinfo"; 35 | private const string OpenIDEndpoint = "https://graph.qq.com/oauth2.0/me"; 36 | 37 | private readonly HttpClient _httpClient; 38 | private readonly ILogger _logger; 39 | 40 | public QQConnectAccountAuthenticationHandler(HttpClient httpClient, ILogger logger) 41 | { 42 | this._httpClient = httpClient; 43 | this._logger = logger; 44 | } 45 | 46 | public override async Task InvokeAsync() 47 | { 48 | if (Options.ReturnEndpointPath != null && 49 | String.Equals(Options.ReturnEndpointPath, Request.Path.Value, StringComparison.OrdinalIgnoreCase)) 50 | { 51 | return await InvokeReturnPathAsync(); 52 | } 53 | return false; 54 | } 55 | 56 | private async Task InvokeReturnPathAsync() 57 | { 58 | _logger.WriteVerbose("InvokeReturnPath"); 59 | 60 | var model = await AuthenticateAsync(); 61 | 62 | var context = new QQConnectReturnEndpointContext(Context, model); 63 | context.SignInAsAuthenticationType = Options.SignInAsAuthenticationType; 64 | context.RedirectUri = model.Properties.RedirectUri; 65 | model.Properties.RedirectUri = null; 66 | 67 | await Options.Provider.ReturnEndpoint(context); 68 | 69 | if (context.SignInAsAuthenticationType != null && context.Identity != null) 70 | { 71 | ClaimsIdentity signInIdentity = context.Identity; 72 | if (!string.Equals(signInIdentity.AuthenticationType, context.SignInAsAuthenticationType, StringComparison.Ordinal)) 73 | { 74 | signInIdentity = new ClaimsIdentity(signInIdentity.Claims, context.SignInAsAuthenticationType, signInIdentity.NameClaimType, signInIdentity.RoleClaimType); 75 | } 76 | Context.Authentication.SignIn(context.Properties, signInIdentity); 77 | } 78 | 79 | if (!context.IsRequestCompleted && context.RedirectUri != null) 80 | { 81 | Response.Redirect(context.RedirectUri); 82 | context.RequestCompleted(); 83 | } 84 | 85 | return context.IsRequestCompleted; 86 | } 87 | 88 | protected override async Task AuthenticateCoreAsync() 89 | { 90 | _logger.WriteVerbose("AuthenticateCore"); 91 | 92 | AuthenticationProperties properties = null; 93 | 94 | try 95 | { 96 | string code = null; 97 | string state = null; 98 | 99 | IReadableStringCollection query = Request.Query; 100 | IList values = query.GetValues("code"); 101 | if (values != null && values.Count == 1) 102 | { 103 | code = values[0]; 104 | } 105 | values = query.GetValues("state"); 106 | if (values != null && values.Count == 1) 107 | { 108 | state = values[0]; 109 | } 110 | 111 | properties = Options.StateDataFormat.Unprotect(state); 112 | if (properties == null) 113 | { 114 | return null; 115 | } 116 | 117 | // OAuth2 10.12 CSRF 118 | if (!ValidateCorrelationId(properties, _logger)) 119 | { 120 | return new AuthenticationTicket(null, properties); 121 | } 122 | 123 | var tokenRequestParameters = new List>() 124 | { 125 | new KeyValuePair("client_id", Options.AppId), 126 | new KeyValuePair("client_secret", Options.AppSecret), 127 | new KeyValuePair("redirect_uri", GenerateRedirectUri()), 128 | new KeyValuePair("code", code), 129 | new KeyValuePair("grant_type", "authorization_code"), 130 | }; 131 | 132 | FormUrlEncodedContent requestContent = new FormUrlEncodedContent(tokenRequestParameters); 133 | 134 | HttpResponseMessage response = await _httpClient.PostAsync(TokenEndpoint, requestContent, Request.CallCancelled); 135 | response.EnsureSuccessStatusCode(); 136 | string oauthTokenResponse = await response.Content.ReadAsStringAsync(); 137 | var tokenDict = QueryStringToDict(oauthTokenResponse); 138 | 139 | string accessToken = null; 140 | if(tokenDict.ContainsKey("access_token")) 141 | { 142 | accessToken = tokenDict["access_token"]; 143 | } 144 | else 145 | { 146 | _logger.WriteWarning("Access token was not found"); 147 | return new AuthenticationTicket(null, properties); 148 | } 149 | 150 | string openIDUri = OpenIDEndpoint + "?access_token=" + Uri.EscapeDataString(accessToken); 151 | HttpResponseMessage openIDResponse = await _httpClient.GetAsync(openIDUri, Request.CallCancelled); 152 | openIDResponse.EnsureSuccessStatusCode(); 153 | string openIDString = await openIDResponse.Content.ReadAsStringAsync(); 154 | openIDString = ExtractOpenIDCallbackBody(openIDString); 155 | JObject openIDInfo = JObject.Parse(openIDString); 156 | 157 | var clientId = openIDInfo["client_id"].Value(); 158 | var openId = openIDInfo["openid"].Value(); 159 | 160 | string userInfoUri = UserInfoEndpoint + 161 | "?access_token=" + Uri.EscapeDataString(accessToken) + 162 | "&oauth_consumer_key=" + Uri.EscapeDataString(clientId) + 163 | "&openid=" + Uri.EscapeDataString(openId); 164 | HttpResponseMessage userInfoResponse = await _httpClient.GetAsync(userInfoUri, Request.CallCancelled); 165 | userInfoResponse.EnsureSuccessStatusCode(); 166 | string userInfoString = await userInfoResponse.Content.ReadAsStringAsync(); 167 | JObject userInfo = JObject.Parse(userInfoString); 168 | 169 | var context = new QQConnectAuthenticatedContext(Context, openId, userInfo, accessToken); 170 | context.Identity = new ClaimsIdentity(new[]{ 171 | new Claim(ClaimTypes.NameIdentifier, context.Id,XmlSchemaString,Options.AuthenticationType), 172 | new Claim(ClaimsIdentity.DefaultNameClaimType, context.Name,XmlSchemaString,Options.AuthenticationType), 173 | new Claim("urn:qqconnect:id", context.Id,XmlSchemaString,Options.AuthenticationType), 174 | new Claim("urn:qqconnect:name", context.Name,XmlSchemaString,Options.AuthenticationType), 175 | }); 176 | 177 | await Options.Provider.Authenticated(context); 178 | 179 | context.Properties = properties; 180 | 181 | return new AuthenticationTicket(context.Identity, context.Properties); 182 | } 183 | catch (Exception ex) 184 | { 185 | _logger.WriteError(ex.Message); 186 | } 187 | 188 | return new AuthenticationTicket(null, properties); 189 | } 190 | 191 | protected override Task ApplyResponseChallengeAsync() 192 | { 193 | _logger.WriteVerbose("ApplyResponseChallenge"); 194 | 195 | if (Response.StatusCode != 401) 196 | { 197 | return Task.FromResult(null); 198 | } 199 | 200 | AuthenticationResponseChallenge challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode); 201 | 202 | if (challenge != null) 203 | { 204 | string requestPrefix = Request.Scheme + "://" + Request.Host; 205 | string currentQueryString = Request.QueryString.Value; 206 | string currentUri = string.IsNullOrEmpty(currentQueryString) 207 | ? requestPrefix + Request.PathBase + Request.Path 208 | : requestPrefix + Request.PathBase + Request.Path + "?" + currentQueryString; 209 | 210 | string redirectUri = requestPrefix + Request.PathBase + Options.ReturnEndpointPath; 211 | 212 | AuthenticationProperties properties = challenge.Properties; 213 | 214 | if (string.IsNullOrEmpty(properties.RedirectUri)) 215 | { 216 | properties.RedirectUri = currentUri; 217 | } 218 | 219 | // OAuth2 10.12 CSRF 220 | GenerateCorrelationId(properties); 221 | 222 | // comma separated 223 | string scope = string.Join(",", Options.Scope); 224 | 225 | string state = Options.StateDataFormat.Protect(properties); 226 | 227 | string authorizationEndpoint = 228 | AuthorizationEndpoint + 229 | "?client_id=" + Uri.EscapeDataString(Options.AppId ?? string.Empty) + 230 | "&redirect_uri=" + Uri.EscapeDataString(redirectUri) + 231 | "&scope=" + Uri.EscapeDataString(scope) + 232 | "&state=" + Uri.EscapeDataString(state) + 233 | "&response_type=code"; 234 | 235 | Response.Redirect(authorizationEndpoint); 236 | } 237 | 238 | return Task.FromResult(null); 239 | } 240 | 241 | private string GenerateRedirectUri() 242 | { 243 | string requestPrefix = Request.Scheme + "://" + Request.Host; 244 | 245 | string redirectUri = requestPrefix + RequestPathBase + Options.ReturnEndpointPath; // + "?state=" + Uri.EscapeDataString(Options.StateDataFormat.Protect(state)); 246 | return redirectUri; 247 | } 248 | 249 | private string ExtractOpenIDCallbackBody(string callbackString) 250 | { 251 | int leftBracketIndex = callbackString.IndexOf('{'); 252 | int rightBracketIndex = callbackString.IndexOf('}'); 253 | if (leftBracketIndex >= 0 && rightBracketIndex >= 0) 254 | { 255 | return callbackString.Substring(leftBracketIndex, rightBracketIndex - leftBracketIndex + 1).Trim(); 256 | } 257 | return callbackString; 258 | } 259 | 260 | private IDictionary QueryStringToDict(string str) 261 | { 262 | var strArr = str.Split('&'); 263 | var dict = new Dictionary(strArr.Length); 264 | foreach(var s in strArr) 265 | { 266 | var equalSymbolIndex = s.IndexOf('='); 267 | if(equalSymbolIndex>0&&equalSymbolIndex