添加OAuth2认证相关配置文件和服务实现,包括环境变量配置、PKCE流程支持、token管理等功能。主要变更: - 新增OAuth2配置文件 - 实现OAuth2服务层 - 更新请求拦截器支持token自动刷新 - 修改认证API和store以支持OAuth2流程
131 lines
5.2 KiB
C#
131 lines
5.2 KiB
C#
using Fengling.AuthService.Data;
|
||
using Fengling.AuthService.Models;
|
||
using Microsoft.AspNetCore;
|
||
using Microsoft.AspNetCore.Authentication;
|
||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||
using Microsoft.AspNetCore.Identity;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using OpenIddict.Abstractions;
|
||
using OpenIddict.Server.AspNetCore;
|
||
using static OpenIddict.Abstractions.OpenIddictConstants;
|
||
using System.Linq;
|
||
|
||
namespace Fengling.AuthService.Controllers;
|
||
|
||
[ApiController]
|
||
[Route("connect")]
|
||
public class LogoutController : ControllerBase
|
||
{
|
||
private readonly IOpenIddictApplicationManager _applicationManager;
|
||
private readonly IOpenIddictAuthorizationManager _authorizationManager;
|
||
private readonly UserManager<ApplicationUser> _userManager;
|
||
private readonly SignInManager<ApplicationUser> _signInManager;
|
||
private readonly ILogger<LogoutController> _logger;
|
||
|
||
public LogoutController(
|
||
IOpenIddictApplicationManager applicationManager,
|
||
IOpenIddictAuthorizationManager authorizationManager,
|
||
UserManager<ApplicationUser> userManager,
|
||
SignInManager<ApplicationUser> signInManager,
|
||
ILogger<LogoutController> logger)
|
||
{
|
||
_applicationManager = applicationManager;
|
||
_authorizationManager = authorizationManager;
|
||
_userManager = userManager;
|
||
_signInManager = signInManager;
|
||
_logger = logger;
|
||
}
|
||
|
||
[HttpGet("endsession")]
|
||
[HttpPost("endsession")]
|
||
[IgnoreAntiforgeryToken]
|
||
public async Task<IActionResult> EndSession()
|
||
{
|
||
var request = HttpContext.GetOpenIddictServerRequest() ??
|
||
throw new InvalidOperationException("OpenIddict request is null");
|
||
|
||
// 标准做法:先尝试从 id_token_hint 中提取客户端信息
|
||
string? clientId = request.ClientId;
|
||
|
||
if (string.IsNullOrEmpty(clientId) && !string.IsNullOrEmpty(request.IdTokenHint))
|
||
{
|
||
try
|
||
{
|
||
// 从 id_token_hint 中提取 client_id (azp claim 或 aud claim)
|
||
var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal;
|
||
if (principal != null)
|
||
{
|
||
// 尝试从 azp (authorized party) claim 获取
|
||
clientId = principal.GetClaim(Claims.AuthorizedParty);
|
||
|
||
// 如果没有 azp,尝试从 aud (audience) claim 获取
|
||
if (string.IsNullOrEmpty(clientId))
|
||
{
|
||
clientId = principal.GetClaim(Claims.Audience);
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogWarning(ex, "Failed to extract client_id from id_token_hint");
|
||
}
|
||
}
|
||
|
||
// 执行登出
|
||
var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme);
|
||
if (result.Succeeded)
|
||
{
|
||
await _signInManager.SignOutAsync();
|
||
}
|
||
|
||
// 处理 post_logout_redirect_uri
|
||
if (!string.IsNullOrEmpty(clientId))
|
||
{
|
||
var application = await _applicationManager.FindByClientIdAsync(clientId);
|
||
if (application != null)
|
||
{
|
||
var registeredUris = await _applicationManager.GetPostLogoutRedirectUrisAsync(application);
|
||
|
||
// 如果提供了 post_logout_redirect_uri,验证它是否在注册的 URI 列表中
|
||
if (!string.IsNullOrEmpty(request.PostLogoutRedirectUri))
|
||
{
|
||
if (registeredUris.Contains(request.PostLogoutRedirectUri))
|
||
{
|
||
// 如果提供了 state,需要附加到重定向 URI
|
||
var redirectUri = request.PostLogoutRedirectUri;
|
||
if (!string.IsNullOrEmpty(request.State))
|
||
{
|
||
var separator = redirectUri.Contains('?') ? "&" : "?";
|
||
redirectUri = $"{redirectUri}{separator}state={Uri.EscapeDataString(request.State)}";
|
||
}
|
||
|
||
return Redirect(redirectUri);
|
||
}
|
||
else
|
||
{
|
||
_logger.LogWarning(
|
||
"Post-logout redirect URI {Uri} is not registered for client {ClientId}",
|
||
request.PostLogoutRedirectUri,
|
||
clientId);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 如果没有提供 post_logout_redirect_uri,使用第一个注册的 URI
|
||
var defaultUri = registeredUris.FirstOrDefault();
|
||
if (!string.IsNullOrEmpty(defaultUri))
|
||
{
|
||
_logger.LogInformation(
|
||
"Using default post-logout redirect URI for client {ClientId}",
|
||
clientId);
|
||
return Redirect(defaultUri);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果无法确定重定向地址,返回默认页面
|
||
return Redirect("/");
|
||
}
|
||
}
|