如何在ASP.NET Boilerplate中设置社交登录?

时间:2018-04-13 01:55:29

标签: c# authentication asp.net-core configuration aspnetboilerplate

我正在尝试通过Google对用户进行身份验证。 我正在使用带有Vue的ASP.NET Core的ABP启动模板。

这是我到目前为止所做的:

我在Web.Core中创建了一个GoogleAuthProviderApi

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.Google;
using Newtonsoft.Json.Linq;

namespace Mindbus.MindbooksSEO.Authentication.External.Google
{
    public class GoogleAuthProviderApi : ExternalAuthProviderApiBase
    {
        public const string Name = "Google";

        public override async Task<ExternalAuthUserInfo> GetUserInfo(string accessCode)
        {
            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET Core OAuth middleware");
                client.DefaultRequestHeaders.Accept.ParseAdd("application/json");
                client.Timeout = TimeSpan.FromSeconds(30);
                client.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB

                var request = new HttpRequestMessage(HttpMethod.Get, GoogleDefaults.UserInformationEndpoint);
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessCode);

                var response = await client.SendAsync(request);

                response.EnsureSuccessStatusCode();

                var payload = JObject.Parse(await response.Content.ReadAsStringAsync());

                return new ExternalAuthUserInfo
                {
                    //Name = GoogleHelper.GetName(payload),
                    EmailAddress = GoogleHelper.GetEmail(payload),
                    //Surname = GoogleHelper.GetFamilyName(payload),
                    //ProviderKey = GoogleHelper.GetId(payload),
                    Provider = Name
                };
            }
        }
    }
}

我在Web.Host中的 AuthConfigurer.cs 中注册了Google外部身份验证:

if (bool.Parse(configuration["Authentication:Google:IsEnabled"]))
{
    services.AddAuthentication().AddGoogle(googleOptions =>
    {
        googleOptions.ClientId = configuration["Authentication:Google:ClientId"];
        googleOptions.ClientSecret = configuration["Authentication:Google:ClientSecret"];
    });
}

我已将设置添加到Web.Host中的 appsettings.json ,并创建了相应的机密( ClientId ClientSecret )秘密管理器工具。

我已使用RequireHttpsAttribute强制API进行SSL。

我在 [ProjectName] WebCoreModule.cs 中注册了GoogleAuthProviderApi

public override void PreInitialize()
{
    Configuration.DefaultNameOrConnectionString = _appConfiguration.GetConnectionString(
        MindbooksSEOConsts.ConnectionStringName
    );

    // Use database for language management
    Configuration.Modules.Zero().LanguageManagement.EnableDbLocalization();

    Configuration.Modules.AbpAspNetCore()
            .CreateControllersForAppServices(
                typeof(MindbooksSEOApplicationModule).GetAssembly()
            );

    ConfigureTokenAuth();

    Configuration.Modules.Zero().UserManagement.ExternalAuthenticationSources.Add<GoogleAuthProviderApi>();
}

我不知道我在这里缺少什么,也不知道到底会发生什么。

我原本以为调用 api / TokenAuth / GetExternalAuthenticationProviders 端点至少会给我一个包含Google的列表,但是这个请求在结果中返回一个空数组。 / p>

此外,对于我来说,这种外部身份验证的范围有点不明确,例如Google和Facebook等OAuth提供商。在我看来,您要么使用OAuth进行服务器端使用,在这种情况下,我不明白为什么要通过API公开部分内容。或者您拥有适用于JavaScript网络应用程序的OAuth,在这种情况下,您不需要在自己的服务器上使用API​​端点,只需通过Web应用程序处理整个客户端。

那么,External Authenticate API端点的确切目的是什么?是否您自己的服务器充当身份验证的代理?这样您可以同时使用外部(Google)API的客户端和服务器端使用吗?

更新1

评论要求我补充一些说明。

#1 :如果我在Postman中添加Abp.TenantId标题,则回复保持不变:

GET /api/TokenAuth/GetExternalAuthenticationProviders HTTP/1.1
Host: localhost:44300
Accept: application/json
Abp.TenantId: 2
Cache-Control: no-cache
Postman-Token: 0cb72e57-4b9a-474d-b60d-492fa727a7a2

#2 :Swagger中的控制台“欺骗”会导致错误:

abp.swagger.login()
undefined
VM40:49 POST https://localhost:44300/api/TokenAuth/Authenticate 500 ()
abp.swagger.login @ VM40:49
(anonymous) @ VM84:1
abp.swagger.addAuthToken()
false

更新2

我认为GoogleAuthProviderApi有问题。在我对所有CLR异常进行调试器中断后,我发现了以下错误:

'Mindbus.MindbooksSEO.Authentication.External.Google.GoogleAuthProviderApi' to type
 'Abp.Authorization.Users.IExternalAuthenticationSource`2
[Mindbus.MindbooksSEO.MultiTenancy.Tenant,
Mindbus.MindbooksSEO.Authorization.Users.User]'.'

2 个答案:

答案 0 :(得分:4)

ASP.NET Core 1.x或MVC 5

  1. 请注意,Social Login 提供商(例如Google)的配置与External Authentication 来源(例如LDAP)完全不同。所以,删除这一行:

    Configuration.Modules.Zero().UserManagement.ExternalAuthenticationSources.Add<GoogleAuthProviderApi>();

  2. 观察GetExternalAuthenticationProviders查看IExternalAuthConfiguration 因此,在IExternalAuthConfiguration的{​​{1}}方法中配置PostInitialize

    *WebHostModule
  3. ASP.NET Core 2.x

    虽然上述处理社交登录提供程序的方式可能有效,但它是no longer recommended

    内置if (bool.Parse(configuration["Authentication:Google:IsEnabled"])) { var externalAuthConfiguration = IocManager.Resolve<IExternalAuthConfiguration>(); externalAuthConfiguration.Providers.Add( new ExternalLoginProviderInfo( GoogleAuthProviderApi.Name, configuration["Authentication:Google:ClientId"], configuration["Authentication:Google:ClientSecret"], typeof(GoogleAuthProviderApi) ) ); } 方式:

    .AddGoogle

    ...意在与:

    一起使用
    if (bool.Parse(configuration["Authentication:Google:IsEnabled"]))
    {
        services.AddAuthentication().AddGoogle(googleOptions =>
        {
            googleOptions.ClientId = configuration["Authentication:Google:ClientId"];
            googleOptions.ClientSecret = configuration["Authentication:Google:ClientSecret"];
        });
    }
    

    获得外部身份验证计划的方法是:

    var result = await _signInManager.ExternalLoginSignInAsync(
        info.LoginProvider,
        info.ProviderKey,
        isPersistent: false,
        bypassTwoFactor : true
    );
    

    您可以修改var schemes = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); 以返回此数据。

答案 1 :(得分:0)

对于那些访问此主题却找不到答案的人(如我)。

aspnetcore 2.2和abp 4.5.0 Facebook工作示例

* WebHostModule.cs

public override void PostInitialize()
        {
            var externalAuthConfiguration = IocManager.Resolve<IExternalAuthConfiguration>();
            externalAuthConfiguration.Providers.Add(
                 new ExternalLoginProviderInfo(
                    FacebookAuthProvider.Name,
                    configuration["Authentication:Facebook:ClientId"],
                    configuration["Authentication:Facebook:Secret"],
                    typeof(FacebookAuthProvider)
                )
            );           
        }

* FacebookAuthProvider.cs

public class FacebookAuthProvider: ExternalAuthProviderApiBase
    {
        private static readonly HttpClient Client = new HttpClient();
        public const string Name = "Facebook";
        public override async Task<ExternalAuthUserInfo> GetUserInfo(string accessCode)
        {
            //gen app access token
            var appAccessTokenResponse = await Client.GetStringAsync("https://graph.facebook.com/oauth/access_token" +
              "?client_id=" + ProviderInfo.ClientId +
              "&client_secret=" + ProviderInfo.ClientSecret +
              "&grant_type=client_credentials");
            var appAccessToken = JsonConvert.DeserializeObject<FacebookAppAccessToken>(appAccessTokenResponse);
            //validate user access token
            var userAccessTokenValidationResponse = await Client.GetStringAsync("https://graph.facebook.com/v3.2/debug_token" +
                "?input_token="+ accessCode +
                "&access_token="+ appAccessToken.AccessToken);
            var userAccessTokenValidation = JsonConvert.DeserializeObject<FacebookUserAccessTokenValidation>(userAccessTokenValidationResponse);
            if (!userAccessTokenValidation.Data.IsValid)
            {
                throw new ArgumentException("login_failure Invalid facebook token.");
            }

            //get userinfo
            var userInfoResponse = await Client.GetStringAsync($"https://graph.facebook.com/v3.2/me?fields=id,email,first_name,last_name&access_token={accessCode}");
            var userInfo = JsonConvert.DeserializeObject<FacebookUserData>(userInfoResponse);

            return new ExternalAuthUserInfo
            {
                Name = userInfo.FirstName,
                EmailAddress = userInfo.Email,
                Surname=userInfo.LastName,
                Provider=Name,
                ProviderKey=userInfo.Id.ToString()
            };

        }
    }

模型

internal class FacebookUserData
    {
        public long Id { get; set; }
        public string Email { get; set; }
        public string Name { get; set; }
        [JsonProperty("first_name")]
        public string FirstName { get; set; }
        [JsonProperty("last_name")]
        public string LastName { get; set; }
        public string Gender { get; set; }
        public string Locale { get; set; }
        public FacebookPictureData Picture { get; set; }
    }

    internal class FacebookPictureData
    {
        public FacebookPicture Data { get; set; }
    }

    internal class FacebookPicture
    {
        public int Height { get; set; }
        public int Width { get; set; }
        [JsonProperty("is_silhouette")]
        public bool IsSilhouette { get; set; }
        public string Url { get; set; }
    }

    internal class FacebookUserAccessTokenData
    {
        [JsonProperty("app_id")]
        public long AppId { get; set; }
        public string Type { get; set; }
        public string Application { get; set; }
        [JsonProperty("expires_at")]
        public long ExpiresAt { get; set; }
        [JsonProperty("is_valid")]
        public bool IsValid { get; set; }
        [JsonProperty("user_id")]
        public long UserId { get; set; }
    }

    internal class FacebookUserAccessTokenValidation
    {
        public FacebookUserAccessTokenData Data { get; set; }
    }

    internal class FacebookAppAccessToken
    {
        [JsonProperty("token_type")]
        public string TokenType { get; set; }
        [JsonProperty("access_token")]
        public string AccessToken { get; set; }
    }