我的问题是,我的客户是一个移动应用程序使用的facebook sdk,直接给我一个访问令牌。 Facebook说使用sdk总是给你一个访问令牌,所以我可以直接给web API访问令牌。我明白这不是很安全,但它是可能的?
解决方法
我试图张贴这作为一个评论,而不是一个答案,因为我不提供一个真正的解决方案,但它太长。
显然,所有WebAPI Owin OAuth选项都是基于浏览器的,也就是说他们需要大量的浏览器重定向请求,不适合本地移动应用程序(我的情况)。
我仍然在调查和实验,但正如Hongye Sun在他的博客文章http://blogs.msdn.com/b/webdev/archive/2013/09/20/understanding-security-features-in-spa-template.aspx?PageIndex=2#comments的一个评论中所简要描述的,使用Facebook登录访问令牌可以直接通过API进行验证,通过图形调用/ me端点。
通过使用图形调用返回的信息,您可以检查用户是否已经注册。
最后,我们需要登录用户,也许使用Authentication.SignIn Owin方法,返回一个将用于所有后续API调用的承载令牌。
编辑:
实际上我错了,承载令牌是发出呼叫“/ Token”端点,其上输入接受类似grant_type =密码&用户名=爱丽丝&密码=密码123
这里的问题是我们没有密码(这是OAuth机制的整个要点),那么我们还能如何调用“/ Token”端点呢?
更新:
我终于找到一个工作的解决方案,以下是我不得不添加到现有的类,使其工作:
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,我添加了以下操作
[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,"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); }
而已。我发现与经典/令牌端点响应的唯一的区别是承载令牌稍短,过期和发布日期在UTC,而不是在GMT(至少在我的机器上)。
我希望这有帮助!