WebApi ASP.NET Identity Facebook для входа в систему

В streamе аутентификации facebook для идентификации asp.net диалог facebook oauth добавляет код, а не токен доступа в redirect_url, чтобы сервер мог обменивать этот код на токен доступа через http://localhost:49164/signin-facebook?code=...&state=...

Моя проблема заключается в том, что мой клиент – мобильное приложение, которое использует facebook sdk и сразу же дает мне токен доступа. Facebook говорит, что использование sdk всегда дает вам токен доступа, так что я могу сразу показать web api токен доступа?

Я понимаю, что это не очень безопасно, но возможно ли это?

Я не знаю, нашли ли вы наконец решение, но я пытаюсь сделать что-то довольно похожее, и я все еще собираю кусочки головоломки. Я попытался опубликовать это как комментарий вместо ответа, так как я не предлагаю реального решения, но он слишком длинный.

Очевидно, что все параметры OAPA для WebAPI Owin основаны на браузере, то есть они требуют большого количества запросов перенаправления браузеров, которые не подходят для родного мобильного приложения (в моем случае). Я все еще расследую и экспериментирую, но как кратко описано Hongye Sun в одном из комментариев к его сообщению в блоге, http://blogs.msdn.com/b/webdev/archive/2013/09/20/understanding-security -features-in-spa-template.aspx? PageIndex = 2 # comments , чтобы войти в Facebook, токен доступа, полученный с помощью Facebook SDK, может быть проверен непосредственно API, создающим вызов графика конечной точке / me.

Используя информацию, возвращаемую вызовом графа, вы можете проверить, зарегистрирован ли пользователь или нет. В конце нам нужно войти в систему, возможно, используя метод Authentication.SignIn Owin, возвращая токен-носитель, который будет использоваться для всех последующих вызовов API.

EDIT: Вообще-то я ошибался, токен-носитель выдается при вызове конечной точки «/ Token», который на входе принимает что-то вроде grant_type=password&username=Alice&password=password123 . Проблема в том, что у нас нет пароля (вот и все. механизма OAuth), так как же еще можно вызвать конечную точку «/ Token»?

UPDATE: я наконец нашел рабочее решение, и вот что мне пришлось добавить к существующим classам, чтобы заставить его работать: Startup.Auth.cs

 public partial class Startup { ///  /// This part has been added to have an API endpoint to authenticate users that accept a Facebook access token ///  static Startup() { PublicClientId = "self"; //UserManagerFactory = () => new UserManager(new UserStore(new ApplicationDbContext())); UserManagerFactory = () => { var userManager = new UserManager(new UserStore(new ApplicationDbContext())); userManager.UserValidator = new UserValidator(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> 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(null); } } 

В AccountController я добавил следующее действие

  [HttpPost] [AllowAnonymous] [Route("FacebookLogin")] public async Task 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); } 

Вот и все. Единственное отличие, которое я обнаружил с ответом на конечную точку classики / токена, заключается в том, что токен-носитель немного короче, а даты истечения и выпуска указаны в формате UTC, а не в GMT (по крайней мере, на моей машине).

Надеюсь, это поможет!

Вслед за отличным решением от @ s0nica я изменил некоторые коды, чтобы интегрироваться с существующим в настоящее время шаблоном ASP.NET MVC. s0nica подходит, но не полностью совместим с MVC (Non-WebApi) AccountController .

Преимущество моего подхода – работать как с ASP.NET MVC, так и с WebApi наоборот.

Основные отличия – это название заявки. В качестве имени заявки используется FacebookAccessToken а затем ссылка ( http://blogs.msdn.com/b/webdev/archive/2013/10/16/get-more-information-from-social-providers-used-in- the-vs-2013-project-templates.aspx ), мой подход совместим с подходом к данной ссылке. Я рекомендую использовать его.

Обратите внимание, что ниже коды являются модифицированной версией ответа @ s0nica. Итак, (1) прохождение данной ссылки, (2), а затем прохождение кода s0nica, (3) и, наконец, рассмотрение моего потом.

Файл Startup.Auth.cs.

 public class CustomBearerAuthenticationProvider : OAuthBearerAuthenticationProvider { // This validates the identity based on the issuer of the claim. // The issuer is set in the API endpoint that logs the user in public override Task ValidateIdentity(OAuthValidateIdentityContext context) { var claims = context.Ticket.Identity.Claims; if (!claims.Any() || claims.Any(claim => claim.Type != "FacebookAccessToken")) // modify claim name context.Rejected(); return Task.FromResult(null); } } 

API / AccountController.cs

  // POST api/Account/FacebookLogin [HttpPost] [AllowAnonymous] [Route("FacebookLogin")] public async Task FacebookLogin([FromBody] FacebookLoginModel model) { if (!ModelState.IsValid) { return BadRequest(ModelState); } if (string.IsNullOrEmpty(model.token)) { return BadRequest("No access token"); } var tokenExpirationTimeSpan = TimeSpan.FromDays(300); ApplicationUser user = null; string username; // Get the fb access token and make a graph call to the /me endpoint var fbUser = await VerifyFacebookAccessToken(model.token); if (fbUser == null) { return BadRequest("Invalid OAuth access token"); } UserLoginInfo loginInfo = new UserLoginInfo("Facebook", model.userid); user = await UserManager.FindAsync(loginInfo); // If user not found, register him with username. if (user == null) { if (String.IsNullOrEmpty(model.username)) return BadRequest("unregistered user"); user = new ApplicationUser { UserName = model.username }; var result = await UserManager.CreateAsync(user); if (result.Succeeded) { result = await UserManager.AddLoginAsync(user.Id, loginInfo); username = model.username; if (!result.Succeeded) return BadRequest("cannot add facebook login"); } else { return BadRequest("cannot create user"); } } else { // existed user. username = user.UserName; } // common process: Facebook claims update, Login token generation user = await UserManager.FindByNameAsync(username); // Optional: make email address confirmed when user is logged in from Facebook. user.Email = fbUser.email; user.EmailConfirmed = true; await UserManager.UpdateAsync(user); // Sign-in the user using the OWIN flow var identity = new ClaimsIdentity(Startup.OAuthBearerOptions.AuthenticationType); var claims = await UserManager.GetClaimsAsync(user.Id); var newClaim = new Claim("FacebookAccessToken", model.token); // For compatibility with ASP.NET MVC AccountController var oldClaim = claims.FirstOrDefault(c => c.Type.Equals("FacebookAccessToken")); if (oldClaim == null) { var claimResult = await UserManager.AddClaimAsync(user.Id, newClaim); if (!claimResult.Succeeded) return BadRequest("cannot add claims"); } else { await UserManager.RemoveClaimAsync(user.Id, oldClaim); await UserManager.AddClaimAsync(user.Id, newClaim); } AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName); var currentUtc = new Microsoft.Owin.Infrastructure.SystemClock().UtcNow; properties.IssuedUtc = currentUtc; properties.ExpiresUtc = currentUtc.Add(tokenExpirationTimeSpan); AuthenticationTicket ticket = new AuthenticationTicket(identity, properties); var accesstoken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket); Request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accesstoken); Authentication.SignIn(identity); // Create the response building a JSON object that mimics exactly the one issued by the default /Token endpoint 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()), new JProperty("model.token", model.token), ); // Return OK return Ok(blob); } 

Модель входа в Facebook для привязки (внутренний class api / AccountController.cs)

  public class FacebookLoginModel { public string token { get; set; } public string username { get; set; } public string userid { get; set; } } public class FacebookUserViewModel { public string id { get; set; } public string first_name { get; set; } public string last_name { get; set; } public string username { get; set; } public string email { get; set; } } 

Метод VerifyFacebookAccessToken (в api / AccountController.cs)

  private async Task VerifyFacebookAccessToken(string accessToken) { FacebookUserViewModel fbUser = null; var path = "https://graph.facebook.com/me?access_token=" + accessToken; var client = new HttpClient(); var uri = new Uri(path); var response = await client.GetAsync(uri); if (response.IsSuccessStatusCode) { var content = await response.Content.ReadAsStringAsync(); fbUser = Newtonsoft.Json.JsonConvert.DeserializeObject(content); } return fbUser; } 

Да, вы можете использовать токен внешнего доступа для безопасного входа в систему.

Я настоятельно рекомендую вам следовать этому руководству , в котором показано, как выполнить аутентификацию на основе токенов с помощью Web API 2 с нуля (используя Angular JS в качестве front-end). В частности, шаг 4 включает в себя два метода, которые позволяют выполнять аутентификацию с использованием токена внешнего доступа, например, как возвращено из собственного SDK:

 [AllowAnonymous, HttpGet] async Task ObtainLocalAccessToken(string provider, string externalAccessToken) [AllowAnonymous, HttpPost] async Task RegisterExternal(RegisterExternalBindingModel model) 

В двух словах:

  1. Используйте собственный SDK для получения токена внешнего доступа.

  2. Вызовите ObtainLocalAccessToken("Facebook", "[fb-access-token]") чтобы определить, имеет ли пользователь уже учетную запись (ответ 200), и в этом случае для вас будет создан новый локальный токен . Он также проверяет, что токен внешнего доступа является законным.

  3. Если вызов на шаге 2 не удался (400 ответов), вам необходимо зарегистрировать новую учетную запись, вызвав RegisterExternal , передав внешний токен. В приведенном выше руководстве есть хороший пример этого (см. AssociateController.js ).

  • Доступ к электронной почте пользователя с помощью нового Android Android SDK
  • Загрузить фото в Facebook с Android Android SDK в Android
  • Давайте будем гением компьютера.