Boj*_*jan 6 c# identity discord asp.net-core-6.0 .net-6.0
我正在尝试弄清楚如何在使用 Dapper 作为我的 ORM 的同时通过 Discord Oauth2 设置登录。
微软这里有一个指南,我按照它来设置我的所有商店。我实际上可以打电话CreateAsync()方法并在我的数据库中创建用户,所以我相信事情的一面已经完全设置完毕。
我的问题在于外部登录。下面你会发现我已经尝试过的内容。
程序.cs:
//omitted code that binds interfaces and classes - this code works and is fully tested. it is not related to problem at hand.
builder.Services.AddIdentity<User, Role>()
.AddDefaultTokenProviders();
builder.Services.AddAuthentication()
.AddCookie(options =>
{
options.LoginPath = "/signin";
options.LogoutPath = "/signout";
})
.AddDiscord(options =>
{
options.ClientId = "some id";
options.ClientSecret = "some secret";
options.ClaimActions.MapCustomJson("urn:discord:avatar:url", user =>
string.Format(
CultureInfo.InvariantCulture,
"https://cdn.discordapp.com/avatars/{0}/{1}.{2}",
user.GetString("id"),
user.GetString("avatar"),
user.GetString("avatar")!.StartsWith("a_") ? "gif" : "png"));
});
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseDeveloperExceptionPage();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
app.Run();
Run Code Online (Sandbox Code Playgroud)
这是帐户控制器代码:
public class AccountController : Controller
{
private readonly ISignInService _signInService;
private readonly IUserService _userService;
public AccountController(ISignInService signInService, IUserService userService)
{
_signInService = signInService;
_userService = userService;
}
[HttpGet("~/signin")]
public async Task<IActionResult> SignIn() => View("SignIn", await HttpContext.GetExternalProvidersAsync());
[HttpPost("~/signin")]
public async Task<IActionResult> SignIn([FromForm] string provider, string returnUrl)
{
if (string.IsNullOrWhiteSpace(provider))
{
return BadRequest();
}
if (!await HttpContext.IsProviderSupportedAsync(provider))
{
return BadRequest();
}
var redirectUrl = Url.Action(nameof(LoginCallback), "Account", new { returnUrl });
var properties = _signInService.ConfigureExternalAuthenticationProperties(provider, redirectUrl, null);
properties.Items.Add("XsrfKey", "Test");
return Challenge(properties, provider);
}
[HttpGet("~/signout")]
[HttpPost("~/signout")]
public IActionResult SignOutCurrentUser()
{
return SignOut(new AuthenticationProperties {RedirectUri = "/"},
CookieAuthenticationDefaults.AuthenticationScheme);
}
//[HttpGet("~/Account/LoginCallback")]
[HttpGet]
public async Task<IActionResult> LoginCallback(string returnUrl = null, string remoteError = null)
{
if (remoteError != null)
{
return RedirectToAction("Index", "Home");
}
var info = await _signInService.GetExternalLoginInfoAsync("Test");
if (info == null)
{
return RedirectToAction("Index", "Home");
}
var result = await _signInService.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);
if (result.Succeeded)
{
return RedirectToLocal(returnUrl);
}
if (result.IsLockedOut)
{
return RedirectToAction("Index", "Home");
}
else
{
// If the user does not have an account, then ask the user to create an account.
ViewData["ReturnUrl"] = returnUrl;
ViewData["LoginProvider"] = info.LoginProvider;
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
return RedirectToAction("Index", "Home");
}
}
private IActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction(nameof(HomeController.Index), "Home");
}
}
}
Run Code Online (Sandbox Code Playgroud)
发生的情况如下:
var info = await _signInService.GetExternalLoginInfoAsync("Test");该行始终为空。我一直在努力找出我在设置中忽略的内容,因为我没有任何错误。
首先...我们需要看一下 SignInManager.cs 中内部方法GetExternalLoginInfoAsync的实现,并记下所有可能导致返回 null 的条件。
我将在下面的代码中以评论的形式提供我的答案:
/// <summary>
/// Gets the external login information for the current login, as an asynchronous operation.
/// </summary>
/// <param name="expectedXsrf">Flag indication whether a Cross Site Request Forgery token was expected in the current request.</param>
/// <returns>The task object representing the asynchronous operation containing the <see name="ExternalLoginInfo"/>
/// for the sign-in attempt.</returns>
public virtual async Task<ExternalLoginInfo> GetExternalLoginInfoAsync(string expectedXsrf = null)
{
var auth = await Context.AuthenticateAsync(IdentityConstants.ExternalScheme);
var items = auth?.Properties?.Items;
if (auth?.Principal == null || items == null || !items.ContainsKey(LoginProviderKey))
{
// What cases can lead us here?
// * The authentication was unsuccessful maybe due to
// - Login cancellation
// - Project not running on a secured environment (https)
// - SignInScheme property of auth options not
// equal to IdentityConstants.ExternalScheme
return null;
}
if (expectedXsrf != null)
{
// It is important to note that XsrfKey is a constant
// declared above in this class whose value is "XsrfId".
if (!items.ContainsKey(XsrfKey))
{
// What cases can lead us here?
// * You passed an argument for expectedXsrf but
// the initialized key-value pairs does not contain
// any key for XsrfKey ("XsrfId").
// In your case the below is wrong:
// properties.Items.Add("XsrfKey", "Test"); <= remove
// Pass the value as 3rd parameter in
// "ConfigureExternalAuthenticationProperties" method call instead
// _signInService.ConfigureExternalAuthenticationProperties(provider, redirectUrl, "Test")
return null;
}
var userId = items[XsrfKey] as string;
if (userId != expectedXsrf)
{
// What cases can lead us here?
// * The argument passed for expectedXsrf does not
// match the value of initialized key-value pair
// for XsrfKey ("XsrfId").
// Ensure "Test" should go with "XsrfId" as key
// by passing the value as 3rd parameter in
// "ConfigureExternalAuthenticationProperties" method call instead.
return null;
}
}
var providerKey = auth.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
var provider = items[LoginProviderKey] as string;
if (providerKey == null || provider == null)
{
return null;
}
var providerDisplayName = (await GetExternalAuthenticationSchemesAsync()).FirstOrDefault(p => p.Name == provider)?.DisplayName
?? provider;
return new ExternalLoginInfo(auth.Principal, provider, providerKey, providerDisplayName)
{
AuthenticationTokens = auth.Properties.GetTokens()
};
}
Run Code Online (Sandbox Code Playgroud)
因此,从代码审查来看,这些是 null 的一些可能原因:
身份验证不成功可能是由于
取消登录
项目未在安全环境中运行 (https)
StartUp.cs 或 appsettings.json 下的身份验证选项的 SignInScheme 属性不等于 IdentityConstants.ExternalScheme
您传递了 ExpectedXsrf 的参数,但initialized key-value pair不包含 的任何键XsrfKey ("XsrfId")。
在你的情况下,以下是错误的:
property.Items.Add("XsrfKey", "测试"); <= 删除此行,因为“XsrfKey”未知。
相反,您可以在“ConfigureExternalAuthenticationProperties”方法调用中将值作为第三个参数传递:
_signInService.ConfigureExternalAuthenticationProperties(provider, redirectUrl, "Test");
| 归档时间: |
|
| 查看次数: |
1927 次 |
| 最近记录: |