且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

MVC的Web 5 API与Facebook访问令牌RegisterExternal而不需要的Cookie

更新时间:2023-02-24 11:21:56

我是错误的,它接受社会令牌饼干!
它不直接接受任何外部令牌。

I was mistaken that it accepts the Social Token with cookie! It doesn't accept any External Token directly.

问题是MVC .. 5照顾一切对我们来说,即从社会媒体收集令牌和验证/处理它。之后,它会生成一个本地令牌。

The thing is.. MVC 5 is taking care of everything for us, i.e. collecting token from Social Medias and validating/processing it. After that it generates a local token.

RegisterExternal 方法也需要cookies来维持,该解决方案没有。

The RegisterExternal method also requires cookies to be maintained, the solution does not.

我已经写了 博客文章 这将在详细解释。添加下面的直截了当的答案。我的目标是让它融入和感觉默认的MVC的Web API的登录/注册流程的组成部分,以确保其易于理解。

I have written a blog post which will explain in detail. Added the straight forward answer below. I aimed to make it blend and feel integral part of Login/Signup flow of default MVC Web API to make sure its easy to understand.

下面的解决方案后,授权属性必须如下工作或你会得到响应未经授权

After the below solution, Authorize attribute must be as below to work or you will get Unauthorized response.

[Authorize]
[HostAuthentication(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalBearer)]
[HostAuthentication(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie)]

使用 ExternalBearer 如果你希望只允许令牌使用的API,使用 ApplicationCookie 如果您希望只允许登录cookie的使用的API,即从一个网站。用户如果既要允许的API为。

Use ExternalBearer if you want to allow only Tokens to use API, use ApplicationCookie if you want to allow only Logged cookie to use API i.e. from a website. User both if you want to allow the API for both.

这个动作加入 AccountController.cs

// POST api/Account/RegisterExternalToken
[OverrideAuthentication]
[AllowAnonymous]
[Route("RegisterExternalToken")]
public async Task<IHttpActionResult> RegisterExternalToken(RegisterExternalTokenBindingModel model)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    ExternalLoginData externalLogin = await ExternalLoginData.FromToken(model.Provider, model.Token);



    if (externalLogin == null)
    {
        return InternalServerError();
    }

    if (externalLogin.LoginProvider != model.Provider)
    {
        Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
        return InternalServerError();
    }

    ApplicationUser user = await UserManager.FindAsync(new UserLoginInfo(externalLogin.LoginProvider,
        externalLogin.ProviderKey));

    bool hasRegistered = user != null;
    ClaimsIdentity identity = null;
    IdentityResult result;

    if (hasRegistered)
    {
        identity = await UserManager.CreateIdentityAsync(user, OAuthDefaults.AuthenticationType);
        IEnumerable<Claim> claims = externalLogin.GetClaims();
        identity.AddClaims(claims);
        Authentication.SignIn(identity);
    }
    else
    {
        user = new ApplicationUser() { Id = Guid.NewGuid().ToString(), UserName = model.Email, Email = model.Email };

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

        var info = new ExternalLoginInfo()
        {
            DefaultUserName = model.Email,
            Login = new UserLoginInfo(model.Provider, externalLogin.ProviderKey)
        };

        result = await UserManager.AddLoginAsync(user.Id, info.Login);
        if (!result.Succeeded)
        {
            return GetErrorResult(result);
        }

        identity = await UserManager.CreateIdentityAsync(user, OAuthDefaults.AuthenticationType);
        IEnumerable<Claim> claims = externalLogin.GetClaims();
        identity.AddClaims(claims);
        Authentication.SignIn(identity);
    }

    AuthenticationTicket ticket = new AuthenticationTicket(identity, new AuthenticationProperties());
    var currentUtc = new Microsoft.Owin.Infrastructure.SystemClock().UtcNow;
    ticket.Properties.IssuedUtc = currentUtc;
    ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromDays(365));
    var accessToken = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
    Request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);



    // Create the response building a JSON object that mimics exactly the one issued by the default /Token endpoint
    JObject token = new JObject(
        new JProperty("userName", user.UserName),
        new JProperty("id", user.Id),
        new JProperty("access_token", accessToken),
        new JProperty("token_type", "bearer"),
        new JProperty("expires_in", TimeSpan.FromDays(365).TotalSeconds.ToString()),
        new JProperty(".issued", currentUtc.ToString("ddd, dd MMM yyyy HH':'mm':'ss 'GMT'")),
        new JProperty(".expires", currentUtc.Add(TimeSpan.FromDays(365)).ToString("ddd, dd MMM yyyy HH:mm:ss 'GMT'"))
    );
    return Ok(token);
}

添加此辅助方法 ExternalLoginData ​​ code>类帮手地区 AccountController.cs

Add this helper method to ExternalLoginData class in helper region in AccountController.cs

public static async Task < ExternalLoginData > FromToken(string provider, string accessToken)
{

    string verifyTokenEndPoint = "", verifyAppEndpoint = "";

    if (provider == "Facebook")
    {
        verifyTokenEndPoint = string.Format("https://graph.facebook.com/me?access_token={0}", accessToken);
        verifyAppEndpoint = string.Format("https://graph.facebook.com/app?access_token={0}", accessToken);
    }
    else if (provider == "Google")
    {
        // not implemented yet
        return null;
        //verifyTokenEndPoint = string.Format("https://www.googleapis.com/oauth2/v1/tokeninfo?access_token={0}", accessToken);
    }
    else
    {
        return null;
    }

    HttpClient client = new HttpClient();
    Uri uri = new Uri(verifyTokenEndPoint);
    HttpResponseMessage response = await client.GetAsync(uri);
    ClaimsIdentity identity = null;
    if (response.IsSuccessStatusCode)
    {
        string content = await response.Content.ReadAsStringAsync();
        dynamic iObj = (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);

        identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);

        if (provider == "Facebook")
        {
            if (appObj["id"] != Startup.facebookAuthOptions.AppId)
            {
                return null;
            }

            identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, iObj["id"].ToString(), ClaimValueTypes.String, "Facebook", "Facebook"));

        }
        else if (provider == "Google")
        {
            //not implemented yet
        }

    }

    if (identity == null)
    {
        return null;
    }

    Claim providerKeyClaim = identity.FindFirst(ClaimTypes.NameIdentifier);

    if (providerKeyClaim == null || String.IsNullOrEmpty(providerKeyClaim.Issuer) || String.IsNullOrEmpty(providerKeyClaim.Value))
    {
        return null;
    }

    if (providerKeyClaim.Issuer == ClaimsIdentity.DefaultIssuer)
    {
        return null;
    }
    return new ExternalLoginData
    {
        LoginProvider = providerKeyClaim.Issuer,
            ProviderKey = providerKeyClaim.Value,
            UserName = identity.FindFirstValue(ClaimTypes.Name)
    };
}
}

和最后的 RegisterExternalTokenBindingModel 正在使用的动作。

and finally the RegisterExternalTokenBindingModel being used by the action.

public class RegisterExternalTokenBindingModel
{
    [Required]
    [Display(Name = "Email")]
    public string Email { get; set; }
    [Required]
    [Display(Name = "Token")]
    public string Token { get; set; }
    [Required]
    [Display(Name = "Provider")]
    public string Provider { get; set; }
}

是的,我们通过电子邮件与令牌细节一起注册时,这不会对你使用Twitter时,如Twitter不提供用户的电子邮件来更改code。我们验证令牌来自于我们的应用程序。不管发送的电子邮件的一次电子邮件的注册,黑客攻击或别人的令牌不能被用来改变电子邮件或获取本地令牌的电子邮件,因为它总是返回本地令牌社会令牌的实际用户通过。

Yes, we pass the email along with Token details while registering, this will not cause you to change the code when using Twitter, as Twitter don't provide users email. We verify token comes from our app. Once email registered, hacked or somebody else's token cannot be used to change email or get local token for that email as it will always return local token for the actual user of the Social Token passed regardless of the email sent.

RegisterExternalToken 端点作品获得令牌两种方式,即注册用户,并发送本地令牌,或者如果用户已经注册然后发送令牌。

RegisterExternalToken endpoint works to get token in both ways i.e. register the user and send the Local token or if user already registered then send the token.