feat(security): /auth/login, /auth/ping, /auth/token endpoints
This commit is contained in:
@@ -0,0 +1,83 @@
|
|||||||
|
using System.Security.Claims;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using ZB.MOM.WW.OtOpcUa.Security.Jwt;
|
||||||
|
using ZB.MOM.WW.OtOpcUa.Security.Ldap;
|
||||||
|
|
||||||
|
namespace ZB.MOM.WW.OtOpcUa.Security.Endpoints;
|
||||||
|
|
||||||
|
public static class AuthEndpoints
|
||||||
|
{
|
||||||
|
public sealed record LoginRequest(string Username, string Password);
|
||||||
|
|
||||||
|
public sealed record TokenResponse(string Token);
|
||||||
|
|
||||||
|
public static IEndpointRouteBuilder MapOtOpcUaAuth(this IEndpointRouteBuilder app)
|
||||||
|
{
|
||||||
|
app.MapPost("/auth/login", (Delegate)LoginAsync).AllowAnonymous();
|
||||||
|
app.MapGet("/auth/ping", (Delegate)Ping).AllowAnonymous();
|
||||||
|
app.MapPost("/auth/token", (Delegate)IssueToken).RequireAuthorization();
|
||||||
|
app.MapPost("/auth/logout", (Delegate)LogoutAsync).RequireAuthorization();
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> LoginAsync(
|
||||||
|
LoginRequest request,
|
||||||
|
HttpContext http,
|
||||||
|
ILdapAuthService ldap,
|
||||||
|
CancellationToken ct)
|
||||||
|
{
|
||||||
|
LdapAuthResult result;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = await ldap.AuthenticateAsync(request.Username, request.Password, ct);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.Success)
|
||||||
|
return Results.Unauthorized();
|
||||||
|
|
||||||
|
var claims = new List<Claim>
|
||||||
|
{
|
||||||
|
new(ClaimTypes.NameIdentifier, result.Username ?? request.Username),
|
||||||
|
new(JwtTokenService.UsernameClaimType, result.Username ?? request.Username),
|
||||||
|
new(JwtTokenService.DisplayNameClaimType, result.DisplayName ?? request.Username),
|
||||||
|
};
|
||||||
|
foreach (var role in result.Roles)
|
||||||
|
claims.Add(new Claim(ClaimTypes.Role, role));
|
||||||
|
|
||||||
|
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
|
||||||
|
var principal = new ClaimsPrincipal(identity);
|
||||||
|
|
||||||
|
await http.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
|
||||||
|
return Results.NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IResult Ping(HttpContext http) =>
|
||||||
|
http.User.Identity?.IsAuthenticated == true ? Results.Ok() : Results.Unauthorized();
|
||||||
|
|
||||||
|
private static IResult IssueToken(HttpContext http, JwtTokenService jwt)
|
||||||
|
{
|
||||||
|
var user = http.User;
|
||||||
|
var username = user.FindFirst(JwtTokenService.UsernameClaimType)?.Value
|
||||||
|
?? user.Identity?.Name
|
||||||
|
?? string.Empty;
|
||||||
|
var displayName = user.FindFirst(JwtTokenService.DisplayNameClaimType)?.Value ?? username;
|
||||||
|
var roles = user.FindAll(ClaimTypes.Role).Select(c => c.Value).ToArray();
|
||||||
|
|
||||||
|
return Results.Ok(new TokenResponse(jwt.Issue(displayName, username, roles)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> LogoutAsync(HttpContext http)
|
||||||
|
{
|
||||||
|
await http.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||||
|
return Results.NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user