AuthenticateAsync всегда возвращает значение null в .Net 8C#

Место общения программистов C#
Anonymous
AuthenticateAsync всегда возвращает значение null в .Net 8

Сообщение Anonymous »

Если кто-нибудь может подсказать мне, где я ошибаюсь.  Я медленно обновлял свою кодовую базу с .Net6 до .Net 8.  С моим контроллером авторизации в методе авторизации с .Net 6 var results = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme) будет возвращать значение null или не удалось.  Затем я нажимал на страницу входа в систему Razor и входил в систему, и тот же результат был бы фактическим результатом AuthenticateResult.  При обновлении до .Net 8 он всегда возвращает значение null даже после входа в систему.  Я просмотрел открытые инструменты разработчика и увидел, что при входе в систему создается файл cookie. Я тестирую это в Postman, используя там вкладку «Авторизация» и используя все правильные параметры.
Вот что не изменилось с .Net 6 на .Net 8:Метод авторизации

Код: Выделить всё

[HttpGet(Name = nameof(Authorize))]
[HttpPost(Name = nameof(Authorize))]
[IgnoreAntiforgeryToken]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task Authorize()
{
var request = HttpContext.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");

// Retrieve the user principal stored in the authentication cookie.
// If it can't be extracted, redirect the user to the login page.
var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme);
if (result == null || !result.Succeeded)
{
if (request.HasPrompt(Prompts.None))
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.LoginRequired,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is not logged in."
}));
}

return Challenge(
authenticationSchemes: IdentityConstants.ApplicationScheme,
properties: new AuthenticationProperties
{
RedirectUri = Request.PathBase + Request.Path + QueryString.Create(
Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList())
});
}

if (request.HasPrompt(Prompts.Login))
{

var prompt = string.Join(" ", request.GetPrompts().Remove(Prompts.Login));

var parameters = Request.HasFormContentType ?
Request.Form.Where(parameter => parameter.Key != Parameters.Prompt).ToList() :
Request.Query.Where(parameter => parameter.Key != Parameters.Prompt).ToList();

parameters.Add(KeyValuePair.Create(Parameters.Prompt, new StringValues(prompt)));

return Challenge(
authenticationSchemes: IdentityConstants.ApplicationScheme,
properties: new AuthenticationProperties
{
RedirectUri = Request.PathBase + Request.Path + QueryString.Create(parameters)
});
}

if (request.MaxAge != null && result.Properties.IssuedUtc != null &&
DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))
{
if (request.HasPrompt(Prompts.None))
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.LoginRequired,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is not logged in."
}));
}

return Challenge(
authenticationSchemes: IdentityConstants.ApplicationScheme,
properties: new AuthenticationProperties
{
RedirectUri = Request.PathBase + Request.Path + QueryString.Create(
Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList())
});
}

var user = await _userManager.GetUserAsync(result.Principal) ??
throw new InvalidOperationException("The user details cannot be retrieved");

var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ??
throw new InvalidOperationException("Details concerning the calling client application cannot be found");

var permissionsArray = await _applicationManager.GetPermissionsAsync(application);
var scopes = permissionsArray.Where(a => a.StartsWith("scp:")).Select(a =>  a[4..]).ToList().ToImmutableArray();

var authorizations = await _authorizationManager.FindAsync(
subject: await _userManager.GetUserIdAsync(user),
client: await _applicationManager.GetIdAsync(application),
status: Statuses.Valid,
type: AuthorizationTypes.Permanent,
scopes: scopes).ToListAsync();

switch (await _applicationManager.GetConsentTypeAsync(application))
{

case ConsentTypes.External when !authorizations.Any():
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
"The logged in user is not allowed to access this client."
}));

case ConsentTypes.Implicit:
case ConsentTypes.External when authorizations.Any():
case ConsentTypes.Explicit when authorizations.Any() && !request.HasPrompt(Prompts.Consent):
var principal = await _signInManager.CreateUserPrincipalAsync(user);

principal.SetScopes(scopes);
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync());

var authorization = authorizations.LastOrDefault();
if (authorization == null)
{
authorization = await _authorizationManager.CreateAsync(
principal: principal,
subject: await _userManager.GetUserIdAsync(user),
client: await _applicationManager.GetIdAsync(application),
type: AuthorizationTypes.Permanent,
scopes: principal.GetScopes());
}

principal.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization));

foreach (var claim in principal.Claims)
{
claim.SetDestinations(GetDestinations(claim, principal));
}

return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);

case ConsentTypes.Explicit when request.HasPrompt(Prompts.None):
case ConsentTypes.Systematic when request.HasPrompt(Prompts.None):
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
"Interactive user consent is required."
}));

default:
return View(new AuthorizeViewModel
{
ApplicationName = await _applicationManager.GetDisplayNameAsync(application),
Scope = string.Join(" ", scopes)
});
}
}
Страница входа

Код: Выделить всё

[AllowAnonymous]
public class LoginModel : PageModel
{
private readonly ApplicationUserManager _userManager;
private readonly SignInManager _signInManager;
private readonly ILogger _logger;

public LoginModel(SignInManager signInManager,
ILogger logger,
ApplicationUserManager userManager)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
}

[BindProperty]
public InputModel Input { get; set; }

public IList ExternalLogins { get; set; }

public string ReturnUrl { get; set; }

[TempData]
public string ErrorMessage { get; set;  }

public class InputModel
{
//Snipped fro brevity
}

public async Task OnGetAsync(string returnUrl = null)
{
if (!string.IsNullOrEmpty(ErrorMessage))
{
ModelState.AddModelError(string.Empty, ErrorMessage);
}

returnUrl ??= Url.Content("~/");

// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);

ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();

ReturnUrl = returnUrl;
}

public async Task OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");

if (ModelState.IsValid)
{

var user = await _userManager.FindByNameAsync(Input.Email);
if (user != null)
{
if (!await _signInManager.CanSignInAsync(user) || (_userManager.SupportsUserLockout && await _userManager.IsLockedOutAsync(user)))
{
_logger.LogInformation("Login was unsuccessful through controller");
}

if (!await _userManager.CheckPasswordAsync(user, Input.Password))
{
_logger.LogInformation("Login was unsuccessful through controller");
await _userManager.AccessFailedAsync(user);
}

if (_userManager.SupportsUserLockout)
{
await _userManager.ResetAccessFailedCountAsync(user);
}
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true);
if (result.Succeeded)
{
_logger.LogInformation("User logged in.");

return LocalRedirect(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, Input.RememberMe });
}
if (result.IsLockedOut)
{
_logger.LogWarning("User account locked out.");
return RedirectToPage("./Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return Page();
}
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return Page();
}
}

return Page();
}
}
Вот что я изменил в .Net 6 на .Net 8, по сути, перейдя с start.cs на program.cs
.Net 6 (startup.cs):

Код: Выделить всё

public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("Policy", builder =>
{
builder.WithOrigins(Configuration.GetSection("Cors:Origins").GetChildren().Select(c =>  c.Value).ToArray())
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});

services.AddControllers();
services.AddRazorPages();

services.AddDbContext(options =>
{
options.UseSqlServer(
Configuration.GetConnectionString("IdentityDB"));

options.UseOpenIddict();
});

services.AddDataProtection()
.PersistKeysToDbContext()
.SetDefaultKeyLifetime(TimeSpan.FromDays(7))
.SetApplicationName("Applications");

services.AddIdentity()
.AddEntityFrameworkStores()
.AddUserStore()
.AddRoleStore()
.AddRoleManager()
.AddUserManager()
.AddErrorDescriber()
.AddDefaultTokenProviders()
.AddDefaultUI();

services.Configure(options =>
{
options.ClaimsIdentity.UserNameClaimType = Claims.Name;
options.ClaimsIdentity.UserIdClaimType = Claims.Subject;
options.ClaimsIdentity.RoleClaimType = Claims.Role;

// Configure the options for the Identity Account
options.SignIn.RequireConfirmedEmail = true;
options.SignIn.RequireConfirmedAccount = true;
options.User.RequireUniqueEmail = true;
options.Lockout.MaxFailedAccessAttempts = 3;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(10);
});

services.Configure(options =>
{
options.ValidationInterval = TimeSpan.FromSeconds(1);
});

services.AddQuartz(options =>
{
options.UseMicrosoftDependencyInjectionJobFactory();
options.UseSimpleTypeLoader();
options.UseInMemoryStore();
});

services.AddQuartzHostedService(options =>  options.WaitForJobsToComplete = true);

services.AddOpenIddict()
// Register the OpenIddict core components.
.AddCore(options =>
{
options.UseEntityFrameworkCore()
.UseDbContext();

options.UseQuartz();
})
// Register the OpenIddict server components.
.AddServer(options =>
{
options.SetAuthorizationEndpointUris("/api/Authorization/Authorize")
.SetTokenEndpointUris("/Token")
.SetIntrospectionEndpointUris("/Introspect")
.SetUserinfoEndpointUris("/api/Userinfo/Userinfo")
.SetVerificationEndpointUris("/Verify");

options.RegisterScopes(Scopes.OpenId, Scopes.Email, Scopes.Profile, Scopes.Roles);

options.UseReferenceAccessTokens()
.UseReferenceRefreshTokens()
.UseDataProtection();

options.AllowClientCredentialsFlow()
.AllowAuthorizationCodeFlow()
.RequireProofKeyForCodeExchange()
.AllowRefreshTokenFlow();

if (_env.IsDevelopment())
{
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
}
else if (_env.IsProduction() || _env.IsStaging())
{
options.AddSigningCertificate(Configuration.GetSection("CertifcateThumbprints:SigningCertificate").Value)
.AddEncryptionCertificate(Configuration.GetSection("CertifcateThumbprints:EncryptionCertificate").Value);
}

options.UseAspNetCore()
.EnableAuthorizationEndpointPassthrough()
.EnableTokenEndpointPassthrough()
.EnableStatusCodePagesIntegration()
.EnableUserinfoEndpointPassthrough()
.EnableVerificationEndpointPassthrough();
})
// Register the OpenIddict validation components.
.AddValidation(options =>
{
options.UseLocalServer();

options.UseDataProtection();

options.UseSystemNetHttp();

options.UseAspNetCore();
});

services.AddSwaggerGen(swagger =>
{
swagger.OperationFilter();
swagger.OperationFilter();
swagger.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());

// Set the comments path for the Swagger JSON and UI.
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
swagger.IncludeXmlComments(xmlPath);
});
services.AddApiVersioning();
services.AddVersionedApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVVV";
options.DefaultApiVersion = ApiVersion.Parse("0.10.alpha");
options.AssumeDefaultVersionWhenUnspecified = true;
});

services.AddTransient();
if (_env.IsDevelopment())
{
services.AddHostedService();
}
else if (_env.IsStaging() || _env.IsProduction())
{
services.AddHostedService();
}
}

public void Configure(IApplicationBuilder app, IApiVersionDescriptionProvider provider)
{
if (_env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseStatusCodePagesWithReExecute("/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.UseCors("Policy");

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseSwagger();

// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.DisplayOperationId();
var versionDescription = provider.ApiVersionDescriptions;
foreach (var description in provider.ApiVersionDescriptions.OrderByDescending(_ => _.ApiVersion))
{
c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", $"API {description.GroupName}");
}
});

app.UseSerilogRequestLogging();

app.UseRouting();

app.UseAuthentication();

app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
});
}
.Net 8 (program.cs)

Код: Выделить всё

builder.Services.AddCors(options =>
{
options.AddPolicy("Policy", opt =>
{
opt.WithOrigins(builder.Configuration.GetSection("Cors:Origins").GetChildren().Select(c => c.Value).ToArray()!)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});

builder.Services.AddControllersWithViews()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new EntityIdJsonConverterFactory());
});

builder.Services.AddRazorPages();

builder.Services.AddDbContext(options =>
{
options.UseSqlServer(
builder.Configuration.GetConnectionString("IdentityDB"));

options.UseOpenIddict();
});

builder.Services.AddDataProtection()
.PersistKeysToDbContext()
.SetDefaultKeyLifetime(TimeSpan.FromDays(7))
.SetApplicationName("Applications");

builder.Services.AddIdentity()
.AddEntityFrameworkStores()
.AddUserStore()
.AddRoleStore()
.AddRoleManager()
.AddUserManager()
.AddErrorDescriber()
.AddDefaultTokenProviders()
.AddDefaultUI();

builder.Services.Configure(options =>
{
options.ClaimsIdentity.UserNameClaimType = Claims.Name;
options.ClaimsIdentity.UserIdClaimType = Claims.Subject;
options.ClaimsIdentity.RoleClaimType = Claims.Role;

options.SignIn.RequireConfirmedEmail = true;
options.SignIn.RequireConfirmedAccount = true;
options.User.RequireUniqueEmail = true;
options.Lockout.MaxFailedAccessAttempts = 3;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(10);
});

builder.Services.Configure(options =>
{
options.ValidationInterval = TimeSpan.FromSeconds(1);
});

builder.Services.AddQuartz(options =>
{
options.UseSimpleTypeLoader();
options.UseInMemoryStore();
});

builder.Services.AddQuartzHostedService(options =>  options.WaitForJobsToComplete = true);

builder.Services.AddOpenIddict()
// Register the OpenIddict core components.
.AddCore(options =>
{
options.UseEntityFrameworkCore()
.UseDbContext();

options.UseQuartz();
})
// Register the OpenIddict server components.
.AddServer(options =>
{
options.SetAuthorizationEndpointUris("api/Authorization/Authorize")
.SetTokenEndpointUris("Token")
.SetIntrospectionEndpointUris("Introspect")
.SetUserinfoEndpointUris("api/Userinfo/Userinfo")
.SetVerificationEndpointUris("Verify");

options.RegisterScopes(Scopes.OpenId, Scopes.Email, Scopes.Profile, Scopes.Roles);

options.UseReferenceAccessTokens()
.UseReferenceRefreshTokens()
.UseDataProtection();

options.AllowClientCredentialsFlow()
.AllowAuthorizationCodeFlow()
.RequireProofKeyForCodeExchange()
.AllowRefreshTokenFlow();

if (builder.Environment.IsDevelopment())
{
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
}
else if (builder.Environment.IsProduction() || builder.Environment.IsStaging())
{
options.AddSigningCertificate(builder.Configuration.GetSection("CertifcateThumbprints:SigningCertificate").Value!)
.AddEncryptionCertificate(builder.Configuration.GetSection("CertifcateThumbprints:EncryptionCertificate").Value!);
}
options.UseAspNetCore()
.EnableAuthorizationEndpointPassthrough()
.EnableTokenEndpointPassthrough()
.EnableStatusCodePagesIntegration()
.EnableUserinfoEndpointPassthrough()
.EnableVerificationEndpointPassthrough();
})
// Register the OpenIddict validation components.
.AddValidation(options =>
{
options.UseLocalServer();

options.UseDataProtection();

options.UseSystemNetHttp();
options.UseAspNetCore();
});

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(swagger =>
{
swagger.OperationFilter();
swagger.OperationFilter();
swagger.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());

// Set the comments path for the Swagger JSON and UI.
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
swagger.IncludeXmlComments(xmlPath);
});

builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(0, 10, "alpha");
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
}).AddMvc()
.AddApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVVV";
options.SubstituteApiVersionInUrl = false;
});

builder.Services.AddApplication()
.AddInfrastructure(builder.Configuration)
.AddDataLibrary();

builder.Services.AddHttpClient("Login", sp => { sp.BaseAddress = new Uri(builder.Configuration.GetSection("BaseAddresses:Api").Value!);  });

builder.Services.AddTransient();
if (builder.Environment.IsDevelopment())
{
builder.Services.AddHostedService();
builder.Services.AddHostedService();
}
else if (builder.Environment.IsStaging() || builder.Environment.IsProduction())
{
builder.Services.AddHostedService();
}

builder.Host.UseWolverine(opts =>
{
opts.PersistMessagesWithSqlServer(builder.Configuration.GetConnectionString("IdentityDB")!);
opts.UseEntityFrameworkCoreTransactions();
opts.Policies.AutoApplyTransactions();
opts.Policies.UseDurableLocalQueues();
opts.Discovery.IncludeAssembly(Assembly.Load("IdentityApplication"));
opts.UseFluentValidation();

opts.Services.AddResourceSetupOnStartup();
opts.OnException().MoveToErrorQueue();
});

builder.Services.AddTransient();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment() || app.Environment.IsStaging())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
var provider = app.Services.GetRequiredService();
app.UseSwaggerUI(c =>
{
c.DisplayOperationId();
var versionDescription = provider.ApiVersionDescriptions;
foreach (var description in provider.ApiVersionDescriptions.OrderByDescending(_ => _.ApiVersion))
{
c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", $"API {description.GroupName}");
}
});
}
else
{
app.UseStatusCodePagesWithReExecute("/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.UseCors("Policy");

app.UseSerilogRequestLogging();

app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();

app.UseAuthorization();

app.UseMiddleware();

app.MapRazorPages();

app.MapControllers();

app.Run();
Итак, с минимальными изменениями, которые я внес, я не могу понять, почему результат всегда возвращает ноль. Файл cookie генерируется на странице входа в систему и отображается в программе и Postman.  Я понимаю, что AuthenticateAsync изменился в .Net 7, но не могу найти хорошей документации о том, что может быть затронуто в моем коде. Не знаю, в чем причина, но я застрял на этом уже больше недели.


Подробнее здесь: https://stackoverflow.com/questions/790 ... -all-times

Вернуться в «C#»