ASP.NET Web API 2:通过本机移动(iOS)应用程序与外部提供程序登录

时间:2014-08-14 07:32:29

标签: ios facebook asp.net-web-api owin asp.net-web-api2

我做了很多搜索,但却未能找到解决此问题的理想解决方案。我知道有一个所谓的解决方案(WebApi ASP.NET Identity Facebook login)然而,解决方案的一些元素是(在我看来)严重hacky(例如,用普通帐户注册用户,然后添加外部登录,而不是注册他们使用外部登录)。

我希望能够在已经在iOS移动应用上使用Facebook SDK登录后注册和验证ASP.NET Web API 2应用程序,即我已经使用他们的SDK对Facebook进行了身份验证,现在想要使用ASP.NET Web API无缝注册/验证。我不想使用我必须使用网络电话(/ api / Account / ExternalLogin)的过程,因为这对本机移动应用程序来说不是一个很棒的用户体验。

我尝试了解OWIN,但.NET框架很复杂,我在如何解决这个问题上一直在努力。

1 个答案:

答案 0 :(得分:1)

我今天需要为我的Ionic应用做这件事。 Web API帐户控制器对如何做事有自己的看法,最好的理解方法是阅读Dominick Baier撰写的这篇非常棒的3部分博客文章。 https://leastprivilege.com/2013/11/26/dissecting-the-web-api-individual-accounts-templatepart-3-external-accounts/

我解决这个问题的方法是忘记开箱即用的流程,而是使用本地Facebook登录中的accessToken然后调用以下服务器代码1)调用Facebook API来验证访问令牌,2)从该Facebook通话,获取电子邮件和身份证,3)获取用户或创建它(和登录),这已经是其他地方的帐户控制器中的代码,4)创建用于后续Web API调用的本地权限JWT。

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

[AllowAnonymous]
[HttpPost]
[Route("JwtFromProviderAccessToken")]
public async Task<IHttpActionResult> JwtFromProviderAccessToken(ProviderAndAccessToken model) {
    string id = null;
    string userName = null;

    if (model.Provider == "Facebook") {
        var fbclient = new Facebook.FacebookClient(model.Token);
        dynamic fb = fbclient.Get("/me?locale=en_US&fields=name,email");
        id = fb.id;
        userName = fb.email;
    }

    //TODO: Google, LinkedIn

    ApplicationUser user = await UserManager.FindAsync(new UserLoginInfo(model.Provider, id));
    bool hasRegistered = user != null;

    string accessToken = null;
    var identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);
    var props = new AuthenticationProperties() {
        IssuedUtc = DateTime.UtcNow,
        ExpiresUtc = DateTime.UtcNow.Add(Startup.OAuthOptions.AccessTokenExpireTimeSpan),
    };

    if (hasRegistered) {
        ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager,
            OAuthDefaults.AuthenticationType);
        ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager,
            CookieAuthenticationDefaults.AuthenticationType);

        AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
        Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);


        identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName));
        identity.AddClaim(new Claim("role", "user"));

    }
    else {

        user = new ApplicationUser() { UserName = userName, Email = userName };

        IdentityResult result = await UserManager.CreateAsync(user);
        if (!result.Succeeded) {
            return GetErrorResult(result);
        }

        result = await UserManager.AddLoginAsync(user.Id, new UserLoginInfo(model.Provider, id));
        if (!result.Succeeded) {
            return GetErrorResult(result);
        }

        identity.AddClaim(new Claim(ClaimTypes.Name, userName));
    }

    identity.AddClaim(new Claim("role", "user"));
    var ticket = new AuthenticationTicket(identity, props);
    accessToken = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);

    return Ok(accessToken);
}

我在Ionic中使用的代码基本上是为了从Facebook获取访问令牌,然后调用Web API以获取本地权限JWT作为Bearer令牌。

Facebook.login(['public_profile', 'email']).then((result) => {
    return this.http.post("<URL>/api/Account/JwtFromProviderAccessToken", { provider: "Facebook", token: result.authResponse.accessToken })
        .map((res: Response) => res.json())
        .catch(this.handleError)
        .subscribe((res: Response) => {
            // use the result as the Bearer token
        });
})...

看起来非常安全,但我知道我不是安全专家,所以这段代码没有保修,如果您发现任何明显的内容并且我会更新代码,请告诉我。