ASP.NET Web API和带有Facebook登录名的身份 [英] ASP.NET Web API and Identity with Facebook login

查看:78
本文介绍了ASP.NET Web API和带有Facebook登录名的身份的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在用于ASP.NET Identity的Facebook身份验证流中,Facebook OAuth对话框将代码而不是访问令牌附加到redirect_url,以便服务器可以通过以下方式将该代码与访问令牌交换:

In the Facebook authentication flow for ASP.NET Identity, the Facebook OAuth dialog appends a code rather than an access token to the redirect_url so that the server can exchange this code for an access token via e.g.:

http://localhost:49164/signin-facebook?code=…&state=…

我的问题是我的客户端是一个使用Facebook SDK的移动应用程序,并且立即为我提供了访问令牌. Facebook说,总是使用SDK 给您访问令牌,所以我可以直接将它提供给ASP.NET Web API吗?

My problem is that my client is a mobile app which uses the Facebook SDK, and that straight away gives me an access token. Facebook says using the SDK always gives you an access token, so can I just give this directly to ASP.NET Web API?

我知道这不是很安全,但是有可能吗?

I understand this is not very secure, but is it even possible?

推荐答案

我不知道您是否找到了解决方案,但我正在尝试做类似的事情,但我仍然在解决难题.我试图将其发布为评论而不是答案,因为我没有提供真正的解决方案,但是它太长了.

I don't know if you ever found a solution, but I'm trying to do something similar and I'm still putting the pieces of the puzzle together. I had tried to post this as a comment instead of an answer, as I do not provide a real solution, but it's too long.

显然,所有WebAPI Owin OAuth选项都是基于浏览器的-也就是说,它们需要大量的浏览器重定向请求,这些请求不适合本机移动应用程序(根据我的情况).我仍在调查和试验中,但作为

Apparently all of the WebAPI Owin OAuth options are browser based—that is, they require lots of browser redirect requests that do not fit a native mobile app (as required for my case). I'm still investigating and experimenting, but as briefly described by Hongye Sun in a comment to his blog post, to login with Facebook the access token received using the Facebook SDK can be verified directly via the API by making a graph call to the /me endpoint.

使用图表调用返回的信息,然后可以检查用户是否已注册.最后,我们需要使用Owin的Authentication.SignIn方法登录用户,并返回一个承载令牌,该令牌将用于所有后续的API调用.

By using the information returned by the graph call, you can then check if the user is already registered or not. At the end, we need to sign-in the user, maybe using Owin's Authentication.SignIn method, returning a bearer token that will be used for all subsequent API calls.

编辑:实际上,我理解错了.承载令牌是在调用/Token端点时发出的,输入时接受以下内容:

Actually, I got it wrong. The bearer token is issued on calling /Token endpoint, which on input accepts something like:

grant_type=password&username=Alice&password=password123

这里的问题是我们没有密码-这就是OAuth机制的重点-那么我们还可以如何调用/Token端点?

The problem here is that we do not have a password—that's the whole point of the OAuth mechanism—so how else can we invoke the /Token endpoint?

更新:我终于找到了一个可行的解决方案,下面是我必须添加到现有类中才能使其起作用的方法:

UPDATE: I finally found a working solution and the following is what I had to add to the existing classes to make it work:

Startup.Auth.cs

public partial class Startup
{
    /// <summary>
    /// This part has been added to have an API endpoint to authenticate users that accept a Facebook access token
    /// </summary>
    static Startup()
    {
        PublicClientId = "self";

        //UserManagerFactory = () => new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
        UserManagerFactory = () => 
        {
            var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
            userManager.UserValidator = new UserValidator<ApplicationUser>(userManager) { AllowOnlyAlphanumericUserNames = false };
            return userManager;
        };

        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true
        };

        OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
        OAuthBearerOptions.AccessTokenFormat = OAuthOptions.AccessTokenFormat;
        OAuthBearerOptions.AccessTokenProvider = OAuthOptions.AccessTokenProvider;
        OAuthBearerOptions.AuthenticationMode = OAuthOptions.AuthenticationMode;
        OAuthBearerOptions.AuthenticationType = OAuthOptions.AuthenticationType;
        OAuthBearerOptions.Description = OAuthOptions.Description;
        OAuthBearerOptions.Provider = new CustomBearerAuthenticationProvider();            
        OAuthBearerOptions.SystemClock = OAuthOptions.SystemClock;
    }

    public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }

    public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

    public static Func<UserManager<ApplicationUser>> UserManagerFactory { get; set; }

    public static string PublicClientId { get; private set; }

    // For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
    public void ConfigureAuth(IAppBuilder app)
    {
        [Initial boilerplate code]

        OAuthBearerAuthenticationExtensions.UseOAuthBearerAuthentication(app, OAuthBearerOptions);

        [More boilerplate code]
    }
}

public class CustomBearerAuthenticationProvider : OAuthBearerAuthenticationProvider
{
    public override Task ValidateIdentity(OAuthValidateIdentityContext context)
    {
        var claims = context.Ticket.Identity.Claims;
        if (claims.Count() == 0 || claims.Any(claim => claim.Issuer != "Facebook" && claim.Issuer != "LOCAL_AUTHORITY" ))
            context.Rejected();
        return Task.FromResult<object>(null);
    }
}

AccountController中,我添加了以下操作:

And in AccountController, I added the following action:

[HttpPost]
[AllowAnonymous]
[Route("FacebookLogin")]
public async Task<IHttpActionResult> FacebookLogin(string token)
{
    [Code to validate input...]
    var tokenExpirationTimeSpan = TimeSpan.FromDays(14);            
    ApplicationUser user = null;    
    // Get the fb access token and make a graph call to the /me endpoint    
    // Check if the user is already registered
    // If yes retrieve the user 
    // If not, register it  
    // Finally sign-in the user: this is the key part of the code that creates the bearer token and authenticate the user
    var identity = new ClaimsIdentity(Startup.OAuthBearerOptions.AuthenticationType);
    identity.AddClaim(new Claim(ClaimTypes.Name, user.Id, null, "Facebook"));
        // This claim is used to correctly populate user id
        identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id, null, "LOCAL_AUTHORITY"));
    AuthenticationTicket ticket = new AuthenticationTicket(identity, new AuthenticationProperties());            
    var currentUtc = new Microsoft.Owin.Infrastructure.SystemClock().UtcNow;
    ticket.Properties.IssuedUtc = currentUtc;
    ticket.Properties.ExpiresUtc = currentUtc.Add(tokenExpirationTimeSpan);            
    var accesstoken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket); 
    Authentication.SignIn(identity);

    // Create the response
    JObject blob = new JObject(
        new JProperty("userName", user.UserName),
        new JProperty("access_token", accesstoken),
        new JProperty("token_type", "bearer"),
        new JProperty("expires_in", tokenExpirationTimeSpan.TotalSeconds.ToString()),
        new JProperty(".issued", ticket.Properties.IssuedUtc.ToString()),
        new JProperty(".expires", ticket.Properties.ExpiresUtc.ToString())
    );
    var json = Newtonsoft.Json.JsonConvert.SerializeObject(blob);
    // Return OK
    return Ok(blob);
}

就是这样!我发现与经典/Token终结点响应的唯一区别是,承载令牌稍短,并且到期日期和发布日期均以UTC而不是GMT(至少在我的计算机上).

That's it! The only difference I found with the classic /Token endpoint response is that the bearer token is slightly shorter and the expiration and issue dates are in UTC instead that in GMT (at least on my machine).

我希望这会有所帮助!

这篇关于ASP.NET Web API和带有Facebook登录名的身份的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆