Aspnetcore Correlation не удалось, когда Facebook или Google логин и неверный токен при подтверждении электронной почты - PullRequest
0 голосов
/ 29 января 2019

Я сделал миграцию моего приложения на aspnetcore, и теперь у меня есть случайная проблема с токенами проверки.1. Проблема заключается в том, что случайным образом пользователи получают

При обработке удаленного входа произошла ошибка.Корреляция не удалась.

Проблема в том, что, если я пойду и протестирую сам, он будет работать.

Вторая проблема заключается в том, что когда пользователь получает токен подтверждения по электронной почте и щелкает ссылку из электронного письма, он будетполучить

неверный токен

, поэтому они не могут подтвердить электронную почту.

Сначала я подумал, что проблема с UseCookiePolicy, но я отключил его.

Startup.cs

namespace Flymark.Online.Web
{
    public class Startup
    {
        private readonly IHostingEnvironment _env;

        public Startup(IHostingEnvironment env)
        {
            _env = env;
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", true, true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }


        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Configure SnapshotCollector from application settings
            services.Configure<SnapshotCollectorConfiguration>(
                Configuration.GetSection(nameof(SnapshotCollectorConfiguration)));
            // Add SnapshotCollector telemetry processor.
            services.AddSingleton<ITelemetryProcessorFactory>(sp => new SnapshotCollectorTelemetryProcessorFactory(sp));
            services.AddApplicationInsightsTelemetryProcessor<TelemetryFilter>();
            services.AddSingleton<ITelemetryInitializer, AppInsightsInitializer>();
            services.AddCors();
            var decompressionOptions = new RequestDecompressionOptions();

            decompressionOptions.UseDefaults();
            services.AddRequestDecompression(decompressionOptions);

            FlymarkAppSettings.Init(Configuration, _env.EnvironmentName);

            var storageUri = new Uri(Configuration.GetValue<string>("Flymark:DataProtectionStorageUrl"));
            //Get a reference to a container to use for the sample code, and create it if it does not exist.
            var container = new CloudBlobClient(storageUri).GetContainerReference("data-protection");
            services.AddDataProtection()
                .SetApplicationName("Flymark.Online")
                .PersistKeysToAzureBlobStorage(container, "data-protection.xml");

            services.AddDetection();
            services.AddAutoMapper();

            services.AddWebMarkupMin(
                    options =>
                    {
                        options.AllowMinificationInDevelopmentEnvironment = true;
                        options.AllowCompressionInDevelopmentEnvironment = true;
                    })
                .AddHtmlMinification(o =>
                {
                    o.ExcludedPages = new List<IUrlMatcher>
                    {
                        new WildcardUrlMatcher("/scripts/*")
                    };
                    o.MinificationSettings.AttributeQuotesRemovalMode = HtmlAttributeQuotesRemovalMode.KeepQuotes;
                    o.MinificationSettings.EmptyTagRenderMode = HtmlEmptyTagRenderMode.NoSlash;
                    o.MinificationSettings.RemoveOptionalEndTags = false;
                })
                .AddXmlMinification()
                .AddHttpCompression();
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.Lax;
            });

            services
                .AddScoped<UserStore<ApplicationUser, IdentityRole<int>, FlymarkContext, int, IdentityUserClaim<int>,
                        IdentityUserRole<int>, IdentityUserLogin<int>, IdentityUserToken<int>, IdentityRoleClaim<int>>,
                    ApplicationUserStore>();
            services.AddScoped<UserManager<ApplicationUser>, FlymarkUserManager>();
            services.AddScoped<RoleManager<IdentityRole<int>>, ApplicationRoleManager>();
            services.AddScoped<SignInManager<ApplicationUser>, ApplicationSignInManager>();
            services
                .AddScoped<RoleStore<IdentityRole<int>, FlymarkContext, int, IdentityUserRole<int>,
                    IdentityRoleClaim<int>>, ApplicationRoleStore>();
            services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

            services.AddIdentity<ApplicationUser, IdentityRole<int>>(
                    o =>
                    {
                        o.User.RequireUniqueEmail = true; 
                    })
                .AddUserStore<ApplicationUserStore>()
                .AddUserManager<FlymarkUserManager>()
                .AddRoleStore<ApplicationRoleStore>()
                .AddRoleManager<ApplicationRoleManager>()
                .AddSignInManager<ApplicationSignInManager>()
                .AddClaimsPrincipalFactory<FlymarkClaimsPrincipalFactory>()
                .AddDefaultTokenProviders();
            services.AddSingleton<ILoggerFactory, LoggerFactory>(sp =>
                new LoggerFactory(
                    sp.GetRequiredService<IEnumerable<ILoggerProvider>>(),
                    sp.GetRequiredService<IOptionsMonitor<LoggerFilterOptions>>()
                )
            );
            services.Configure<ApiBehaviorOptions>(options => { options.SuppressModelStateInvalidFilter = true; });
            services.AddMemoryCache();
            services.AddSingleton<IEmailSender, FlymarkEmailSender>();

            services.AddMvc(o =>
                {
                    o.Conventions.Add(new FlymarkAsyncConvention());
                    o.AllowValidatingTopLevelNodes = false;
                    o.AllowEmptyInputInBodyModelBinding = true;
                })
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
                .AddJsonOptions(opt =>
                {
                    opt.SerializerSettings.DateFormatString = "dd/MM/yyyy";
                    opt.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
                    var resolver = opt.SerializerSettings.ContractResolver;
                    if (resolver == null) return;
                    if (resolver is DefaultContractResolver res) res.NamingStrategy = null;
                });
            services.Configure<IdentityOptions>(options =>
            {
                // Default Password settings.
                options.Password.RequireDigit = false;
                options.Password.RequireLowercase = false;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = false;
                options.Password.RequiredLength = 6;
                options.Password.RequiredUniqueChars = 1;
                options.Lockout.MaxFailedAccessAttempts = 20;
            });
            services
                .AddAuthorization(options =>
                {
                    options.DefaultPolicy = new AuthorizationPolicyBuilder()
                        .AddAuthenticationSchemes(OAuthValidationDefaults.AuthenticationScheme,
                            IdentityConstants.ApplicationScheme)
                        .RequireAuthenticatedUser()
                        .Build();
                });
            services.AddAuthentication()
                .AddExternalAuthProviders(Configuration)
                .AddFlymarkOpenIdConnectServer()
                .AddOAuthValidation(OAuthValidationDefaults.AuthenticationScheme);

            services.Configure<SecurityStampValidatorOptions>(options =>
            {
                // This is the key to control how often validation takes place
                options.ValidationInterval = TimeSpan.FromMinutes(15);
            });
            services.ConfigureApplicationCookie(config =>
            {
                config.LoginPath = "/Identity/Account/LogIn";
                config.AccessDeniedPath = "/Identity/Account/LogIn";
                config.SlidingExpiration = true;
                config.Events.OnRedirectToLogin = OnRedirectToLoginAsync;
            });
        }
        private Task OnRedirectToLoginAsync(RedirectContext<CookieAuthenticationOptions> context)
        {
            if (context.HttpContext.Request.Path.Value.Contains("/api"))
                context.Response.StatusCode = 401;
            else
                context.Response.Redirect(context.RedirectUri);

            return Task.CompletedTask;
        }

        public void ConfigureContainer(ContainerBuilder builder)
        {
            builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());

            //builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
            builder.RegisterModule(new FlymarkDalDiModule
            {
                Configuration = Configuration
            });
            builder.RegisterModule(new DbDiModule(FlymarkAppSettings.Instance.DbContextConnection,
                FlymarkAppSettings.Instance.StorageConnectionString));
            builder.RegisterModule<FlymarkWebDiModule>();
        }

        private CultureInfo CreateCulture(string key)
        {
            return new CultureInfo(key)
            {
                NumberFormat = {NumberDecimalSeparator = "."},
                DateTimeFormat = {ShortDatePattern = "dd/MM/yyyy"}
            };
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env,
            ILoggerFactory loggerFactory, IMapper mapper)
        {
#if DEBUG
            mapper.ConfigurationProvider.AssertConfigurationIsValid();
#endif

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
                app.UseStaticFiles(new StaticFileOptions
                {
                    OnPrepareResponse = context =>
                    {
                        context.Context.Response.Headers.Add("Cache-Control", "no-cache, no-store");
                        context.Context.Response.Headers.Add("Expires", "-1");
                    }
                });
            }
            else
            {
                app.UseExceptionHandler("/Error/Error500");
                app.UseStaticFiles();
            }

            app.UseCors(builder =>
            {
                builder.AllowAnyOrigin()
                    .AllowAnyMethod()
                    .AllowCredentials()
                    .SetPreflightMaxAge(TimeSpan.FromMinutes(5))
                    .AllowAnyHeader();
            });
            app.UseRequestDecompression();
            app.UseLegacyTokenContentTypeFixMiddleware();
            var supportedCultures = new[]
            {
                CreateCulture("en"),
                CreateCulture("ru"),
                CreateCulture("uk")
            };
            app.UseFlymarkExceptionMiddleware();
            app.UseCookiePolicy();
            app
                .UseAuthentication()
                .UseDomainMiddleware()
                .UseRequestLocalization(new RequestLocalizationOptions
                {
                    DefaultRequestCulture = new RequestCulture("en"),
                    SupportedCultures = supportedCultures,
                    SupportedUICultures = supportedCultures
                })
                .UseWebMarkupMin();

            app.Use(async (ctx, next) =>
            {
                await next();

                if (ctx.Response.StatusCode == 404 && !ctx.Response.HasStarted)
                {
                    //Re-execute the request so the user gets the error page
                    var originalPath = ctx.Request.Path.Value;
                    ctx.Items["originalPath"] = originalPath;
                    ctx.Request.Path = "/error/error404";
                    await next();
                }
            });
            app
                .UseMvc(routes =>
                {
                    routes.MapRoute(
                        "areaRoute",
                        "{area:exists}/{controller=Dashboard}/{action=Index}/{id?}");
                    routes.MapRoute(
                        "default",
                        "{controller=Home}/{action=Index}/{id?}");
                });
        }
    }
}

Я генерирую URL для подтверждения по электронной почте следующим образом:

var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);

var callbackUrl = Url.Page("/Account/ConfirmEmail",
                        null,
                        new {userId = user.Id, code = code.ToBase64String()},
                        returnDomainUrl.Scheme,
                        returnDomainUrl.Host);

Я также подумал, что это может быть angularjs (у меня все еще естьна моей странице), но он не загружен в / signin-facebook, так как обрабатывается промежуточным ПО.

Я думаю, что проблема связана с защитой данных, так как я получаю их при входе и подтверждении по электронной почте

Я такжепопытался создать 64 почтовых токена, но это не поможет, кроме того, я думаю, что url автоматически кодируется Page.Url

Ответы [ 2 ]

0 голосов
/ 20 февраля 2019

Наконец, после нескольких недель исследований, я обнаружил проблему.

Когда пользователь регистрируется, я отправляю электронную почту и смс, затем пользователь идет и подтверждает смс, что вызовет обновление штампа безопасности.Затем, если пользователь нажимает «Подтвердить электронную почту», и она не работает, потому что штамп безопасности не является таким же, как в токене

Таким образом, пересылается электронное письмо с подтверждением после подтверждения номера телефона.Решил половину моей проблемы.

0 голосов
/ 01 февраля 2019

Скорее всего, проверка токенов завершилась неудачно, поскольку токены генерируются в домене и проверяются в другом домене.

В ASP.Net это можно решить, используя одинаковый machineKey в обоих доменах. Файлы web.config,

Для ASP.Net Core вы можете заменить machineKey, как описано здесь , чтобы у вас были одинаковые криптографические параметры в обоих доменах.

см .: ЗаменитьASP.NET machineKey в ASP.NET Core

...