[英]Authorize callback endpoint keeps redirecting to the user interaction login page IdentityServer
我正在開發一個 SPA 網絡應用程序,我正在使用 IdentityServer4 代碼流來處理授權。 所以我有以下組件:
https://localhost:5001
https://localhost:5001
http://localhost:8100
現在我正在嘗試對移動應用程序用戶進行身份驗證,但是用戶交互登錄屏幕不斷重定向,並且我正在獲取login_required
。
追溯電話,這就是我得到的:
/connect/authorize
端點- 檢查https://localhost:5001/auth/login?returnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fredirect_uri%3Dhttp%253A%252F%252Flocalhost%253A8100%252Fauth%252Fcallback%26client_id%3Dcharla-mobile%26response_type%3Dcode%26state%3Dpq1nokeuVj%26scope%3Dcharla-api%2520openid%2520profile%2520offline_access%26code_challenge%3DJxDVsm2YnMAbvOuemWWXjYLLt-Mi1TpHoO7zhDkCWSI%26code_challenge_method%3DS256
-檢查AccountController.cs
的login
端點- 檢查https://localhost:5001/auth/login?returnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fredirect_uri%3Dhttp%253A%252F%252Flocalhost%253A8100%252Fauth%252Fcallback%26client_id%3Dcharla-mobile%26response_type%3Dcode%26state%3Dpq1nokeuVj%26scope%3Dcharla-api%2520openid%2520profile%2520offline_access%26code_challenge%3DJxDVsm2YnMAbvOuemWWXjYLLt-Mi1TpHoO7zhDkCWSI%26code_challenge_method%3DS256
-檢查https://localhost:5001/auth/login?returnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fredirect_uri%3Dhttp%253A%252F%252Flocalhost%253A8100%252Fauth%252Fcallback%26client_id%3Dcharla-mobile%26response_type%3Dcode%26state%3Dpbx8alT61z%26scope%3Dcharla-api%2520openid%2520profile%2520offline_access%26code_challenge%3DrGappKbnVpUNzlNHst4t5RlHephWFfJTVXuwtpQ8tZI%26code_challenge_method%3DS256
-問題就在這里IdentityServer不能意義上說,用戶現在簽約在調試終端中,我不斷收到login_required
錯誤。所以這是我的設置:
啟動文件
namespace Charla
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityCore<ApplicationUser>(options => { });
new IdentityBuilder(typeof(ApplicationUser), typeof(IdentityRole), services)
.AddRoleManager<RoleManager<IdentityRole>>()
.AddSignInManager<SignInManager<ApplicationUser>>()
.AddEntityFrameworkStores<ConverseContext>();
/*services.AddIdentity<ApplicationUser, IdentityRole>()
.AddRoleManager<RoleManager<IdentityRole>>()
.AddSignInManager<SignInManager<ApplicationUser>>()
.AddEntityFrameworkStores<ConverseContext>()
.AddDefaultTokenProviders();*/
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
var builder = services
.AddIdentityServer(SetupIdentityServer)
.AddDeveloperSigningCredential()
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = b => b.UseMySql(Configuration.GetConnectionString("DefaultConnection"),
sqloptions => {
sqloptions.ServerVersion(new Version(10, 1, 37), ServerType.MariaDb); // replace with your Server Version and Type
sqloptions.MigrationsAssembly(migrationsAssembly);
});
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b => b.UseMySql(Configuration.GetConnectionString("DefaultConnection"),
sqloptions => {
sqloptions.ServerVersion(new Version(10, 1, 37), ServerType.MariaDb); // replace with your Server Version and Type
sqloptions.MigrationsAssembly(migrationsAssembly);
});
});
/*services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options => {
options.Authority = Configuration.GetValue<string>("IdentityServer:Jwt:Authority");
options.RequireHttpsMetadata = false;
options.ApiName = "charla-api";
});*/
services.AddAuthentication(opt => {
opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(jwt =>{
jwt.Authority = Configuration.GetValue<string>("IdentityServer:Jwt:Authority");
jwt.RequireHttpsMetadata = false;
jwt.TokenValidationParameters.ValidateAudience = false;
jwt.TokenValidationParameters.ValidTypes = new[] { "at+jwt" };
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
//endpoints.MapRazorPages();
endpoints.MapHub<ChatHub>("/hub");
});
}
private static void SetupIdentityServer(IdentityServerOptions options)
{
options.UserInteraction.LoginUrl = "/auth/login";
options.UserInteraction.LoginReturnUrlParameter = "returnUrl";
options.UserInteraction.LogoutUrl = "/logout";
options.UserInteraction.ErrorUrl= "/error/identity";
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
// see https://identityserver4.readthedocs.io/en/latest/topics/resources.html
//options.EmitStaticAudienceClaim = true;
//identityServerOptions.Authentication.CookieLifetime = TimeSpan.FromDays(1);
}
}
}
}
賬戶控制器.cs
[Route("api/[controller]")]
[ApiController]
[AllowAnonymous]
public class AccountController : ControllerBase
{
[HttpPost("login")]
public async Task<IActionResult> Login(UserResource model) {
var result = await signInManager.PasswordSignInAsync(model.email, model.password, isPersistent: true, lockoutOnFailure: false);
var context = await interaction.GetAuthorizationContextAsync(model.return_url);
if (result.Succeeded) {
var uo = db.Users.Include(q => q.UserOrganization).Single( q => q.Email == model.email ).UserOrganization.First();
uo.LastLogin = DateTime.UtcNow;
await db.SaveChangesAsync();
// let identity server know that we loggedin
await identityEvents.RaiseAsync(new UserLoginSuccessEvent(
model.email, uo.UserId, model.email, clientId: context?.Client.ClientId
));
//return Redirect(model.return_url);
return Ok( new{
email = model.email,
return_url = context.RedirectUri
} );
}
await identityEvents.RaiseAsync(new UserLoginFailureEvent(model.email, "invalid credentials", clientId:context?.Client.ClientId));
return NotFound(new {});
}
IdentityConfig.cs - 我正在使用 EF 表,但它是從下面播種的:
using IdentityServer4;
using IdentityServer4.Models;
using System.Collections.Generic;
namespace Charla
{
public static class IdentityConfig
{
public static IEnumerable<IdentityResource> IdentityResources =>
new IdentityResource[]
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("charla-api", "Charla API Resource")
};
}
public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{
new ApiScope("charla-api", "Charla API Scope")
};
public static IEnumerable<Client> Clients =>
new Client[]
{
// charla web app
new Client
{
ClientId = "charla-spa",
ClientName = "Charla Web App",
RequireClientSecret = false,
AllowOfflineAccess = true,
AllowAccessTokensViaBrowser = true,
AllowedGrantTypes = GrantTypes.Code,
RedirectUris = {
"https://localhost:5001/authentication/login-callback",
"https://app-dev.getcharla.com/authentication/login-callback",
"https://app.getcharla.com/authentication/login-callback"
},
//FrontChannelLogoutUri = "https://localhost:5001/authentication/logout-callback",
PostLogoutRedirectUris = {
"https://localhost:5001/authentication/logout-callback",
"https://app-dev.getcharla.com/authentication/logout-callback",
"https://app.getcharla.com/authentication/logout-callback"
},
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.OfflineAccess,
"charla-api"
}
},
// mobile client
new Client
{
ClientId = "charla-mobile",
ClientName = "Charla Mobile Apps",
RequireClientSecret = false,
AllowedGrantTypes = GrantTypes.Code,
AllowAccessTokensViaBrowser = true,
AllowOfflineAccess = true,
RedirectUris = {
"https://getcharla.com/ios_redirect",
"http://localhost:8100/auth/callback"
},
PostLogoutRedirectUris = {
"https://getcharla.com/ios_redirect_endsession",
"http://localhost:8100/auth/endsession"
},
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.OfflineAccess,
"charla-api"
},
AllowedCorsOrigins = { "http://localhost:8100", "https://localhost:5001", "http://localhost:5000" }
},
};
}
}
login.component.ts - 觸發對login
端點的調用並執行重定向的 Web 組件中的函數:
ngOnInit(): void {
this.route.queryParams.subscribe(params => {
this.returnUrl = params['returnUrl'] || '/';
});
}
/**
* Form Submit
*/
submit() {
const controls = this.loginForm.controls;
const authData = {
email: controls['email'].value,
password: controls['password'].value
};
this.auth
.login(authData.email, authData.password, this.returnUrl)
.pipe(
finalize(() => {
this.loading = false;
this.cdr.markForCheck();
})
)
.subscribe(
result => {
window.location = this.returnUrl;
this.router.navigateByUrl(this.returnUrl); // Main page
},
err => {
console.log(err);
if (( 'status' in err) && ( err.status === 404)){
this.authNoticeService.setNotice(this.translate.instant('AUTH.VALIDATION.INVALID_LOGIN'), 'danger');
}
}
);
}
不知道為什么它不起作用。 我讀到我應該在login
web api 端點中使用HttpContent.SignInAsync
,但我已經在使用var result = await signInManager.PasswordSignInAsync(model.email, model.password, isPersistent: true, lockoutOnFailure: false);
所以我認為這就足夠了。
我不確定我所做的其他選擇是否正確,例如使用AddIdentityCore
而不是AddIdentity
。 我應該添加AddAspNetIdentity<ApplicationUser>()
嗎?
通過跟蹤您提供的跟蹤,這可能是由於瀏覽器上有關 Cookie 的新規則而發生的。
在新的 ASP.NET Core 應用程序上,會自動添加samesite=none
屬性,但瀏覽器要求您也指定Secure
屬性,否則 Set-Cookie 將被阻止。
要配置您的 IdentityServer,請在Startup
類的Configure
方法中添加以下代碼塊:
app.UseCookiePolicy(new CookiePolicyOptions
{
HttpOnly = HttpOnlyPolicy.None,
MinimumSameSitePolicy = SameSiteMode.None,
Secure = CookieSecurePolicy.Always
});
使用安全屬性時必須使用HTTPS,否則會失敗
關於該主題的另一個 SO 問題: Session cookie set `SameSite=None; 安全;`不起作用
以及微軟關於 ASP.NET Core 的一些信息: https : //docs.microsoft.com/pt-br/aspnet/core/security/samesite?view=aspnetcore-5.0
在注銷並重新登錄時,我的 SPA 遇到了類似的問題。 該問題似乎是由 Visual Studio 更新引起的。 如果您使用命令提示符導航到項目文件夾並使用dotnet run
' 甚至更好的dotnet watch run
(以便您可以在dotnet watch run
更改 .net 核心代碼),您可以繞過該問題; 購買不通過 IIS 在 Visual Studio 中運行代碼。
如果這對您有效,請將 Properties Launch 更新為 Project(也使用 dotnet run),然后您也可以進行調試。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.