- App.net Core 8 MVC приложение, настроенное на действие чисто как API (возвращает JSON, без традиционных представлений)
- Frontend: React Running on http: // localhost: 3000
- Аутентификация учетной записи Microsoft, настроенная через AddMicrosoftAccount
- идентификация основной идентификации пользователей
Код: Выделить всё
Program.csКод: Выделить всё
using AppBdsAPI.Data;
using AppBdsAPI.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
var builder = WebApplication.CreateBuilder(args);
// Configurar logging para depuração
builder.Services.AddLogging(logging =>
{
logging.AddConsole();
logging.AddDebug();
logging.SetMinimumLevel(LogLevel.Debug);
});
// Configurar a string de conexão
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
// Configurar o DbContext
builder.Services.AddDbContext(options =>
options.UseSqlServer(connectionString));
// Configurar o Identity com sua classe User personalizada
builder.Services.AddIdentity(options =>
{
options.SignIn.RequireConfirmedAccount = true;
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequireUppercase = true;
options.Password.RequireLowercase = true;
})
.AddEntityFrameworkStores()
.AddDefaultTokenProviders()
.AddDefaultUI(); // Kept this as it's an MVC project, even if acting as API
// ====================================================================================
// COOKIE CONFIGURATION FOR EXTERNAL SCHEME (Attempt to fix SameSite issues)
// ====================================================================================
builder.Services.Configure(IdentityConstants.ExternalScheme, options =>
{
options.Cookie.SameSite = SameSiteMode.None;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // Requires HTTPS
});
// ====================================================================================
// Configurar CORS
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowFrontend", policy =>
{
policy.WithOrigins(
"https://localhost:3000"
)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
// ====================================================================================
// MICROSOFT AUTHENTICATION CONFIGURATION
// ====================================================================================
builder.Services.AddAuthentication().AddMicrosoftAccount(microsoftOptions =>
{
microsoftOptions.ClientId = builder.Configuration["Authentication:Microsoft:ClientId"];
microsoftOptions.ClientSecret = builder.Configuration["Authentication:Microsoft:ClientSecret"];
microsoftOptions.SignInScheme = IdentityConstants.ExternalScheme; // Explicitly setting SignInScheme
microsoftOptions.CallbackPath = "/auth/callback"; // Custom callback path
});
// ====================================================================================
// ====================================================================================
// RAZOR PAGES SERVICES (Kept as it's an MVC project)
// ====================================================================================
builder.Services.AddRazorPages();
// ====================================================================================
builder.Services.AddControllers();
builder.Services.AddControllersWithViews();
var app = builder.Build();
// ====================================================================================
// HOST HEADER OVERRIDE MIDDLEWARE (Currently commented out for production, not active for dev)
// ====================================================================================
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
else // Production
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
/*
// This block is for production deployment with custom domains, not active locally.
app.Use((context, next) =>
{
context.Request.Host = new HostString("api.suaaplicacao.com");
context.Request.Scheme = "https";
return next();
});
*/
}
// ====================================================================================
// Habilitar middlewares na ordem correta
app.UseHttpsRedirection(); // Ensures HTTPS
app.UseStaticFiles();
app.UseCors("AllowFrontend");
app.UseRouting();
app.UseAuthentication(); // IMPORTANT: Before UseAuthorization
app.UseAuthorization();
app.MapControllers();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages(); // IMPORTANT: If AddRazorPages() is used
// Add error endpoint for debugging
app.MapGet("/error", (string message) => Results.BadRequest(new { error = "OAuth error", message }));
app.Run();
< /code>
AuthController.csКод: Выделить всё
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using AppBdsAPI.Models;
using System.Security.Claims;
using Microsoft.EntityFrameworkCore;
[Route("auth")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly UserManager _userManager;
public AuthController(UserManager userManager)
{
_userManager = userManager;
}
// Endpoint to initiate Microsoft login
[HttpGet("login")]
public IActionResult Login()
{
var properties = new AuthenticationProperties
{
// Internal redirect URI after successful external authentication
RedirectUri = "/auth/callback"
};
return Challenge(properties, "Microsoft");
}
// Callback endpoint after external authentication
[HttpGet("callback")]
public async Task Callback()
{
// Authenticate using the external scheme
var result = await HttpContext.AuthenticateAsync(IdentityConstants.ExternalScheme);
if (result?.Principal == null)
{
var errorMessage = result?.Failure?.Message ?? "Authentication failed.";
Console.WriteLine($"Authentication Error: {errorMessage}");
return BadRequest(new { error = errorMessage });
}
// Extract email from the principal
var email = result.Principal.FindFirst(ClaimTypes.Email)?.Value;
if (string.IsNullOrWhiteSpace(email))
{
Console.WriteLine("No email found in authentication result.");
return BadRequest(new { error = "Email not provided by Microsoft." });
}
// Validate email domain
if (!email.EndsWith("@ipt.pt"))
{
Console.WriteLine($"Invalid email domain: {email}");
return BadRequest(new { error = "Email must belong to @ipt.pt domain." });
}
// Find or create user
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
{
Console.WriteLine($"User not found for email: {email}");
return NotFound(new { error = "User not found." });
}
// Generate and save token
user.Token = Guid.NewGuid().ToString();
await _userManager.UpdateAsync(user);
var frontendRedirectUrl = $"http://localhost:3000/callback?token={user.Token}";
Console.WriteLine($"Redirecting to: {frontendRedirectUrl}");
return Redirect(frontendRedirectUrl);
}
// Logout endpoint
[HttpGet("logout")]
public async Task Logout(string returnUrl = "/")
{
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
return Redirect("http://localhost:3000/login");
}
}
< /code>
What I've already tried/checked:
[*]Azure AD Redirect URI: Confirmed that https://localhost:7253/auth/callback is precisely registered in my Azure AD App Registration (Application ID ad41e17a-362b-49db-82c8-dc7d5760efba). I've also tried /signin-microsoft with corresponding code changes, but the error persists.
[*]HTTPS: I am accessing the API via HTTPS (https://localhost:7253) throughout the entire login flow.
[*]Cookie SameSite Configuration: I've added builder.Services.Configure(IdentityConstants.ExternalScheme, ...)[*]
Код: Выделить всё
SignInScheme[*]
Код: Выделить всё
AddDefaultUI()[*] Кэш браузера/cookie: выполнил несколько полных кеша браузера и очистки cookie для localhost и microsoftonline.com и перезапустить браузер
restart restart. Применение после каждого изменения кода < /p>
< /li>
< /ul>
Несмотря на эти шаги, состояние «Oauth не хватало или неверная» сохраняется ошибка ». Мне интересно, есть ли у меня тонкое взаимодействие, или что redirecturi в аутентификации properties (
Код: Выделить всё
/auth/callbackКод: Выделить всё
/auth/callbackЛюбое понимание или предложения для дальнейшей отладки были бы очень оценены!
Подробнее здесь: https://stackoverflow.com/questions/796 ... eexception
Мобильная версия