I'm trying to figure out how to setup a login via Discord Oauth2 while using Dapper as my ORM.
Microsoft has a guide here that I have followed to setup all of my stores. I infact can call CreateAsync() method and a user gets created in my database, so I believe that side of things is completely setup.
My issues lie within external login. Below you will find what I have tried.
Program.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();
Here is the Account Controller Code:
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");
}
}
}
Here is what happens:
- I click on the Login via discord button.
- I am taken to Discord Website
- I login via the discord website
- I am redirected back to my website
- info is never retrieved.
var info = await _signInService.GetExternalLoginInfoAsync("Test");that line is always null.
I've been struggling to figure out what I have overlooked in my setup as I don't have any errors about anything.