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

Rob*_*Rob 4 c# facebook asp.net-web-api bearer-token asp.net-core

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

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

在我的 Xamarin.Forms 应用程序中,使用以下代码将 facebook 令牌设置为不记名令牌;

_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", UserToken);
Run Code Online (Sandbox Code Playgroud)

据我所知,这正确地在请求的标头中设置了承载令牌。我和我的一位同事谈过这个问题,他告诉我看一下 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.
Run Code Online (Sandbox Code Playgroud)

我在这里做错了什么?

编辑:同时,如果忙于寻找解决方案。在 Google 上阅读了大量内容后,似乎添加 AuthorizationHandler 是目前的最佳选择。从那里我可以向 Facebook 发出请求以检查令牌是否有效。我已将以下代码添加到我的ConfigureServices 方法中;

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

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

        services.AddMvc();
    }
Run Code Online (Sandbox Code Playgroud)

我创建了一个 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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

Rob*_*Rob 5

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

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

首先创建一个继承自;FacebookRequirement的类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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

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; }
    }
Run Code Online (Sandbox Code Playgroud)

最后但并非最不重要的一点是,SocialAuthService将处理 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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

  • 也许我迟到了,但这仍然是使用不记名令牌在 .NET Core WebApi 中进行社交登录的方法吗?有没有办法(或教程)在 WebApi Core 中使用 Microsoft.ApsNetCore.Authentication.Facebook 内置包? (2认同)