У меня есть установка и приложение Angular, которое использует as pnet core + Identity Server 4 + ASP. NET Identity backend для аутентификации пользователей. Я использую npm package angular -auth-oid c -client для взаимодействия между моим приложением angular и основным приложением pnet и позволяю пользователям проходить аутентификацию с помощью Auth Code Flow + PKCE.
Процесс аутентификации отлично работает на моей Windows + Chrome установке, как локально, так и при развертывании в Google App Engine. Однако при тестировании на устройствах IOS и использовании chrome / safari процесс входа в систему не работает. Я подозреваю, что это может быть связано с тем же атрибутом Cook ie, который обсуждается здесь https://www.thinktecture.com/en/identity/samesite/prepare-your-identityserver/, однако я реализовал это исправление, но все же пользователи Safari / IOS не могут войти в систему.
Пользователь начинает процесс аутентификации, и сервер идентификации переходит на мою пользовательскую страницу входа в систему, которая является частью приложения angular. Затем они входят в систему, используя форму, и в случае успеха (с использованием SignInManager.PasswordSignInAsyn c) ReturnUrl возвращается клиенту, где я использую document.location.href = res.returnUrl
для перенаправления пользователя и продолжения процесса входа в систему. Я считаю, что это шаг, который не удается, возможно, потому, что файлы cookie не отправляются с запросом на сервер идентификации? Именно в этот момент страница входа в систему просто обновляет sh для пользователей safari / IOS. Стоит отметить, что мой сервер идентификации и приложения angular находятся в разных поддоменах.
Я включил ниже файл startup.cs с моего сервера идентификации
public class Startup
{
private IWebHostEnvironment Environment { get; }
private IConfiguration Configuration { get; }
public Startup(IWebHostEnvironment environment, IConfiguration configuration)
{
Environment = environment;
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddDbContext<MyDbContext>(options =>
options.UseMySql(Configuration.GetConnectionString("MyDb")));
services.AddIdentity<MyUser, MyRole>(options => { options.User.RequireUniqueEmail = true; })
.AddEntityFrameworkStores<MyDbContext>()
.AddDefaultTokenProviders();
var identityServerBuilder = services.AddIdentityServer(options =>
{
options.UserInteraction.LoginUrl = $"{Config.FrontendUrl(Configuration)}/login";
options.UserInteraction.ErrorUrl = $"{Config.FrontendUrl(Configuration)}/error";
options.UserInteraction.LogoutUrl = $"{Config.FrontendUrl(Configuration)}/logout";
})
.AddAspNetIdentity<MyUser>()
.AddInMemoryApiScopes(Config.ApisScopes)
.AddInMemoryApiResources(Config.Apis)
.AddInMemoryIdentityResources(Config.Ids)
.AddInMemoryClients(Config.Clients(Configuration))
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseMySql(Configuration.GetConnectionString("MyIdServerStore"),
sql => sql.MigrationsAssembly(migrationsAssembly));
});
if (Environment.IsProduction())
{
identityServerBuilder
.AddSigningCredential({omitted});
}
else
{
identityServerBuilder
.AddDeveloperSigningCredential();
}
services.Configure<DataProtectionTokenProviderOptions>(opt => opt.TokenLifespan = TimeSpan.FromHours(1));
services.AddControllers();
services.AddCustomCors(Environment, Configuration);
services.Configure<SendGridOptions>(Configuration.GetSection("SendGridOptions"));
services.AddTransient<IRegistrationService, RegistrationService>();
services.AddTransient<IRoutingService, RoutingService>();
services.AddTransient<IEmailSender, EmailSender>();
services.AddTransient<IEmailContentService, EmailContentService>();
services.AddTransient<IAccountService, AccountService>();
services.AddTransient<IReturnUrlParser, Services.ReturnUrlParser>();
services.AddTransient<IUserClaimsPrincipalFactory<SmbUser>, ClaimsFactory>();
services.ConfigureNonBreakingSameSiteCookies();
}
public void Configure(IApplicationBuilder app)
{
if (Environment.IsProduction())
{
var forwardOptions = new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto,
RequireHeaderSymmetry = false
};
forwardOptions.KnownNetworks.Clear();
forwardOptions.KnownProxies.Clear();
app.UseForwardedHeaders(forwardOptions);
}
app.UseExceptionHandler("/error");
app.UseSerilogRequestLogging();
app.UseHsts();
app.UseHttpsRedirection();
app.UseCookiePolicy();
app.UseIdentityServer();
app.UseCustomCors();
app.UseRouting();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
}
My Angular Код для входа в приложение приведен ниже:
of(this.returnUrl)
.pipe(
take(1),
switchMap((url) =>
this.authenticationService.logMeIn$({
username: this.loginForm.controls['username'].value,
password: this.loginForm.controls['password'].value,
returnUrl: url,
} as LoginRequest)
)
)
.subscribe(
(res) => {
this.success = res.success;
this.loading = false;
this.completed = true;
if (res && res.success) {
document.location.href = res.returnUrl;
}
},
() => {
this.success = false;
this.loading = false;
this.completed = true;
}
);
Мой повар ie конфигурация:
public static IServiceCollection ConfigureNonBreakingSameSiteCookies(this IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
options.OnAppendCookie = cookieContext =>
CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
options.OnDeleteCookie = cookieContext =>
CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
});
return services;
}
private static void CheckSameSite(HttpContext httpContext, CookieOptions options)
{
if (options.SameSite == SameSiteMode.None)
{
var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
if (DisallowsSameSiteNone(userAgent))
{
options.SameSite = SameSiteMode.Unspecified;
}
}
}
private static bool DisallowsSameSiteNone(string userAgent)
{
if (userAgent.Contains("CPU iPhone OS 12")
|| userAgent.Contains("iPad; CPU OS 12"))
{
return true;
}
if (userAgent.Contains("Safari")
&& userAgent.Contains("Macintosh; Intel Mac OS X 10_14")
&& userAgent.Contains("Version/"))
{
return true;
}
if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6"))
{
return true;
}
return false;
}
}
Может ли кто-нибудь увидеть что-то неправильное в моей настройке, из-за которого происходит сбой входа в IOS / Safari ? При необходимости с радостью предоставим более подробную информацию