Ошибка входа на сервер идентификации в Safari / IOS - PullRequest
0 голосов
/ 10 июля 2020

У меня есть установка и приложение 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 ? При необходимости с радостью предоставим более подробную информацию

...