Skip to content

Commit 7993114

Browse files
committed
feat(account): add external login binding
1 parent b04784c commit 7993114

File tree

13 files changed

+397
-11
lines changed

13 files changed

+397
-11
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace LINGYUN.Abp.Account;
2+
public class ExternalLoginInfoDto
3+
{
4+
public string Name { get; set; }
5+
6+
public string DisplayName { get; set; }
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using System.Collections.Generic;
2+
3+
namespace LINGYUN.Abp.Account;
4+
public class ExternalLoginResultDto
5+
{
6+
public List<UserLoginInfoDto> UserLogins { get; set; } = new List<UserLoginInfoDto>();
7+
public List<ExternalLoginInfoDto> ExternalLogins { get; set; } = new List<ExternalLoginInfoDto>();
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System.ComponentModel.DataAnnotations;
2+
3+
namespace LINGYUN.Abp.Account;
4+
public class RemoveExternalLoginInput
5+
{
6+
[Required]
7+
public string LoginProvider { get; set; }
8+
9+
[Required]
10+
public string ProviderKey { get; set; }
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace LINGYUN.Abp.Account;
2+
public class UserLoginInfoDto
3+
{
4+
public string LoginProvider { get; set; }
5+
public string ProviderKey { get; set; }
6+
public string ProviderDisplayName { get; set; }
7+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<Import Project="..\..\..\..\configureawait.props" />
4+
<Import Project="..\..\..\..\common.props" />
5+
6+
<PropertyGroup>
7+
<TargetFramework>net9.0</TargetFramework>
8+
<AssemblyName>LINGYUN.Abp.Account.WeChat.Work</AssemblyName>
9+
<PackageId>LINGYUN.Abp.Account.WeChat.Work</PackageId>
10+
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
11+
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
12+
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
13+
<RootNamespace />
14+
</PropertyGroup>
15+
16+
<ItemGroup>
17+
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" />
18+
</ItemGroup>
19+
20+
<ItemGroup>
21+
<ProjectReference Include="..\..\..\framework\wechat\LINGYUN.Abp.WeChat.Work\LINGYUN.Abp.WeChat.Work.csproj" />
22+
<ProjectReference Include="..\..\identity\LINGYUN.Abp.Identity.Domain\LINGYUN.Abp.Identity.Domain.csproj" />
23+
</ItemGroup>
24+
25+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using LINGYUN.Abp.WeChat.Work;
2+
using LINGYUN.Abp.WeChat.Work.Localization;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using Volo.Abp.AspNetCore.Mvc;
5+
using Volo.Abp.AspNetCore.Mvc.Localization;
6+
using Volo.Abp.Modularity;
7+
8+
namespace LINGYUN.Abp.Account.WeChat.Work;
9+
10+
[DependsOn(
11+
typeof(AbpWeChatWorkModule),
12+
typeof(AbpAspNetCoreMvcModule))]
13+
public class AbpAccountWeChatWorkModule : AbpModule
14+
{
15+
public override void PreConfigureServices(ServiceConfigurationContext context)
16+
{
17+
PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options =>
18+
{
19+
options.AddAssemblyResource(typeof(WeChatWorkResource), typeof(AbpAccountWeChatWorkModule).Assembly);
20+
});
21+
22+
PreConfigure<IMvcBuilder>(mvcBuilder =>
23+
{
24+
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAccountWeChatWorkModule).Assembly);
25+
});
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using Microsoft.AspNetCore.Authorization;
2+
using System.Threading.Tasks;
3+
using Volo.Abp.AspNetCore.Mvc;
4+
using Volo.Abp.Identity;
5+
using Volo.Abp.Users;
6+
7+
namespace LINGYUN.Abp.Account.WeChat.Work.Controllers;
8+
9+
[Authorize]
10+
public class WeChatWorkAccountController : AbpControllerBase
11+
{
12+
private readonly IdentityUserManager identityUserManager;
13+
/// <summary>
14+
/// 绑定用户
15+
/// </summary>
16+
/// <param name="code"></param>
17+
/// <returns></returns>
18+
public async virtual Task BindAsync(string code)
19+
{
20+
var user = await identityUserManager.GetByIdAsync(CurrentUser.GetId());
21+
22+
}
23+
}

aspnet-core/modules/account/LINGYUN.Abp.Account.Web/AbpAccountAuthenticationTypes.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
public static class AbpAccountAuthenticationTypes
44
{
55
public const string ShouldChangePassword = "Abp.Account.ShouldChangePassword";
6+
public const string ConfirmUserScheme = "Abp.Account.ConfirmUser";
67
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
using LINGYUN.Abp.Account.Web.ExternalProviders;
2+
using Microsoft.AspNetCore.Authorization;
3+
using Microsoft.AspNetCore.Mvc;
4+
using Microsoft.AspNetCore.WebUtilities;
5+
using Microsoft.Extensions.Logging;
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Linq;
9+
using System.Security.Claims;
10+
using System.Threading.Tasks;
11+
using Volo.Abp;
12+
using Volo.Abp.Account;
13+
using Volo.Abp.Account.Localization;
14+
using Volo.Abp.AspNetCore.Mvc;
15+
using Volo.Abp.Identity;
16+
using Volo.Abp.Identity.AspNetCore;
17+
using Volo.Abp.Users;
18+
19+
namespace LINGYUN.Abp.Account.Web.Areas.Account.Controllers;
20+
21+
[Controller]
22+
[Area(AccountRemoteServiceConsts.ModuleName)]
23+
[Route($"api/{AccountRemoteServiceConsts.ModuleName}")]
24+
[RemoteService(Name = AccountRemoteServiceConsts.RemoteServiceName)]
25+
public class AccountController : AbpController
26+
{
27+
protected AbpSignInManager SignInManager => LazyServiceProvider.LazyGetRequiredService<AbpSignInManager>();
28+
protected IdentityUserManager UserManager => LazyServiceProvider.LazyGetRequiredService<IdentityUserManager>();
29+
protected IExternalProviderService ExternalProviderService => LazyServiceProvider.LazyGetRequiredService<IExternalProviderService>();
30+
31+
public AccountController()
32+
{
33+
LocalizationResource = typeof(AccountResource);
34+
}
35+
36+
[HttpGet]
37+
[Authorize]
38+
[Route("external-logins")]
39+
public async virtual Task<ExternalLoginResultDto> GetExternalLoginsAsync()
40+
{
41+
var currentUser = await UserManager.GetByIdAsync(CurrentUser.GetId());
42+
var userLogins = await UserManager.GetLoginsAsync(currentUser);
43+
var externalProviders = await ExternalProviderService.GetAllAsync();
44+
45+
return new ExternalLoginResultDto
46+
{
47+
UserLogins = userLogins.Select(x => new UserLoginInfoDto
48+
{
49+
ProviderDisplayName = x.ProviderDisplayName,
50+
ProviderKey = x.ProviderKey,
51+
LoginProvider = x.LoginProvider,
52+
}).ToList(),
53+
ExternalLogins = externalProviders.Select(x =>
54+
{
55+
return new ExternalLoginInfoDto
56+
{
57+
Name = x.Name,
58+
DisplayName = x.DisplayName,
59+
};
60+
}).ToList(),
61+
};
62+
}
63+
64+
[HttpDelete]
65+
[Authorize]
66+
[Route("external-logins/remove")]
67+
public async virtual Task RemoveExternalLoginAsync(RemoveExternalLoginInput input)
68+
{
69+
var currentUser = await UserManager.GetByIdAsync(CurrentUser.GetId());
70+
var identityResult = await UserManager.RemoveLoginAsync(
71+
currentUser,
72+
input.LoginProvider,
73+
input.ProviderKey);
74+
75+
if (!identityResult.Succeeded)
76+
{
77+
throw new UserFriendlyException("Operation failed: " + identityResult.Errors.Select(e => $"[{e.Code}] {e.Description}").JoinAsString(", "));
78+
}
79+
// 解绑的是当前身份认证方案则退出登录
80+
var amr = CurrentUser.FindClaimValue(ClaimTypes.AuthenticationMethod);
81+
if (!amr.IsNullOrWhiteSpace() && string.Equals(amr, input.LoginProvider, StringComparison.InvariantCultureIgnoreCase))
82+
{
83+
await SignInManager.SignOutAsync();
84+
}
85+
}
86+
87+
[HttpGet]
88+
[Authorize]
89+
[Route("external-logins/bind")]
90+
public virtual async Task<IActionResult> ExternalLoginBindAsync(string provider, string returnUrl)
91+
{
92+
if (string.IsNullOrWhiteSpace(provider) || string.IsNullOrWhiteSpace(returnUrl))
93+
{
94+
Logger.LogWarning("The parameter is incorrect");
95+
return Redirect(QueryHelpers.AddQueryString(returnUrl, new Dictionary<string, string>()
96+
{
97+
["error"] = "The parameter is incorrect"
98+
}));
99+
}
100+
101+
var tenantId = CurrentTenant.Id;
102+
var userId = CurrentUser.GetId();
103+
104+
var redirectUrl = Url.Page("/Account/ExternalLoginBind", pageHandler: "BindCallback", values: new { returnUrl, userId, tenantId });
105+
106+
var properties = SignInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, userId.ToString());
107+
properties.Items["scheme"] = provider;
108+
109+
return await Task.FromResult(Challenge(properties, provider));
110+
}
111+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@page
2+
@model LINGYUN.Abp.Account.Web.Pages.Account.ExternalLoginBindModel
3+
@{
4+
}

0 commit comments

Comments
 (0)