diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/FodyWeavers.xml b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/FodyWeavers.xsd b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN.Abp.Account.OAuth.csproj b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN.Abp.Account.OAuth.csproj
new file mode 100644
index 000000000..5766da178
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN.Abp.Account.OAuth.csproj
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+ netstandard2.0;netstandard2.1;net8.0;net9.0
+ LINGYUN.Abp.Account.OAuth
+ LINGYUN.Abp.Account.OAuth
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/AbpAccountOAuthModule.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/AbpAccountOAuthModule.cs
new file mode 100644
index 000000000..77f9e9870
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/AbpAccountOAuthModule.cs
@@ -0,0 +1,28 @@
+using LINGYUN.Abp.Account.OAuth.Localization;
+using Volo.Abp.Features;
+using Volo.Abp.Localization;
+using Volo.Abp.Modularity;
+using Volo.Abp.Settings;
+using Volo.Abp.VirtualFileSystem;
+
+namespace LINGYUN.Abp.Account.OAuth;
+
+[DependsOn(typeof(AbpFeaturesModule))]
+[DependsOn(typeof(AbpSettingsModule))]
+public class AbpAccountOAuthModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+
+ Configure(options =>
+ {
+ options.Resources
+ .Add()
+ .AddVirtualJson("/LINGYUN/Abp/Account/OAuth/Localization/Resources");
+ });
+ }
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Features/AccountOAuthFeatureDefinitionProvider.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Features/AccountOAuthFeatureDefinitionProvider.cs
new file mode 100644
index 000000000..9d0a3068c
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Features/AccountOAuthFeatureDefinitionProvider.cs
@@ -0,0 +1,52 @@
+using LINGYUN.Abp.Account.OAuth.Localization;
+using Volo.Abp.Features;
+using Volo.Abp.Localization;
+using Volo.Abp.Validation.StringValues;
+
+namespace LINGYUN.Abp.Account.OAuth.Features;
+
+public class AccountOAuthFeatureDefinitionProvider : FeatureDefinitionProvider
+{
+ public override void Define(IFeatureDefinitionContext context)
+ {
+ var group = context.AddGroup(
+ name: AccountOAuthFeatureNames.GroupName,
+ displayName: L("Features:ExternalOAuthLogin"));
+
+ group.AddFeature(
+ name: AccountOAuthFeatureNames.GitHub.Enable,
+ defaultValue: "false",
+ displayName: L("Features:GithubOAuthEnable"),
+ description: L("Features:GithubOAuthEnableDesc"),
+ valueType: new ToggleStringValueType(new BooleanValueValidator()));
+ group.AddFeature(
+ name: AccountOAuthFeatureNames.QQ.Enable,
+ defaultValue: "false",
+ displayName: L("Features:QQOAuthEnable"),
+ description: L("Features:QQOAuthEnableDesc"),
+ valueType: new ToggleStringValueType(new BooleanValueValidator()));
+ group.AddFeature(
+ name: AccountOAuthFeatureNames.WeChat.Enable,
+ defaultValue: "false",
+ displayName: L("Features:WeChatOAuthEnable"),
+ description: L("Features:WeChatOAuthEnableDesc"),
+ valueType: new ToggleStringValueType(new BooleanValueValidator()));
+ group.AddFeature(
+ name: AccountOAuthFeatureNames.WeCom.Enable,
+ defaultValue: "false",
+ displayName: L("Features:WeComOAuthEnable"),
+ description: L("Features:WeComOAuthEnableDesc"),
+ valueType: new ToggleStringValueType(new BooleanValueValidator()));
+ group.AddFeature(
+ name: AccountOAuthFeatureNames.Bilibili.Enable,
+ defaultValue: "false",
+ displayName: L("Features:BilibiliOAuthEnable"),
+ description: L("Features:BilibiliOAuthEnableDesc"),
+ valueType: new ToggleStringValueType(new BooleanValueValidator()));
+ }
+
+ private static LocalizableString L(string name)
+ {
+ return LocalizableString.Create(name);
+ }
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Features/AccountOAuthFeatureNames.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Features/AccountOAuthFeatureNames.cs
new file mode 100644
index 000000000..4d97aabb1
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Features/AccountOAuthFeatureNames.cs
@@ -0,0 +1,46 @@
+namespace LINGYUN.Abp.Account.OAuth.Features;
+
+public static class AccountOAuthFeatureNames
+{
+ public const string GroupName = "Abp.Account.OAuth";
+ public static class GitHub
+ {
+ public const string Prefix = GroupName + ".GitHub";
+ ///
+ /// 启用Github认证登录
+ ///
+ public const string Enable = Prefix + ".Enable";
+ }
+ public static class QQ
+ {
+ public const string Prefix = GroupName + ".QQ";
+ ///
+ /// 启用QQ认证登录
+ ///
+ public const string Enable = Prefix + ".Enable";
+ }
+ public static class WeChat
+ {
+ public const string Prefix = GroupName + ".WeChat";
+ ///
+ /// 启用微信认证登录
+ ///
+ public const string Enable = Prefix + ".Enable";
+ }
+ public static class WeCom
+ {
+ public const string Prefix = GroupName + ".WeCom";
+ ///
+ /// 启用企业微信认证登录
+ ///
+ public const string Enable = Prefix + ".Enable";
+ }
+ public static class Bilibili
+ {
+ public const string Prefix = GroupName + ".Bilibili";
+ ///
+ /// 启用Bilibili认证登录
+ ///
+ public const string Enable = Prefix + ".Enable";
+ }
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Localization/AccountOAuthResource.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Localization/AccountOAuthResource.cs
new file mode 100644
index 000000000..32b981ed8
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Localization/AccountOAuthResource.cs
@@ -0,0 +1,8 @@
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.Account.OAuth.Localization;
+
+[LocalizationResourceName("AbpAccountOAuth")]
+public class AccountOAuthResource
+{
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Localization/Resources/en.json b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Localization/Resources/en.json
new file mode 100644
index 000000000..94849218d
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Localization/Resources/en.json
@@ -0,0 +1,36 @@
+{
+ "culture": "en",
+ "texts": {
+ "Permission:ExternalOAuthLogin": "External Oauth Login",
+ "Features:ExternalOAuthLogin": "External Oauth Login",
+ "Features:GithubOAuthEnable": "GitHub",
+ "Features:GithubOAuthEnableDesc": "Enable to enable the application to support login via a GitHub account.",
+ "Features:QQOAuthEnable": "QQ",
+ "Features:QQOAuthEnableDesc": "Enable to enable the application to support login via QQ account.",
+ "Features:WeChatOAuthEnable": "WeChat",
+ "Features:WeChatOAuthEnableDesc": "Enable to enable the application to support login via the wechat official account.",
+ "Features:WeComOAuthEnable": "WeCom",
+ "Features:WeComOAuthEnableDesc": "Enable to enable the application to support login via wecom.",
+ "Features:BilibiliOAuthEnable": "Bilibili",
+ "Features:BilibiliOAuthEnableDesc": "Enable to allow the application to support login via a Bilibili account.",
+ "Settings:ExternalOAuthLogin": "External Oauth Login",
+ "Settings:GitHubAuth": "GitHub",
+ "Settings:GitHubClientId": "Client Id",
+ "Settings:GitHubClientIdDesc": "The client ID received from GitHub during registration. for details: https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps",
+ "Settings:GitHubClientSecret": "Client Secret",
+ "Settings:GitHubClientSecretDesc": "The client key of the OAuth application that you received from GitHub. for details: https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps",
+ "Settings:BilibiliAuth": "Bilibili",
+ "Settings:BilibiliClientId": "Client Id",
+ "Settings:BilibiliClientIdDesc": "Client Id, for details: https://open.bilibili.com/doc/4/eaf0e2b5-bde9-b9a0-9be1-019bb455701c#h1-u7B80u4ECB",
+ "Settings:BilibiliClientSecret": "Client Secret",
+ "Settings:BilibiliClientSecretDesc": "Client Secret, for details: https://open.bilibili.com/doc/4/eaf0e2b5-bde9-b9a0-9be1-019bb455701c#h1-u7B80u4ECB",
+ "OAuth:Microsoft": "Microsoft",
+ "OAuth:Twitter": "Twitter",
+ "OAuth:GitHub": "GitHub",
+ "OAuth:Google": "Google",
+ "OAuth:QQ": "QQ",
+ "OAuth:Weixin": "WeChat",
+ "OAuth:WorkWeixin": "WeCom",
+ "OAuth:Bilibili": "Bilibili"
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Localization/Resources/zh-Hans.json b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Localization/Resources/zh-Hans.json
new file mode 100644
index 000000000..859fe07b1
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Localization/Resources/zh-Hans.json
@@ -0,0 +1,36 @@
+{
+ "culture": "zh-Hans",
+ "texts": {
+ "Permission:ExternalOAuthLogin": "外部登录",
+ "Features:ExternalOAuthLogin": "外部登录",
+ "Features:GithubOAuthEnable": "GitHub认证",
+ "Features:GithubOAuthEnableDesc": "启用以使应用程序支持通过GitHub账号登录.",
+ "Features:QQOAuthEnable": "QQ认证",
+ "Features:QQOAuthEnableDesc": "启用以使应用程序支持通过QQ账号登录.",
+ "Features:WeChatOAuthEnable": "微信认证",
+ "Features:WeChatOAuthEnableDesc": "启用以使应用程序支持通过微信公众号登录.",
+ "Features:WeComOAuthEnable": "企业微信认证",
+ "Features:WeComOAuthEnableDesc": "启用以使应用程序支持通过企业微信登录.",
+ "Features:BilibiliOAuthEnable": "Bilibili认证",
+ "Features:BilibiliOAuthEnableDesc": "启用以使应用程序支持通过Bilibili账号登录.",
+ "Settings:ExternalOAuthLogin": "外部登录",
+ "Settings:GitHubAuth": "GitHub登录",
+ "Settings:GitHubClientId": "Client Id",
+ "Settings:GitHubClientIdDesc": "注册时从 GitHub 收到的客户端 ID.详见: https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps",
+ "Settings:GitHubClientSecret": "Client Secret",
+ "Settings:GitHubClientSecretDesc": "您从 GitHub 收到的 OAuth 应用程序的客户端密钥.详见: https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps",
+ "Settings:BilibiliAuth": "Bilibili登录",
+ "Settings:BilibiliClientId": "Client Id",
+ "Settings:BilibiliClientIdDesc": "应用Id, 详见: https://open.bilibili.com/doc/4/eaf0e2b5-bde9-b9a0-9be1-019bb455701c#h1-u7B80u4ECB",
+ "Settings:BilibiliClientSecret": "Client Secret",
+ "Settings:BilibiliClientSecretDesc": "应用密钥, 详见: https://open.bilibili.com/doc/4/eaf0e2b5-bde9-b9a0-9be1-019bb455701c#h1-u7B80u4ECB",
+ "OAuth:Microsoft": "Microsoft",
+ "OAuth:Twitter": "Twitter",
+ "OAuth:GitHub": "GitHub",
+ "OAuth:Google": "Google",
+ "OAuth:QQ": "QQ",
+ "OAuth:Weixin": "微信",
+ "OAuth:WorkWeixin": "企业微信",
+ "OAuth:Bilibili": "Bilibili"
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Settings/AccountOAuthSettingDefinitionProvider.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Settings/AccountOAuthSettingDefinitionProvider.cs
new file mode 100644
index 000000000..dba71ecbf
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Settings/AccountOAuthSettingDefinitionProvider.cs
@@ -0,0 +1,77 @@
+using LINGYUN.Abp.Account.OAuth.Localization;
+using Volo.Abp.Localization;
+using Volo.Abp.Settings;
+
+namespace LINGYUN.Abp.Account.OAuth.Settings;
+
+public class AccountOAuthSettingDefinitionProvider : SettingDefinitionProvider
+{
+ public override void Define(ISettingDefinitionContext context)
+ {
+ context.Add(GetGitHubSettings());
+ context.Add(GetBilibiliSettings());
+ }
+
+ private SettingDefinition[] GetGitHubSettings()
+ {
+ return new SettingDefinition[]
+ {
+ new SettingDefinition(
+ AccountOAuthSettingNames.GitHub.ClientId,
+ displayName: L("Settings:GitHubClientId"),
+ description: L("Settings:GitHubClientIdDesc"),
+ isVisibleToClients: false,
+ isEncrypted: true)
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ ConfigurationSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ new SettingDefinition(
+ AccountOAuthSettingNames.GitHub.ClientSecret,
+ displayName: L("Settings:GitHubClientSecret"),
+ description: L("Settings:GitHubClientSecretDesc"),
+ isVisibleToClients: false,
+ isEncrypted: true)
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ ConfigurationSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ };
+ }
+
+ private SettingDefinition[] GetBilibiliSettings()
+ {
+ return new SettingDefinition[]
+ {
+ new SettingDefinition(
+ AccountOAuthSettingNames.Bilibili.ClientId,
+ displayName: L("Settings:BilibiliClientId"),
+ description: L("Settings:BilibiliClientIdDesc"),
+ isVisibleToClients: false,
+ isEncrypted: true)
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ ConfigurationSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ new SettingDefinition(
+ AccountOAuthSettingNames.Bilibili.ClientSecret,
+ displayName: L("Settings:BilibiliClientSecret"),
+ description: L("Settings:BilibiliClientSecretDesc"),
+ isVisibleToClients: false,
+ isEncrypted: true)
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ ConfigurationSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ };
+ }
+
+ protected ILocalizableString L(string name)
+ {
+ return LocalizableString.Create(name);
+ }
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Settings/AccountOAuthSettingNames.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Settings/AccountOAuthSettingNames.cs
new file mode 100644
index 000000000..345b72bbf
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.OAuth/LINGYUN/Abp/Account/OAuth/Settings/AccountOAuthSettingNames.cs
@@ -0,0 +1,30 @@
+namespace LINGYUN.Abp.Account.OAuth.Settings;
+
+public static class AccountOAuthSettingNames
+{
+ public const string GroupName = "Abp.Account.OAuth";
+ public static class GitHub
+ {
+ public const string Prefix = GroupName + ".GitHub";
+ ///
+ /// ClientId
+ ///
+ public const string ClientId = Prefix + ".ClientId";
+ ///
+ /// ClientSecret
+ ///
+ public const string ClientSecret = Prefix + ".ClientSecret";
+ }
+ public static class Bilibili
+ {
+ public const string Prefix = GroupName + ".Bilibili";
+ ///
+ /// ClientId
+ ///
+ public const string ClientId = Prefix + ".ClientId";
+ ///
+ /// ClientSecret
+ ///
+ public const string ClientSecret = Prefix + ".ClientSecret";
+ }
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/AbpAccountWebOAuthModule.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/AbpAccountWebOAuthModule.cs
new file mode 100644
index 000000000..e0be1f337
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/AbpAccountWebOAuthModule.cs
@@ -0,0 +1,105 @@
+using AspNet.Security.OAuth.Bilibili;
+using AspNet.Security.OAuth.GitHub;
+using AspNet.Security.OAuth.QQ;
+using AspNet.Security.OAuth.Weixin;
+using AspNet.Security.OAuth.WorkWeixin;
+using LINGYUN.Abp.Account.OAuth;
+using LINGYUN.Abp.Account.OAuth.Localization;
+using LINGYUN.Abp.Account.Web.OAuth.ExternalProviders.Bilibili;
+using LINGYUN.Abp.Account.Web.OAuth.ExternalProviders.GitHub;
+using LINGYUN.Abp.Account.Web.OAuth.ExternalProviders.QQ;
+using LINGYUN.Abp.Account.Web.OAuth.ExternalProviders.WeChat;
+using LINGYUN.Abp.Account.Web.OAuth.ExternalProviders.WeCom;
+using LINGYUN.Abp.Account.Web.OAuth.Microsoft.Extensions.DependencyInjection;
+using LINGYUN.Abp.Tencent.QQ;
+using LINGYUN.Abp.WeChat.Official;
+using LINGYUN.Abp.WeChat.Work;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Account.Localization;
+using Volo.Abp.AspNetCore.Mvc.Localization;
+using Volo.Abp.Localization;
+using Volo.Abp.Modularity;
+using Volo.Abp.VirtualFileSystem;
+
+namespace LINGYUN.Abp.Account.Web.OAuth;
+
+[DependsOn(typeof(AbpAccountWebModule))]
+[DependsOn(typeof(AbpAccountOAuthModule))]
+[DependsOn(typeof(AbpTencentQQModule))]
+[DependsOn(typeof(AbpWeChatOfficialModule))]
+[DependsOn(typeof(AbpWeChatWorkModule))]
+public class AbpAccountWebOAuthModule : AbpModule
+{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.PreConfigure(options =>
+ {
+ options.AddAssemblyResource(typeof(AccountResource), typeof(AbpAccountWebOAuthModule).Assembly);
+ });
+
+ PreConfigure(mvcBuilder =>
+ {
+ mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAccountWebOAuthModule).Assembly);
+ });
+ }
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded("LINGYUN.Abp.Account.Web.OAuth");
+ });
+
+ Configure(options =>
+ {
+ options.Resources
+ .Get()
+ .AddBaseTypes(typeof(AccountOAuthResource));
+ });
+
+ context.Services
+ .AddAuthentication()
+ .AddGitHub(options =>
+ {
+ options.ClientId = "ClientId";
+ options.ClientSecret = "ClientSecret";
+
+ options.Scope.Add("user:email");
+ }).UseSettingProvider<
+ GitHubAuthenticationOptions,
+ GitHubAuthenticationHandler,
+ GitHubAuthHandlerOptionsProvider>()
+ .AddQQ(options =>
+ {
+ options.ClientId = "ClientId";
+ options.ClientSecret = "ClientSecret";
+ }).UseSettingProvider<
+ QQAuthenticationOptions,
+ QQAuthenticationHandler,
+ QQAuthHandlerOptionsProvider>()
+ .AddWeixin(options =>
+ {
+ options.ClientId = "ClientId";
+ options.ClientSecret = "ClientSecret";
+ }).UseSettingProvider<
+ WeixinAuthenticationOptions,
+ WeixinAuthenticationHandler,
+ WeChatAuthHandlerOptionsProvider>()
+ .AddWorkWeixin(options =>
+ {
+ options.ClientId = "ClientId";
+ options.ClientSecret = "ClientSecret";
+ }).UseSettingProvider<
+ WorkWeixinAuthenticationOptions,
+ WorkWeixinAuthenticationHandler,
+ WeComAuthHandlerOptionsProvider>()
+ .AddBilibili(options =>
+ {
+ options.ClientId = "ClientId";
+ options.ClientSecret = "ClientSecret";
+ }).UseSettingProvider<
+ BilibiliAuthenticationOptions,
+ BilibiliAuthenticationHandler,
+ BilibiliAuthHandlerOptionsProvider>();
+ }
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/AccountAuthenticationRequestHandler.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/AccountAuthenticationRequestHandler.cs
new file mode 100644
index 000000000..1601087fb
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/AccountAuthenticationRequestHandler.cs
@@ -0,0 +1,81 @@
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Http;
+using System;
+using System.Security.Claims;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Account.Web.OAuth.ExternalProviders;
+
+public class AccountAuthenticationRequestHandler : IAuthenticationRequestHandler
+ where TOptions : RemoteAuthenticationOptions, new()
+ where THandler : RemoteAuthenticationHandler
+{
+ protected THandler InnerHandler { get; }
+ protected IOAuthHandlerOptionsProvider OptionsProvider { get; }
+ public AccountAuthenticationRequestHandler(
+ THandler innerHandler,
+ IOAuthHandlerOptionsProvider optionsProvider)
+ {
+ InnerHandler = innerHandler;
+ OptionsProvider = optionsProvider;
+ }
+
+ public virtual async Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
+ {
+ await InnerHandler.InitializeAsync(scheme, context);
+ }
+
+ public virtual async Task AuthenticateAsync()
+ {
+ return await InnerHandler.AuthenticateAsync();
+ }
+
+ public virtual async Task ChallengeAsync(AuthenticationProperties? properties)
+ {
+ await InitializeOptionsAsync();
+
+ await InnerHandler.ChallengeAsync(properties);
+ }
+
+ public virtual async Task ForbidAsync(AuthenticationProperties? properties)
+ {
+ await InnerHandler.ForbidAsync(properties);
+ }
+
+ public async Task SignOutAsync(AuthenticationProperties properties)
+ {
+ if (!(InnerHandler is IAuthenticationSignOutHandler signOutHandler))
+ {
+ throw new InvalidOperationException($"The authentication handler registered for scheme '{InnerHandler.Scheme}' is '{InnerHandler.GetType().Name}' which cannot be used for SignOutAsync");
+ }
+
+ await InitializeOptionsAsync();
+ await signOutHandler.SignOutAsync(properties);
+ }
+
+ public async Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
+ {
+ if (!(InnerHandler is IAuthenticationSignInHandler signInHandler))
+ {
+ throw new InvalidOperationException($"The authentication handler registered for scheme '{InnerHandler.Scheme}' is '{InnerHandler.GetType().Name}' which cannot be used for SignInAsync");
+ }
+
+ await InitializeOptionsAsync();
+ await signInHandler.SignInAsync(user, properties);
+ }
+
+ public virtual async Task HandleRequestAsync()
+ {
+ if (await InnerHandler.ShouldHandleRequestAsync())
+ {
+ await InitializeOptionsAsync();
+ }
+
+ return await InnerHandler.HandleRequestAsync();
+ }
+
+ protected async virtual Task InitializeOptionsAsync()
+ {
+ await OptionsProvider.SetOptionsAsync(InnerHandler.Options);
+ }
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/Bilibili/BilibiliAuthHandlerOptionsProvider.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/Bilibili/BilibiliAuthHandlerOptionsProvider.cs
new file mode 100644
index 000000000..933a4e634
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/Bilibili/BilibiliAuthHandlerOptionsProvider.cs
@@ -0,0 +1,29 @@
+using AspNet.Security.OAuth.Bilibili;
+using LINGYUN.Abp.Account.OAuth.Settings;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.Settings;
+
+namespace LINGYUN.Abp.Account.Web.OAuth.ExternalProviders.Bilibili;
+
+public class BilibiliAuthHandlerOptionsProvider : OAuthHandlerOptionsProvider
+{
+ public BilibiliAuthHandlerOptionsProvider(ISettingProvider settingProvider) : base(settingProvider)
+ {
+ }
+
+ public async override Task SetOptionsAsync(BilibiliAuthenticationOptions options)
+ {
+ var clientId = await SettingProvider.GetOrNullAsync(AccountOAuthSettingNames.Bilibili.ClientId);
+ var clientSecret = await SettingProvider.GetOrNullAsync(AccountOAuthSettingNames.Bilibili.ClientSecret);
+
+ if (!clientId.IsNullOrWhiteSpace())
+ {
+ options.ClientId = clientId;
+ }
+ if (!clientSecret.IsNullOrWhiteSpace())
+ {
+ options.ClientSecret = clientSecret;
+ }
+ }
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/GitHub/GitHubAuthHandlerOptionsProvider.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/GitHub/GitHubAuthHandlerOptionsProvider.cs
new file mode 100644
index 000000000..4bfd43fcf
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/GitHub/GitHubAuthHandlerOptionsProvider.cs
@@ -0,0 +1,29 @@
+using AspNet.Security.OAuth.GitHub;
+using LINGYUN.Abp.Account.OAuth.Settings;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.Settings;
+
+namespace LINGYUN.Abp.Account.Web.OAuth.ExternalProviders.GitHub;
+
+public class GitHubAuthHandlerOptionsProvider : OAuthHandlerOptionsProvider
+{
+ public GitHubAuthHandlerOptionsProvider(ISettingProvider settingProvider) : base(settingProvider)
+ {
+ }
+
+ public async override Task SetOptionsAsync(GitHubAuthenticationOptions options)
+ {
+ var clientId = await SettingProvider.GetOrNullAsync(AccountOAuthSettingNames.GitHub.ClientId);
+ var clientSecret = await SettingProvider.GetOrNullAsync(AccountOAuthSettingNames.GitHub.ClientSecret);
+
+ if (!clientId.IsNullOrWhiteSpace())
+ {
+ options.ClientId = clientId;
+ }
+ if (!clientSecret.IsNullOrWhiteSpace())
+ {
+ options.ClientSecret = clientSecret;
+ }
+ }
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/IOAuthHandlerOptionsProvider.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/IOAuthHandlerOptionsProvider.cs
new file mode 100644
index 000000000..b845a34e5
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/IOAuthHandlerOptionsProvider.cs
@@ -0,0 +1,10 @@
+using Microsoft.AspNetCore.Authentication;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Account.Web.OAuth.ExternalProviders;
+
+public interface IOAuthHandlerOptionsProvider
+ where TOptions : RemoteAuthenticationOptions, new()
+{
+ Task SetOptionsAsync(TOptions options);
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/OAuthExternalProviderService.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/OAuthExternalProviderService.cs
new file mode 100644
index 000000000..e35a66c3b
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/OAuthExternalProviderService.cs
@@ -0,0 +1,68 @@
+using AspNet.Security.OAuth.Bilibili;
+using AspNet.Security.OAuth.GitHub;
+using AspNet.Security.OAuth.QQ;
+using AspNet.Security.OAuth.Weixin;
+using AspNet.Security.OAuth.WorkWeixin;
+using LINGYUN.Abp.Account.OAuth.Features;
+using LINGYUN.Abp.Account.Web.ExternalProviders;
+using LINGYUN.Abp.Account.Web.Models;
+using LINGYUN.Abp.Account.Web.OAuth.Pages.Account.Components.ExternalProviders;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.Extensions.Localization;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Volo.Abp.Account.Localization;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Features;
+
+namespace LINGYUN.Abp.Account.Web.OAuth.ExternalProviders;
+
+public class OAuthExternalProviderService : IExternalProviderService, ITransientDependency
+{
+ private static readonly Dictionary _providerFeaturesMap = new Dictionary
+ {
+ [GitHubAuthenticationDefaults.AuthenticationScheme] = AccountOAuthFeatureNames.GitHub.Enable,
+ [QQAuthenticationDefaults.AuthenticationScheme] = AccountOAuthFeatureNames.QQ.Enable,
+ [WeixinAuthenticationDefaults.AuthenticationScheme] = AccountOAuthFeatureNames.WeChat.Enable,
+ [WorkWeixinAuthenticationDefaults.AuthenticationScheme] = AccountOAuthFeatureNames.WeCom.Enable,
+ [BilibiliAuthenticationDefaults.AuthenticationScheme] = AccountOAuthFeatureNames.Bilibili.Enable
+ };
+
+ private readonly IFeatureChecker _featureChecker;
+ private readonly IStringLocalizer _stringLocalizer;
+ private readonly IAuthenticationSchemeProvider _authenticationSchemeProvider;
+ public OAuthExternalProviderService(
+ IFeatureChecker featureChecker,
+ IStringLocalizer stringLocalizer,
+ IAuthenticationSchemeProvider authenticationSchemeProvider)
+ {
+ _featureChecker = featureChecker;
+ _stringLocalizer = stringLocalizer;
+ _authenticationSchemeProvider = authenticationSchemeProvider;
+ }
+ public async virtual Task> GetAllAsync()
+ {
+ var models = new List();
+
+ var schemas = await _authenticationSchemeProvider.GetAllSchemesAsync();
+
+ foreach (var schema in schemas)
+ {
+ if (_providerFeaturesMap.TryGetValue(schema.Name, out var schemaFeature))
+ {
+ if (await _featureChecker.IsEnabledAsync(schemaFeature))
+ {
+ models.Add(new ExternalLoginProviderModel
+ {
+ Name = schema.Name,
+ AuthenticationScheme = schema.Name,
+ DisplayName = _stringLocalizer[$"OAuth:{schema.Name}"],
+ ComponentType = typeof(ExternalProviderViewComponent),
+ });
+ }
+ }
+ }
+
+ return models;
+ }
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/OAuthHandlerOptionsProvider.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/OAuthHandlerOptionsProvider.cs
new file mode 100644
index 000000000..1fdd23695
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/OAuthHandlerOptionsProvider.cs
@@ -0,0 +1,18 @@
+using Microsoft.AspNetCore.Authentication;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Settings;
+
+namespace LINGYUN.Abp.Account.Web.OAuth.ExternalProviders;
+
+public abstract class OAuthHandlerOptionsProvider : IOAuthHandlerOptionsProvider, ITransientDependency
+ where TOptions : RemoteAuthenticationOptions, new()
+{
+ protected ISettingProvider SettingProvider { get; }
+ public OAuthHandlerOptionsProvider(ISettingProvider settingProvider)
+ {
+ SettingProvider = settingProvider;
+ }
+
+ public abstract Task SetOptionsAsync(TOptions options);
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/QQ/QQAuthHandlerOptionsProvider.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/QQ/QQAuthHandlerOptionsProvider.cs
new file mode 100644
index 000000000..76f3b7b41
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/QQ/QQAuthHandlerOptionsProvider.cs
@@ -0,0 +1,29 @@
+using AspNet.Security.OAuth.QQ;
+using LINGYUN.Abp.Tencent.QQ.Settings;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.Settings;
+
+namespace LINGYUN.Abp.Account.Web.OAuth.ExternalProviders.QQ;
+
+public class QQAuthHandlerOptionsProvider : OAuthHandlerOptionsProvider
+{
+ public QQAuthHandlerOptionsProvider(ISettingProvider settingProvider) : base(settingProvider)
+ {
+ }
+
+ public async override Task SetOptionsAsync(QQAuthenticationOptions options)
+ {
+ var clientId = await SettingProvider.GetOrNullAsync(TencentQQSettingNames.QQConnect.AppId);
+ var clientSecret = await SettingProvider.GetOrNullAsync(TencentQQSettingNames.QQConnect.AppKey);
+
+ if (!clientId.IsNullOrWhiteSpace())
+ {
+ options.ClientId = clientId;
+ }
+ if (!clientSecret.IsNullOrWhiteSpace())
+ {
+ options.ClientSecret = clientSecret;
+ }
+ }
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/WeChat/WeChatAuthHandlerOptionsProvider.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/WeChat/WeChatAuthHandlerOptionsProvider.cs
new file mode 100644
index 000000000..7382370ef
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/WeChat/WeChatAuthHandlerOptionsProvider.cs
@@ -0,0 +1,29 @@
+using AspNet.Security.OAuth.Weixin;
+using LINGYUN.Abp.WeChat.Official.Settings;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.Settings;
+
+namespace LINGYUN.Abp.Account.Web.OAuth.ExternalProviders.WeChat;
+
+public class WeChatAuthHandlerOptionsProvider : OAuthHandlerOptionsProvider
+{
+ public WeChatAuthHandlerOptionsProvider(ISettingProvider settingProvider) : base(settingProvider)
+ {
+ }
+
+ public async override Task SetOptionsAsync(WeixinAuthenticationOptions options)
+ {
+ var clientId = await SettingProvider.GetOrNullAsync(WeChatOfficialSettingNames.AppId);
+ var clientSecret = await SettingProvider.GetOrNullAsync(WeChatOfficialSettingNames.AppSecret);
+
+ if (!clientId.IsNullOrWhiteSpace())
+ {
+ options.ClientId = clientId;
+ }
+ if (!clientSecret.IsNullOrWhiteSpace())
+ {
+ options.ClientSecret = clientSecret;
+ }
+ }
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/WeCom/WeComAuthHandlerOptionsProvider.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/WeCom/WeComAuthHandlerOptionsProvider.cs
new file mode 100644
index 000000000..45d077871
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/ExternalProviders/WeCom/WeComAuthHandlerOptionsProvider.cs
@@ -0,0 +1,34 @@
+using AspNet.Security.OAuth.WorkWeixin;
+using LINGYUN.Abp.WeChat.Work.Settings;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.Settings;
+
+namespace LINGYUN.Abp.Account.Web.OAuth.ExternalProviders.WeCom;
+
+public class WeComAuthHandlerOptionsProvider : OAuthHandlerOptionsProvider
+{
+ public WeComAuthHandlerOptionsProvider(ISettingProvider settingProvider) : base(settingProvider)
+ {
+ }
+
+ public async override Task SetOptionsAsync(WorkWeixinAuthenticationOptions options)
+ {
+ var clientId = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.CorpId);
+ var clientSecret = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.Secret);
+ var agentId = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.AgentId);
+
+ if (!clientId.IsNullOrWhiteSpace())
+ {
+ options.ClientId = clientId;
+ }
+ if (!clientSecret.IsNullOrWhiteSpace())
+ {
+ options.ClientSecret = clientSecret;
+ }
+ if (!agentId.IsNullOrWhiteSpace())
+ {
+ options.AgentId = agentId;
+ }
+ }
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/FodyWeavers.xml b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/FodyWeavers.xsd b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/LINGYUN.Abp.Account.Web.OAuth.csproj b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/LINGYUN.Abp.Account.Web.OAuth.csproj
new file mode 100644
index 000000000..ce4c135c6
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/LINGYUN.Abp.Account.Web.OAuth.csproj
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+ net9.0
+ LINGYUN.Abp.Account.Web.OAuth
+ LINGYUN.Abp.Account.Web.OAuth
+ false
+ false
+ false
+ LINGYUN.Abp.Account.Web.OAuth
+ Library
+ true
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Microsoft/Extensions/DependencyInjection/AuthenticationBuilderExtensions.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Microsoft/Extensions/DependencyInjection/AuthenticationBuilderExtensions.cs
new file mode 100644
index 000000000..8e9749770
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Microsoft/Extensions/DependencyInjection/AuthenticationBuilderExtensions.cs
@@ -0,0 +1,32 @@
+using JetBrains.Annotations;
+using LINGYUN.Abp.Account.Web.OAuth.ExternalProviders;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using System;
+using System.Linq;
+using Volo.Abp;
+
+namespace LINGYUN.Abp.Account.Web.OAuth.Microsoft.Extensions.DependencyInjection;
+
+public static class AuthenticationBuilderExtensions
+{
+ public static AuthenticationBuilder UseSettingProvider(
+ [NotNull] this AuthenticationBuilder authenticationBuilder)
+ where TOptions : RemoteAuthenticationOptions, new()
+ where THandler : RemoteAuthenticationHandler
+ where TOptionsProvider : IOAuthHandlerOptionsProvider
+ {
+ Check.NotNull(authenticationBuilder, nameof(authenticationBuilder));
+
+ var handler = authenticationBuilder.Services.LastOrDefault(x => x.ServiceType == typeof(THandler));
+ authenticationBuilder.Services.Replace(new ServiceDescriptor(
+ typeof(THandler),
+ provider => new AccountAuthenticationRequestHandler(
+ (THandler)ActivatorUtilities.CreateInstance(provider, typeof(THandler)),
+ provider.GetRequiredService()),
+ handler?.Lifetime ?? ServiceLifetime.Transient));
+
+ return authenticationBuilder;
+ }
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/Bilibili/Default.cshtml b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/Bilibili/Default.cshtml
new file mode 100644
index 000000000..74174a1c8
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/Bilibili/Default.cshtml
@@ -0,0 +1,14 @@
+@using Microsoft.AspNetCore.Mvc.Localization
+@using Volo.Abp.Account.Localization
+@inject IHtmlLocalizer L
+@model LINGYUN.Abp.Account.Web.Models.ExternalLoginProviderModel
+
+
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/ExternalProviderViewComponent.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/ExternalProviderViewComponent.cs
new file mode 100644
index 000000000..a28515a41
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/ExternalProviderViewComponent.cs
@@ -0,0 +1,13 @@
+using LINGYUN.Abp.Account.Web.Models;
+using Microsoft.AspNetCore.Mvc;
+using Volo.Abp.AspNetCore.Mvc;
+
+namespace LINGYUN.Abp.Account.Web.OAuth.Pages.Account.Components.ExternalProviders;
+
+public class ExternalProviderViewComponent : AbpViewComponent
+{
+ public virtual IViewComponentResult Invoke(ExternalLoginProviderModel model)
+ {
+ return View($"~/Pages/Account/Components/ExternalProviders/{model.AuthenticationScheme}/Default.cshtml", model);
+ }
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/GitHub/Default.cshtml b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/GitHub/Default.cshtml
new file mode 100644
index 000000000..314b8152e
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/GitHub/Default.cshtml
@@ -0,0 +1,14 @@
+@using Microsoft.AspNetCore.Mvc.Localization
+@using Volo.Abp.Account.Localization
+@inject IHtmlLocalizer L
+@model LINGYUN.Abp.Account.Web.Models.ExternalLoginProviderModel
+
+
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/QQ/Default.cshtml b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/QQ/Default.cshtml
new file mode 100644
index 000000000..dff3f36f7
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/QQ/Default.cshtml
@@ -0,0 +1,14 @@
+@using Microsoft.AspNetCore.Mvc.Localization
+@using Volo.Abp.Account.Localization
+@inject IHtmlLocalizer L
+@model LINGYUN.Abp.Account.Web.Models.ExternalLoginProviderModel
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/Weixin/Default.cshtml b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/Weixin/Default.cshtml
new file mode 100644
index 000000000..c94fa9662
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/Weixin/Default.cshtml
@@ -0,0 +1,14 @@
+@using Microsoft.AspNetCore.Mvc.Localization
+@using Volo.Abp.Account.Localization
+@inject IHtmlLocalizer L
+@model LINGYUN.Abp.Account.Web.Models.ExternalLoginProviderModel
+
+
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/WorkWeixin/Default.cshtml b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/WorkWeixin/Default.cshtml
new file mode 100644
index 000000000..1170c32b6
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Pages/Account/Components/ExternalProviders/WorkWeixin/Default.cshtml
@@ -0,0 +1,13 @@
+@using Microsoft.AspNetCore.Mvc.Localization
+@using Volo.Abp.Account.Localization
+@inject IHtmlLocalizer L
+@model LINGYUN.Abp.Account.Web.Models.ExternalLoginProviderModel
+
+
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Properties/launchSettings.json b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Properties/launchSettings.json
new file mode 100644
index 000000000..0b8d5f6e6
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/Properties/launchSettings.json
@@ -0,0 +1,12 @@
+{
+ "profiles": {
+ "LINGYUN.Abp.Account.Web.OAuth": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:50897;http://localhost:50898"
+ }
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/wwwroot/images/bilibili_logo_18x18.png b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/wwwroot/images/bilibili_logo_18x18.png
new file mode 100644
index 000000000..610a0d31a
Binary files /dev/null and b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/wwwroot/images/bilibili_logo_18x18.png differ
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/wwwroot/images/qq_logo_15x18.png b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/wwwroot/images/qq_logo_15x18.png
new file mode 100644
index 000000000..97efc10fb
Binary files /dev/null and b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/wwwroot/images/qq_logo_15x18.png differ
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/wwwroot/images/wecom_logo_77x18.png b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/wwwroot/images/wecom_logo_77x18.png
new file mode 100644
index 000000000..5f828dcc7
Binary files /dev/null and b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OAuth/wwwroot/images/wecom_logo_77x18.png differ
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/ExternalProviders/IExternalProviderService.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/ExternalProviders/IExternalProviderService.cs
new file mode 100644
index 000000000..3ba0087b3
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/ExternalProviders/IExternalProviderService.cs
@@ -0,0 +1,10 @@
+using LINGYUN.Abp.Account.Web.Models;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Account.Web.ExternalProviders;
+
+public interface IExternalProviderService
+{
+ Task> GetAllAsync();
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Models/ExternalLoginProviderModel.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Models/ExternalLoginProviderModel.cs
new file mode 100644
index 000000000..506398975
--- /dev/null
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Models/ExternalLoginProviderModel.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace LINGYUN.Abp.Account.Web.Models;
+
+public class ExternalLoginProviderModel
+{
+ public Type ComponentType { get; set; }
+ public string Name { get; set; }
+ public string DisplayName { get; set; }
+ public string AuthenticationScheme { get; set; }
+}
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/ChangePassword.cshtml.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/ChangePassword.cshtml.cs
index e90aa16f6..9d54fb631 100644
--- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/ChangePassword.cshtml.cs
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/ChangePassword.cshtml.cs
@@ -90,6 +90,11 @@ public class ChangePasswordModel : AccountPageModel
Alerts.Warning(L["NewPasswordSameAsOld"]);
return Page();
}
+ if (Input.NewPassword != Input.NewPasswordConfirm)
+ {
+ Alerts.Warning(L["NewPasswordConfirmFailed"]);
+ return Page();
+ }
var userInfo = await GetCurrentUser();
if (userInfo != null)
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.cshtml b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.cshtml
index f1e346cad..06a4e0835 100644
--- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.cshtml
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.cshtml
@@ -74,7 +74,20 @@
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.cshtml.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.cshtml.cs
index 4c8c28d64..eb331a83a 100644
--- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.cshtml.cs
+++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.cshtml.cs
@@ -1,6 +1,9 @@
+using LINGYUN.Abp.Account.Web.ExternalProviders;
+using LINGYUN.Abp.Account.Web.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
@@ -8,30 +11,81 @@ using System.Diagnostics;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
+using Volo.Abp;
using Volo.Abp.Account.Settings;
using Volo.Abp.Account.Web;
+using Volo.Abp.Account.Web.Pages.Account;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.Identity.AspNetCore;
+using Volo.Abp.Reflection;
using Volo.Abp.Security.Claims;
using Volo.Abp.Settings;
+using Volo.Abp.Validation;
+using static Volo.Abp.Account.Web.Pages.Account.LoginModel;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
namespace LINGYUN.Abp.Account.Web.Pages.Account;
-[ExposeServices(typeof(Volo.Abp.Account.Web.Pages.Account.LoginModel))]
-public class LoginModel : Volo.Abp.Account.Web.Pages.Account.LoginModel
+//[ExposeServices(typeof(Volo.Abp.Account.Web.Pages.Account.LoginModel))]
+public class LoginModel : AccountPageModel
{
+ [HiddenInput]
+ [BindProperty(SupportsGet = true)]
+ public string ReturnUrl { get; set; }
+
+ [HiddenInput]
+ [BindProperty(SupportsGet = true)]
+ public string ReturnUrlHash { get; set; }
+
+ [BindProperty]
+ public LoginInputModel LoginInput { get; set; }
+
+ public bool EnableLocalLogin { get; set; }
+
+ public bool ShowCancelButton { get; set; }
+ public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1;
+ public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null;
+
+ public IEnumerable ExternalProviders { get; set; }
+ public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !x.DisplayName.IsNullOrWhiteSpace());
+
+
+ protected IExternalProviderService ExternalProviderService { get; }
+ protected IAuthenticationSchemeProvider SchemeProvider { get; }
+ protected AbpAccountOptions AccountOptions { get; }
+ protected IdentityDynamicClaimsPrincipalContributorCache IdentityDynamicClaimsPrincipalContributorCache { get; }
public LoginModel(
+ IExternalProviderService externalProviderService,
IAuthenticationSchemeProvider schemeProvider,
IOptions accountOptions,
IOptions identityOptions,
IdentityDynamicClaimsPrincipalContributorCache identityDynamicClaimsPrincipalContributorCache)
- : base(schemeProvider, accountOptions, identityOptions, identityDynamicClaimsPrincipalContributorCache)
{
+ ExternalProviderService = externalProviderService;
+ SchemeProvider = schemeProvider;
+ IdentityOptions = identityOptions;
+ AccountOptions = accountOptions.Value;
+ IdentityDynamicClaimsPrincipalContributorCache = identityDynamicClaimsPrincipalContributorCache;
}
- public async override Task OnPostAsync(string action)
+ public virtual async Task OnGetAsync()
+ {
+ LoginInput = new LoginInputModel();
+
+ ExternalProviders = await GetExternalProviders();
+
+ EnableLocalLogin = await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin);
+
+ if (IsExternalLoginOnly)
+ {
+ return await OnPostExternalLogin(ExternalProviders.First().AuthenticationScheme);
+ }
+
+ return Page();
+ }
+
+ public async virtual Task OnPostAsync(string action)
{
await CheckLocalLoginAsync();
@@ -66,44 +120,17 @@ public class LoginModel : Volo.Abp.Account.Web.Pages.Account.LoginModel
if (result.IsLockedOut)
{
- Alerts.Warning(L["UserLockedOutMessage"]);
- return Page();
+ return await HandleUserLockedOut();
}
if (result.IsNotAllowed)
{
- var notAllowedUser = await GetIdentityUserAsync(LoginInput.UserNameOrEmailAddress);
- if (await UserManager.CheckPasswordAsync(notAllowedUser, LoginInput.Password))
- {
- // û
- if (notAllowedUser.ShouldChangePasswordOnNextLogin || await UserManager.ShouldPeriodicallyChangePasswordAsync(notAllowedUser))
- {
- var changePwdIdentity = new ClaimsIdentity(AbpAccountAuthenticationTypes.ShouldChangePassword);
- changePwdIdentity.AddClaim(new Claim(AbpClaimTypes.UserId, notAllowedUser.Id.ToString()));
- if (notAllowedUser.TenantId.HasValue)
- {
- changePwdIdentity.AddClaim(new Claim(AbpClaimTypes.TenantId, notAllowedUser.TenantId.ToString()));
- }
-
- await HttpContext.SignInAsync(AbpAccountAuthenticationTypes.ShouldChangePassword, new ClaimsPrincipal(changePwdIdentity));
-
- return RedirectToPage("ChangePassword", new
- {
- returnUrl = ReturnUrl,
- returnUrlHash = ReturnUrlHash,
- rememberMe = LoginInput.RememberMe
- });
- }
- }
-
- Alerts.Warning(L["LoginIsNotAllowed"]);
- return Page();
+ return await HandleUserNotAllowed();
}
if (!result.Succeeded)
{
- Alerts.Danger(L["InvalidUserNameOrPassword"]);
- return Page();
+ return await HandleUserNameOrPasswordInvalid();
}
//TODO: Find a way of getting user's id from the logged in user and do not query it again like that!
@@ -117,7 +144,123 @@ public class LoginModel : Volo.Abp.Account.Web.Pages.Account.LoginModel
return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash);
}
- protected override Task TwoFactorLoginResultAsync()
+ public virtual async Task OnPostExternalLogin(string provider)
+ {
+ var redirectUrl = Url.Page("./Login", pageHandler: "ExternalLoginCallback", values: new { ReturnUrl, ReturnUrlHash });
+ var properties = SignInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
+ properties.Items["scheme"] = provider;
+
+ return await Task.FromResult(Challenge(properties, provider));
+ }
+
+ public virtual async Task OnGetExternalLoginCallbackAsync(string returnUrl = "", string returnUrlHash = "", string remoteError = null)
+ {
+ //TODO: Did not implemented Identity Server 4 sample for this method (see ExternalLoginCallback in Quickstart of IDS4 sample)
+ /* Also did not implement these:
+ * - Logout(string logoutId)
+ */
+
+ if (remoteError != null)
+ {
+ Logger.LogWarning($"External login callback error: {remoteError}");
+ return RedirectToPage("./Login");
+ }
+
+ await IdentityOptions.SetAsync();
+
+ var loginInfo = await SignInManager.GetExternalLoginInfoAsync();
+ if (loginInfo == null)
+ {
+ Logger.LogWarning("External login info is not available");
+ return RedirectToPage("./Login");
+ }
+
+ var result = await SignInManager.ExternalLoginSignInAsync(
+ loginInfo.LoginProvider,
+ loginInfo.ProviderKey,
+ isPersistent: false,
+ bypassTwoFactor: true
+ );
+
+ if (!result.Succeeded)
+ {
+ await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
+ {
+ Identity = IdentitySecurityLogIdentityConsts.IdentityExternal,
+ Action = "Login" + result
+ });
+ }
+
+ if (result.IsLockedOut)
+ {
+ Logger.LogWarning($"External login callback error: user is locked out!");
+ throw new UserFriendlyException("Cannot proceed because user is locked out!");
+ }
+
+ if (result.IsNotAllowed)
+ {
+ Logger.LogWarning($"External login callback error: user is not allowed!");
+ throw new UserFriendlyException("Cannot proceed because user is not allowed!");
+ }
+
+ IdentityUser user;
+ if (result.Succeeded)
+ {
+ user = await UserManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey);
+ if (user != null)
+ {
+ // Clear the dynamic claims cache.
+ await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
+ }
+
+ return await RedirectSafelyAsync(returnUrl, returnUrlHash);
+ }
+
+ //TODO: Handle other cases for result!
+
+ var email = loginInfo.Principal.FindFirstValue(AbpClaimTypes.Email) ?? loginInfo.Principal.FindFirstValue(ClaimTypes.Email);
+ if (email.IsNullOrWhiteSpace())
+ {
+ return RedirectToPage("./Register", new
+ {
+ IsExternalLogin = true,
+ ExternalLoginAuthSchema = loginInfo.LoginProvider,
+ ReturnUrl = returnUrl
+ });
+ }
+
+ user = await UserManager.FindByEmailAsync(email);
+ if (user == null)
+ {
+ return RedirectToPage("./Register", new
+ {
+ IsExternalLogin = true,
+ ExternalLoginAuthSchema = loginInfo.LoginProvider,
+ ReturnUrl = returnUrl
+ });
+ }
+
+ if (await UserManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey) == null)
+ {
+ CheckIdentityErrors(await UserManager.AddLoginAsync(user, loginInfo));
+ }
+
+ await SignInManager.SignInAsync(user, false);
+
+ await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
+ {
+ Identity = IdentitySecurityLogIdentityConsts.IdentityExternal,
+ Action = result.ToIdentitySecurityLogAction(),
+ UserName = user.Name
+ });
+
+ // Clear the dynamic claims cache.
+ await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
+
+ return await RedirectSafelyAsync(returnUrl, returnUrlHash);
+ }
+
+ protected virtual Task TwoFactorLoginResultAsync()
{
// ض˫֤ҳ
return Task.FromResult(RedirectToPage("SendCode", new
@@ -128,39 +271,117 @@ public class LoginModel : Volo.Abp.Account.Web.Pages.Account.LoginModel
}));
}
+
protected virtual async Task GetIdentityUserAsync(string userNameOrEmailAddress)
{
return await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ??
await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress);
}
- protected async override Task> GetExternalProviders()
+ protected async virtual Task> GetExternalProviders()
{
var schemes = await SchemeProvider.GetAllSchemesAsync();
+ var externalProviders = await ExternalProviderService.GetAllAsync();
- var providers = schemes
- .Where(x => x.DisplayName != null || x.Name.Equals(AccountOptions.WindowsAuthenticationSchemeName, StringComparison.OrdinalIgnoreCase))
- .Select(x => new ExternalProviderModel
- {
- DisplayName = x.DisplayName,
- AuthenticationScheme = x.Name
- })
- .ToList();
-
- foreach (var provider in providers)
+ var externalProviderModels = new List();
+ foreach (var scheme in schemes)
{
- var localizedDisplayName = L[provider.DisplayName];
- if (localizedDisplayName.ResourceNotFound)
+ if (TryGetExternalLoginProvider(scheme, externalProviders, out var externalLoginProvider) ||
+ scheme.Name.Equals(AccountOptions.WindowsAuthenticationSchemeName, StringComparison.OrdinalIgnoreCase))
{
- localizedDisplayName = L["AuthenticationScheme:" + provider.DisplayName];
+ externalProviderModels.Add(new ExternalLoginProviderModel
+ {
+ Name = externalLoginProvider.Name,
+ AuthenticationScheme = scheme.Name,
+ DisplayName = externalLoginProvider.DisplayName,
+ ComponentType = externalLoginProvider.ComponentType,
+ });
}
+ }
- if (!localizedDisplayName.ResourceNotFound)
+ return externalProviderModels;
+ }
+
+ protected virtual bool TryGetExternalLoginProvider(AuthenticationScheme scheme, List externalProviders, out ExternalLoginProviderModel externalLoginProvider)
+ {
+ if (ReflectionHelper.IsAssignableToGenericType(scheme.HandlerType, typeof(RemoteAuthenticationHandler<>)))
+ {
+ externalLoginProvider = externalProviders.FirstOrDefault(x => x.Name == scheme.Name);
+ return externalLoginProvider != null;
+ }
+
+ externalLoginProvider = null;
+ return false;
+ }
+
+ protected virtual async Task ReplaceEmailToUsernameOfInputIfNeeds()
+ {
+ if (!ValidationHelper.IsValidEmailAddress(LoginInput.UserNameOrEmailAddress))
+ {
+ return;
+ }
+
+ var userByUsername = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress);
+ if (userByUsername != null)
+ {
+ return;
+ }
+
+ var userByEmail = await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress);
+ if (userByEmail == null)
+ {
+ return;
+ }
+
+ LoginInput.UserNameOrEmailAddress = userByEmail.UserName;
+ }
+
+ protected virtual async Task CheckLocalLoginAsync()
+ {
+ if (!await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin))
+ {
+ throw new UserFriendlyException(L["LocalLoginDisabledMessage"]);
+ }
+ }
+
+ protected virtual Task HandleUserLockedOut()
+ {
+ Alerts.Warning(L["UserLockedOutMessage"]);
+ return Task.FromResult(Page());
+ }
+
+ protected async virtual Task HandleUserNotAllowed()
+ {
+ var notAllowedUser = await GetIdentityUserAsync(LoginInput.UserNameOrEmailAddress);
+ if (await UserManager.CheckPasswordAsync(notAllowedUser, LoginInput.Password))
+ {
+ // û
+ if (notAllowedUser.ShouldChangePasswordOnNextLogin || await UserManager.ShouldPeriodicallyChangePasswordAsync(notAllowedUser))
{
- provider.DisplayName = localizedDisplayName.Value;
+ var changePwdIdentity = new ClaimsIdentity(AbpAccountAuthenticationTypes.ShouldChangePassword);
+ changePwdIdentity.AddClaim(new Claim(AbpClaimTypes.UserId, notAllowedUser.Id.ToString()));
+ if (notAllowedUser.TenantId.HasValue)
+ {
+ changePwdIdentity.AddClaim(new Claim(AbpClaimTypes.TenantId, notAllowedUser.TenantId.ToString()));
+ }
+
+ await HttpContext.SignInAsync(AbpAccountAuthenticationTypes.ShouldChangePassword, new ClaimsPrincipal(changePwdIdentity));
+
+ return RedirectToPage("ChangePassword", new
+ {
+ returnUrl = ReturnUrl,
+ returnUrlHash = ReturnUrlHash,
+ rememberMe = LoginInput.RememberMe
+ });
}
}
+ Alerts.Warning(L["LoginIsNotAllowed"]);
+ return Page();
+ }
- return providers;
+ protected virtual Task HandleUserNameOrPasswordInvalid()
+ {
+ Alerts.Danger(L["InvalidUserNameOrPassword"]);
+ return Task.FromResult(Page());
}
}
diff --git a/aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.WeChat/LINGYUN/Abp/OpenIddict/WeChat/WeChatOffcialTokenExtensionGrant.cs b/aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.WeChat/LINGYUN/Abp/OpenIddict/WeChat/WeChatOffcialTokenExtensionGrant.cs
index a802e5624..2a9290767 100644
--- a/aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.WeChat/LINGYUN/Abp/OpenIddict/WeChat/WeChatOffcialTokenExtensionGrant.cs
+++ b/aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.WeChat/LINGYUN/Abp/OpenIddict/WeChat/WeChatOffcialTokenExtensionGrant.cs
@@ -1,12 +1,7 @@
using LINGYUN.Abp.WeChat.Official;
-using LINGYUN.Abp.WeChat.Official.Features;
using LINGYUN.Abp.WeChat.OpenId;
-using Microsoft.Extensions.Localization;
using System.Threading.Tasks;
-using Volo.Abp;
-using Volo.Abp.Features;
using Volo.Abp.OpenIddict.ExtensionGrantTypes;
-using Volo.Abp.OpenIddict.Localization;
namespace LINGYUN.Abp.OpenIddict.WeChat;
public class WeChatOffcialTokenExtensionGrant : WeChatTokenExtensionGrant
@@ -17,17 +12,17 @@ public class WeChatOffcialTokenExtensionGrant : WeChatTokenExtensionGrant
public override string AuthenticationMethod => AbpWeChatOfficialConsts.AuthenticationMethod;
- protected async override Task CheckFeatureAsync(ExtensionGrantContext context)
- {
- var featureChecker = GetRequiredService(context);
+ //protected async override Task CheckFeatureAsync(ExtensionGrantContext context)
+ //{
+ // var featureChecker = GetRequiredService(context);
- if (!await featureChecker.IsEnabledAsync(WeChatOfficialFeatures.EnableAuthorization))
- {
- var localizer = GetRequiredService>(context);
+ // if (!await featureChecker.IsEnabledAsync(WeChatOfficialFeatures.EnableAuthorization))
+ // {
+ // var localizer = GetRequiredService>(context);
- throw new AbpException(localizer["OfficialAuthorizationDisabledMessage"]);
- }
- }
+ // throw new AbpException(localizer["OfficialAuthorizationDisabledMessage"]);
+ // }
+ //}
protected async override Task FindOpenIdAsync(ExtensionGrantContext context, string code)
{
diff --git a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN.Abp.SettingManagement.Application.csproj b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN.Abp.SettingManagement.Application.csproj
index 7bbf2f072..14ff0c1fb 100644
--- a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN.Abp.SettingManagement.Application.csproj
+++ b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN.Abp.SettingManagement.Application.csproj
@@ -28,6 +28,7 @@
+
diff --git a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/AbpSettingManagementApplicationModule.cs b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/AbpSettingManagementApplicationModule.cs
index 0794787ed..948f517ef 100644
--- a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/AbpSettingManagementApplicationModule.cs
+++ b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/AbpSettingManagementApplicationModule.cs
@@ -1,4 +1,7 @@
-using Microsoft.Extensions.DependencyInjection;
+using LINGYUN.Abp.Account;
+using LINGYUN.Abp.Account.OAuth;
+using LINGYUN.Abp.Account.OAuth.Localization;
+using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Application;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
@@ -13,6 +16,8 @@ namespace LINGYUN.Abp.SettingManagement;
typeof(AbpSettingManagementDomainModule),
typeof(AbpSettingManagementApplicationContractsModule),
typeof(VoloAbpSettingManagementApplicationContractsModule),
+ typeof(AbpAccountApplicationContractsModule),
+ typeof(AbpAccountOAuthModule),
typeof(AbpDddApplicationModule)
)]
public class AbpSettingManagementApplicationModule : AbpModule
@@ -29,7 +34,8 @@ public class AbpSettingManagementApplicationModule : AbpModule
Configure(options =>
{
options.Resources.Get()
- .AddVirtualJson("/LINGYUN/Abp/SettingManagement/Localization/Resources");
+ .AddVirtualJson("/LINGYUN/Abp/SettingManagement/Localization/Resources")
+ .AddBaseTypes(typeof(AccountOAuthResource));
});
}
}
diff --git a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs
index 3c3a4b019..7a40bc1ac 100644
--- a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs
+++ b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs
@@ -1,4 +1,6 @@
-using LINGYUN.Abp.Identity;
+using LINGYUN.Abp.Account.OAuth.Features;
+using LINGYUN.Abp.Account.OAuth.Settings;
+using LINGYUN.Abp.Identity;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using System.Linq;
@@ -489,6 +491,48 @@ public class SettingAppService : ApplicationService, ISettingAppService, ISettin
#endregion
+ #region 外部登录
+
+ var oauthSettingDto = new SettingGroupDto(L["Settings:ExternalOAuthLogin"], L["Settings:ExternalOAuthLogin"]);
+
+ if (await FeatureChecker.IsEnabledAsync(true, AccountOAuthFeatureNames.GitHub.Enable))
+ {
+ var githubOAuthSetting = oauthSettingDto.AddSetting(L["Settings:GitHubAuth"], L["Settings:GitHubAuth"]);
+ githubOAuthSetting.AddDetail(
+ await SettingDefinitionManager.GetAsync(AccountOAuthSettingNames.GitHub.ClientId),
+ StringLocalizerFactory,
+ await SettingManager.GetOrNullAsync(AccountOAuthSettingNames.GitHub.ClientId, providerName, providerKey),
+ ValueType.String,
+ providerName);
+ githubOAuthSetting.AddDetail(
+ await SettingDefinitionManager.GetAsync(AccountOAuthSettingNames.GitHub.ClientSecret),
+ StringLocalizerFactory,
+ await SettingManager.GetOrNullAsync(AccountOAuthSettingNames.GitHub.ClientSecret, providerName, providerKey),
+ ValueType.String,
+ providerName);
+ }
+
+ if (await FeatureChecker.IsEnabledAsync(true, AccountOAuthFeatureNames.Bilibili.Enable))
+ {
+ var bilibiliOAuthSetting = oauthSettingDto.AddSetting(L["Settings:BilibiliAuth"], L["Settings:BilibiliAuth"]);
+ bilibiliOAuthSetting.AddDetail(
+ await SettingDefinitionManager.GetAsync(AccountOAuthSettingNames.Bilibili.ClientId),
+ StringLocalizerFactory,
+ await SettingManager.GetOrNullAsync(AccountOAuthSettingNames.Bilibili.ClientId, providerName, providerKey),
+ ValueType.String,
+ providerName);
+ bilibiliOAuthSetting.AddDetail(
+ await SettingDefinitionManager.GetAsync(AccountOAuthSettingNames.Bilibili.ClientSecret),
+ StringLocalizerFactory,
+ await SettingManager.GetOrNullAsync(AccountOAuthSettingNames.Bilibili.ClientSecret, providerName, providerKey),
+ ValueType.String,
+ providerName);
+ }
+
+ settingGroups.AddGroup(oauthSettingDto);
+
+ #endregion
+
return settingGroups;
}