如何在 asp.net 核心 2 中验证 facebook web api

How to authenticate facebook web api in asp.net core 2

我目前正在 Xamarin.Forms 中构建我的第一个移动应用程序。该应用程序有一个 facebook 登录名,在用户登录后我存储了 facebook 令牌,因为我想将它用作 bearer-token 来验证针对 API.[=17 的任何进一步请求=]

API 是一个 .NET core 2.0 项目,我正在努力使身份验证正常工作。

在我的 Xamarin.Forms 应用程序中,Facebook 令牌设置为 bearer-token,代码如下;

_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", UserToken);

据我所知,这会在请求的 headers 中正确设置不记名令牌。 我已经和我的一位同事谈过这个,他告诉我看一下应该支持这个的 Identityserver4。但就目前而言,我决定不这样做,因为对我来说,目前实施它是开销。所以我决定坚持使用 facebook 令牌作为不记名令牌的想法并验证这一点。

所以我的下一步是找到一种方法来验证 Facebook 传入的不记名令牌,以检查它是否(仍然)有效。 所以我已经为我的 API 项目配置了如下启动;

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(o =>
        {
            o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddFacebook(o =>
        {
            o.AppId = "MyAppId";
            o.AppSecret = "MyAppSecret";
        });

        services.AddMvc();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        //Enable authentication
        app.UseAuthentication();

        //Enable support for default files (eg. default.htm, default.html, index.htm, index.html)
        app.UseDefaultFiles();

        //Configure support for static files
        app.UseStaticFiles();

        app.UseMvc();
    }
}

但是当我使用邮递员发送请求并测试是否一切正常时,我收到以下错误;

InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.

我做错了什么?

编辑: 与此同时,如果一直忙于为此寻找解决方案。在 Google 上阅读了大量内容后,似乎添加授权处理程序是目前的方法。从那里我可以向 facebook 请求检查令牌是否有效。我已将以下代码添加到我的 ConfigureServices 方法中;

public void ConfigureServices(IServiceCollection services)
    {
        //Other code

        services.AddAuthorization(options =>
        {
            options.AddPolicy("FacebookAuthentication", policy => policy.Requirements.Add(new FacebookRequirement()));
        });

        services.AddMvc();
    }

我已经创建了一个 FacebookRequirement 来帮助我处理政策;

public class FacebookRequirement : AuthorizationHandler<FacebookRequirement>, IAuthorizationRequirement
    {
        private readonly IHttpContextAccessor contextAccessor;
        public FacebookRequirement(IHttpContextAccessor contextAccessor)
        {
            this.contextAccessor = contextAccessor;
        }

        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FacebookRequirement requirement)
        {
            //var socialConfig = new SocialConfig{Facebook = new SocialApp{AppId = "MyAppId", AppSecret = "MyAppSecret" } };
            //var socialservice = new SocialAuthService(socialConfig);

            //var result = await socialservice.VerifyFacebookTokenAsync()
            var httpContext = contextAccessor.HttpContext;

            if (httpContext != null && httpContext.Request.Headers.ContainsKey("Authorization"))
            {
                var token = httpContext.Request.Headers.Where(x => x.Key == "Authorization").ToList();
            }

            context.Succeed(requirement);

            return Task.FromResult(0);
        }
    }

我 运行 现在遇到的问题是我不知道从哪里获得 IHttpContextAccessor。这是以某种方式注入的吗?我是否在解决这个问题的正确道路上?

我最终创建了自己的 AuthorizationHandler 以使用不记名令牌验证针对 facebook 的传入请求。将来我可能会开始使用 Identityserver 来处理多种登录类型。但目前facebook已经足够了。

下面是解决方案,供以后参考。

首先创建一个 FacebookRequirement class 继承自 AuthorizationHandler;

public class FacebookRequirement : AuthorizationHandler<FacebookRequirement>, IAuthorizationRequirement
    {
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FacebookRequirement requirement)
        {
            var socialConfig = new SocialConfig { Facebook = new SocialApp { AppId = "<FacebookAppId>", AppSecret = "<FacebookAppSecret>" } };
            var socialservice = new SocialAuthService(socialConfig);

            var authorizationFilterContext = context.Resource as AuthorizationFilterContext;
            if (authorizationFilterContext == null)
            {
                context.Fail();
                return Task.FromResult(0);
            }

            var httpContext = authorizationFilterContext.HttpContext;
            if (httpContext != null && httpContext.Request.Headers.ContainsKey("Authorization"))
            {
                var authorizationHeaders = httpContext.Request.Headers.Where(x => x.Key == "Authorization").ToList();
                var token = authorizationHeaders.FirstOrDefault(header => header.Key == "Authorization").Value.ToString().Split(' ')[1];

                var user = socialservice.VerifyTokenAsync(new ExternalToken { Provider = "Facebook", Token = token }).Result;
                if (!user.IsVerified)
                {
                    context.Fail();
                    return Task.FromResult(0);
                }

                context.Succeed(requirement);
                return Task.FromResult(0);
            }

            context.Fail();
            return Task.FromResult(0);
        }
    }

添加以下 classes,其中将包含代表用户的配置;

public class SocialConfig
    {
        public SocialApp Facebook { get; set; }
    }

    public class SocialApp
    {
        public string AppId { get; set; }
        public string AppSecret { get; set; }
    }

    public class User
    {
        public Guid Id { get; set; }
        public string SocialUserId { get; set; }
        public string Email { get; set; }
        public bool IsVerified { get; set; }
        public string Name { get; set; }

        public User()
        {
            IsVerified = false;
        }
    }

    public class ExternalToken
    {
        public string Provider { get; set; }
        public string Token { get; set; }
    }

最后但同样重要的是,SocialAuthService class 将处理 facebook 的请求;

public class SocialAuthService
    {
        private SocialConfig SocialConfig { get; set; }

        public SocialAuthService(SocialConfig socialConfig)
        {
            SocialConfig = socialConfig;
        }

        public async Task<User> VerifyTokenAsync(ExternalToken exteralToken)
        {
            switch (exteralToken.Provider)
            {
                case "Facebook":
                    return await VerifyFacebookTokenAsync(exteralToken.Token);
                default:
                    return null;
            }
        }

        private async Task<User> VerifyFacebookTokenAsync(string token)
        {
            var user = new User();
            var client = new HttpClient();

            var verifyTokenEndPoint = string.Format("https://graph.facebook.com/me?access_token={0}&fields=email,name", token);
            var verifyAppEndpoint = string.Format("https://graph.facebook.com/app?access_token={0}", token);

            var uri = new Uri(verifyTokenEndPoint);
            var response = await client.GetAsync(uri);

            if (response.IsSuccessStatusCode)
            {
                var content = await response.Content.ReadAsStringAsync();
                dynamic userObj = (Newtonsoft.Json.Linq.JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(content);

                uri = new Uri(verifyAppEndpoint);
                response = await client.GetAsync(uri);
                content = await response.Content.ReadAsStringAsync();
                dynamic appObj = (Newtonsoft.Json.Linq.JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(content);

                if (appObj["id"] == SocialConfig.Facebook.AppId)
                {
                    //token is from our App
                    user.SocialUserId = userObj["id"];
                    user.Email = userObj["email"];
                    user.Name = userObj["name"];
                    user.IsVerified = true;
                }

                return user;
            }
            return user;
        }
    }

这会将来自请求的 Facebook 令牌验证为不记名令牌,Facebook 会检查它是否仍然有效。