WIP
This commit is contained in:
1 parent
2cf6128a85
commit
45a5e3fc5e
11 files changed
+428
-40
No files matched your search
@@ -1,3 +1,4 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using BudgetApp.Components;
|
||||
@@ -34,6 +35,47 @@ builder.Services.ConfigureApplicationCookie(options =>
|
||||
options.LogoutPath = "/logout";
|
||||
});
|
||||
|
||||
// External authentication providers (only registered when configured)
|
||||
var authBuilder = builder.Services.AddAuthentication();
|
||||
|
||||
var googleSection = builder.Configuration.GetSection("Authentication:Google");
|
||||
if (!string.IsNullOrEmpty(googleSection["ClientId"]) && !string.IsNullOrEmpty(googleSection["ClientSecret"]))
|
||||
{
|
||||
authBuilder.AddGoogle(options =>
|
||||
{
|
||||
options.ClientId = googleSection["ClientId"]!;
|
||||
options.ClientSecret = googleSection["ClientSecret"]!;
|
||||
});
|
||||
}
|
||||
|
||||
var microsoftSection = builder.Configuration.GetSection("Authentication:Microsoft");
|
||||
if (!string.IsNullOrEmpty(microsoftSection["ClientId"]) && !string.IsNullOrEmpty(microsoftSection["ClientSecret"]))
|
||||
{
|
||||
authBuilder.AddMicrosoftAccount(options =>
|
||||
{
|
||||
options.ClientId = microsoftSection["ClientId"]!;
|
||||
options.ClientSecret = microsoftSection["ClientSecret"]!;
|
||||
});
|
||||
}
|
||||
|
||||
var githubSection = builder.Configuration.GetSection("Authentication:GitHub");
|
||||
if (!string.IsNullOrEmpty(githubSection["ClientId"]) && !string.IsNullOrEmpty(githubSection["ClientSecret"]))
|
||||
{
|
||||
authBuilder.AddOAuth("GitHub", "GitHub", options =>
|
||||
{
|
||||
options.ClientId = githubSection["ClientId"]!;
|
||||
options.ClientSecret = githubSection["ClientSecret"]!;
|
||||
options.AuthorizationEndpoint = "https://github.com/login/oauth/authorize";
|
||||
options.TokenEndpoint = "https://github.com/login/oauth/access_token";
|
||||
options.UserInformationEndpoint = "https://api.github.com/user";
|
||||
options.CallbackPath = "/signin-github";
|
||||
options.Scope.Add("user:email");
|
||||
options.ClaimActions.MapJsonKey(System.Security.Claims.ClaimTypes.NameIdentifier, "id");
|
||||
options.ClaimActions.MapJsonKey(System.Security.Claims.ClaimTypes.Name, "login");
|
||||
options.ClaimActions.MapJsonKey(System.Security.Claims.ClaimTypes.Email, "email");
|
||||
});
|
||||
}
|
||||
|
||||
// Application services
|
||||
builder.Services.AddScoped<AccountService>();
|
||||
builder.Services.AddScoped<TransactionService>();
|
||||
@@ -75,4 +117,62 @@ app.MapStaticAssets();
|
||||
app.MapRazorComponents<App>()
|
||||
.AddInteractiveServerRenderMode();
|
||||
|
||||
// External authentication endpoints
|
||||
app.MapPost("/api/auth/external-login", (HttpContext context) =>
|
||||
{
|
||||
var provider = context.Request.Form["provider"].ToString();
|
||||
var returnUrl = context.Request.Form["returnUrl"].ToString();
|
||||
if (string.IsNullOrEmpty(returnUrl)) returnUrl = "/";
|
||||
|
||||
var redirectUrl = $"/api/auth/external-callback?returnUrl={Uri.EscapeDataString(returnUrl)}";
|
||||
var properties = new Microsoft.AspNetCore.Authentication.AuthenticationProperties { RedirectUri = redirectUrl };
|
||||
return Results.Challenge(properties, [provider]);
|
||||
});
|
||||
|
||||
app.MapGet("/api/auth/external-callback", async (
|
||||
HttpContext context,
|
||||
SignInManager<ApplicationUser> signInManager,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
string? returnUrl) =>
|
||||
{
|
||||
returnUrl ??= "/";
|
||||
var info = await signInManager.GetExternalLoginInfoAsync();
|
||||
if (info is null)
|
||||
return Results.Redirect("/login");
|
||||
|
||||
var signInResult = await signInManager.ExternalLoginSignInAsync(
|
||||
info.LoginProvider, info.ProviderKey, isPersistent: false);
|
||||
|
||||
if (signInResult.Succeeded)
|
||||
return Results.Redirect(returnUrl);
|
||||
|
||||
// Auto-register: create user from external claims
|
||||
var email = info.Principal.FindFirst(System.Security.Claims.ClaimTypes.Email)?.Value;
|
||||
var userName = email ?? info.Principal.FindFirst(System.Security.Claims.ClaimTypes.Name)?.Value
|
||||
?? $"{info.LoginProvider}_{info.ProviderKey}";
|
||||
|
||||
var user = new ApplicationUser { UserName = userName, Email = email };
|
||||
var createResult = await userManager.CreateAsync(user);
|
||||
if (createResult.Succeeded)
|
||||
{
|
||||
await userManager.AddLoginAsync(user, info);
|
||||
await signInManager.SignInAsync(user, isPersistent: false);
|
||||
return Results.Redirect(returnUrl);
|
||||
}
|
||||
|
||||
// If the email is already taken, try linking
|
||||
if (email is not null)
|
||||
{
|
||||
var existingUser = await userManager.FindByEmailAsync(email);
|
||||
if (existingUser is not null)
|
||||
{
|
||||
await userManager.AddLoginAsync(existingUser, info);
|
||||
await signInManager.SignInAsync(existingUser, isPersistent: false);
|
||||
return Results.Redirect(returnUrl);
|
||||
}
|
||||
}
|
||||
|
||||
return Results.Redirect("/login");
|
||||
});
|
||||
|
||||
app.Run();
|
||||
Reference in new issue
Block a user