简体   繁体   English

ASP.NET Core中的Google + / Facebook身份验证

[英]Google+/ Facebook Authentication in ASP.NET Core

I've been reading through a lot of guides for getting basic user authentication going using external authentication services, and I just can't get the freaking thing to work. 我已经阅读了很多指南,以使用外部身份验证服务来进行基本的用户身份验证,但我只是无法正常工作。

I started with a "boiler plate" .NET Core project with user-level security. 我从一个具有用户级安全性的“样板” .NET Core项目开始。 I have created my Google and Facebook Auth keys, and included the necessary libraries. 我已经创建了Google和Facebook Auth密钥,并包括了必要的库。 I set my user class like so: 我将用户类设置为:

public class ApplicationUser : IdentityUser<Guid>
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string MiddleName { get; set; }
    public string Nickname { get; set; }
}

My Startup.cs file looks like this: 我的Startup.cs文件如下所示:

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

        if (env.IsDevelopment())
            builder.AddUserSecrets();

        builder.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)
    {
        // Add framework services.
        services.AddDbContext<ApplicationDbContext>(
            options => options.UseNpgsql(Configuration.GetConnectionString("ApplicationDb")));

        services.AddIdentity<ApplicationUser, ApplicationRole>(opt =>
            {
                opt.Password.RequireNonAlphanumeric = false;
                opt.Password.RequireUppercase = false;
                opt.Password.RequireLowercase = false;
                opt.Password.RequireDigit = false;
                opt.Password.RequiredLength = 8;

                opt.User.RequireUniqueEmail = true;
                opt.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_=+~@.";

                opt.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
                opt.Lockout.MaxFailedAccessAttempts = 5;

                opt.SignIn.RequireConfirmedEmail = true;
                opt.SignIn.RequireConfirmedPhoneNumber = false;
            })
            .AddEntityFrameworkStores<ApplicationDbContext, Guid>()
            .AddDefaultTokenProviders();

        services.AddMvc(options => { options.Filters.Add(new RequireHttpsAttribute()); });

        // Add application services.
        services.AddTransient<IEmailSender, AuthMessageSender>();
        services.AddTransient<ISmsSender, AuthMessageSender>();
    }

    // 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)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseDatabaseErrorPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseStaticFiles();

        app.UseIdentity();

        //Add external authentication middleware below.To configure them please see http://go.microsoft.com/fwlink/?LinkID=532715
        var facebookOptions = new FacebookOptions
        {
            AppId = Configuration["Authentication:Facebook:AppId"],
            AppSecret = Configuration["Authentication:Facebook:AppSecret"]
        };
        app.UseFacebookAuthentication(facebookOptions);

        var googleOptions = new GoogleOptions
        {
            ClientId = Configuration["Authentication:Google:AppId"],
            ClientSecret = Configuration["Authentication:Google:AppSecret"]
        };
        app.UseGoogleAuthentication(googleOptions);

        app.UseMvc(routes =>
        {
            routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

So far, so good. 到现在为止还挺好。 I start up the project, click on the "Log In" link, and I can choose to log in with Google or Facebook. 我启动该项目,单击“登录”链接,然后选择使用Google或Facebook登录。 I select Google, and... the first time I try, I get prompted for approval by Google, and pointed back to the Register page, where I create my local account with nickname, etc. 我选择Google,然后...第一次尝试,系统提示我获得Google的批准,并指向“注册”页面,在该页面上我用昵称创建本地帐户,等等。

Now, I can go add a local password to my account. 现在,我可以将本地密码添加到我的帐户了。 All is working swimmingly. 所有人都在努力工作。 So I log off and try to log in again using my Google credentials, after deleting my cookie. 因此,在删除Cookie后,我注销并尝试使用我的Google凭据再次登录。 Surprisingly, I'm brought to the Register page again, but I can't register because I'm already registered. 令人惊讶的是,我再次被带到“注册”页面,但是由于已经注册,所以无法注册。 My local password won't work either. 我的本地密码也不起作用。

So I dig into this a bit more. 因此,我对此进行了更多研究。 In the AccountController there is a method called ExternalLoginCallback where all the magic seems to happen. AccountController有一个名为ExternalLoginCallback的方法,似乎所有魔术都在发生。 My method looks like this: 我的方法如下所示:

    [HttpGet]
    [AllowAnonymous]
    public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
    {
        if (remoteError != null)
        {
            ModelState.AddModelError(string.Empty, $"Error from external provider: {remoteError}");
            return View(nameof(Login));
        }
        var info = await _signInManager.GetExternalLoginInfoAsync();
        if (info == null)
            return RedirectToAction(nameof(Login));

        // Sign in the user with this external login provider if the user already has a login.
        var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, false);
        if (result.Succeeded)
        {
            _logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider);
            return RedirectToLocal(returnUrl);
        }
        if (result.RequiresTwoFactor)
            return RedirectToAction(nameof(SendCode), new
            {
                ReturnUrl = returnUrl
            });
        if (result.IsLockedOut)
            return View("Lockout");
        // If the user does not have an account, then ask the user to create an account.
        ViewData["ReturnUrl"] = returnUrl;
        ViewData["LoginProvider"] = info.LoginProvider;
        var email = info.Principal.FindFirstValue(ClaimTypes.Email);
        var firstName = info.Principal.FindFirstValue(ClaimTypes.GivenName);
        var lastName = info.Principal.FindFirstValue(ClaimTypes.Surname);
        return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel
        {
            Email = email,
            FirstName = firstName,
            LastName = lastName
        });
    }

I trace the code as it goes through the _signInManager.GetExternalLoginInfoAsync() call, and... the result is NotAllowed ? 我通过_signInManager.GetExternalLoginInfoAsync()调用来跟踪代码,并且...结果是NotAllowed吗?

NotAllowed is one of 4 possible results, as far as I can tell, the other 3 being Succeeded , RequiresTwoFactor , and IsLockedOut . 据我所知, NotAllowed是4种可能结果之一,另外3种是SucceededRequiresTwoFactorIsLockedOut The other 3 values get sent someplace else, so I must assume NotAllowed is expected when no local account exists... except that a local count exists. 其他3个值在其他地方发送,因此我必须假定在不存在本地帐户的情况下期望NotAllowed ...除了存在本地计数之外。

Can someone give me an idea of what is going on here? 有人可以告诉我这里发生了什么吗? Once I sign off with Google (or Facebook, the same sort of thing happens there), I cannot log back in with them, and there is almost zero useful feedback to determine what the actual problem is. 一旦我与Google(或Facebook)签了字(在那里也发生了类似的事情),我将无法重新登录,并且几乎没有零的有用反馈来确定实际问题是什么。

Never mind; 没关系; I'm an idiot. 我是个白痴。

I thought I was being super cool when I put this line in: 当我把这行代码放进去时,我以为自己很酷:

opt.SignIn.RequireConfirmedEmail = true;

...but, since I wasn't confirming the email, guess what wasn't happening? ...但是,由于我没有确认电子邮件,因此您猜怎么了? SMH... SMH ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM