Dear,
I have problem with external login, I had follow tutorial from Microsoft Learn, but for some reason when I need to post ExternalLoginConfirmation , Principal are null.
But I had pass the login on the . google
Here is my code.
Program.cs
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OAuth.Claims;
using Microsoft.AspNetCore.Identity;
using Milking.Infrastructure;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddInfrastructure(builder.Configuration);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddAuthentication()
.AddGoogle(options =>
{
IConfigurationSection googleAuthSection = builder.Configuration.GetSection("Authentication:Google");
options.ClientId = googleAuthSection["ClientId"];
options.ClientSecret = googleAuthSection["ClientSecret"];
})
.AddMicrosoftAccount(options =>
{
IConfigurationSection microsoftAuthSection = builder.Configuration.GetSection("Authentication:Microsoft");
options.ClientId = microsoftAuthSection["ClientId"];
options.ClientSecret = microsoftAuthSection["ClientSecret"];
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
AuthController.cs
using System.Security.Claims;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Milking.WebMVC.Models;
namespace Milking.WebMVC.Controllers;
public class AuthController : Controller
{
private readonly SignInManager<IdentityUser> _signInManager;
private readonly UserManager<IdentityUser> _userManager;
public AuthController(SignInManager<IdentityUser> signInManager, UserManager<IdentityUser> userManager)
{
_signInManager = signInManager;
_userManager = userManager;
}
[HttpGet]
public IActionResult Login()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult ExternalLogin(string provider)
{
var redirectUrl = Url.Action(nameof(ExternalLoginCallback), "Auth");
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return Challenge(properties, provider);
}
[HttpGet]
public async Task<IActionResult> ExternalLoginCallback()
{
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info is null)
{
//return RedirectToAction(nameof(Login));
return BadRequest();
}
var signInResult = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: false);
if (signInResult.Succeeded)
{
return RedirectToAction(nameof(Login));
}
ViewData["Provider"] = info.LoginProvider;
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
return View("ExternalLogin", new ExternalLoginModel { Email = email });
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ExternalLoginConfirmation(ExternalLoginModel model)
{
if (!ModelState.IsValid)
return View(model);
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
return Content("system has error");
var user = await _userManager.FindByEmailAsync(model.Email);
IdentityResult result = new();
if (user != null)
{
var logins = await _userManager.GetLoginsAsync(user);
if (!logins.Any())
{
result = await _userManager.AddLoginAsync(user, info);
if (!result.Succeeded)
{
ModelState.TryAddModelError(string.Empty, result.Errors.Select(_ => _.Description).FirstOrDefault());
return View(nameof(ExternalLogin), model);
}
}
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
model.Principal = info.Principal;
user = new(model.Email)
{
Email = model.Email
};
result = await _userManager.CreateAsync(user);
if (result.Succeeded)
{
result = await _userManager.AddLoginAsync(user, info);
if (result.Succeeded)
{
//TODO: Send an email for the email confirmation and add a default role as in the Register action
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
}
}
foreach (var error in result.Errors)
{
ModelState.TryAddModelError(error.Code, error.Description);
}
return View(nameof(ExternalLogin), model);
}
}
And from another layer which is calling AddInfrastructure
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Milking.Infrastructure.Data;
namespace Milking.Infrastructure;
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<IdentityAppContext>(options =>
{
options.UseSqlServer(configuration.GetConnectionString("IdentityConnection"));
});
services.AddIdentity<IdentityUser, IdentityRole>(options =>
{
options.SignIn.RequireConfirmedEmail = true;
})
.AddEntityFrameworkStores<IdentityAppContext>()
.AddDefaultTokenProviders()
.AddErrorDescriber<IdentityErrorDescriber>();
return services;
}
}
Error picture: