2015-09-20 4 views
8

Я только что выполнил инструкции в this article, чтобы добавить Google в качестве провайдера входа в мое приложение MVC 5. Кажется, все работает нормально, но когда я вхожу в систему через Google, он хочет, чтобы я зарегистрировал адрес электронной почты/имя пользователя, предоставленный Google в качестве новой учетной записи в своем приложении. Если я останусь по электронной почте, как есть, и нажмите кнопку «Зарегистрироваться», она сообщит мне, что адрес уже сделан, так как я ранее зарегистрировался у своего собственного провайдера входа в свой приложение.Связать новый логин Google или Facebook с существующей учетной записью

Как я могу настроить код по умолчанию, сгенерированный шаблоном проекта MVC, чтобы я мог связать логин Google с существующей локальной учетной записью?

P.S. У меня точно такая же проблема с Facebook.

ответ

4

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

В результате Identity позволяет создавать новую учетную запись с внешним логином при входе в систему, а не при подключении к существующему. Однако, как только пользователь аутентифицируется другими способами, предоставляются методы для связывания дополнительных логинов.

Если вы не обеспокоены относительно умеренным риском безопасности, связанной с только при условии, что если электронная почта соответствует это тот же человек, то вам нужно изменить только ExternalLoginCallback в AccountController.cs попытаться найти пользователя по электронной почте:

var user = await UserManager.FindByEmailAsync(loginInfo.Email); 

А потом подписать их в:

await SignInManager.SignInAsync(user); 
0

TLDR вы должны пройти через все сценарии вручную в вашей ExternalLoginConfirmation функции, и есть таблица базы данных, чтобы иметь возможность соответствовать членской идентификатор пользователя с OAuth идентификатором пользователя. Таким образом, вы можете «связывать» несколько учетных записей OAuth с одной локальной учетной записью.

Ниже приведен фрагмент кода из одного из наших проектов - мы надеемся, что это достаточно ясно

public ActionResult ExternalLoginCallback() 
{ 
    var returnUrl = HttpContext.Request.QueryString["returnUrl"]; 

    var result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl })); 
    if (result.IsSuccessful == false) 
    { 
     return this.View("ExternalLoginFailure", result); 
    } 

    // Login user if provider represents a valid already registered user 
    if (OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false)) 
    { 
     return this.RedirectToLocal(returnUrl); 
    } 

    // If the current user is logged in already - add new account 
    if (User.Identity.IsAuthenticated) 
    { 
     OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, User.Identity.Name); 
     return this.RedirectToLocal(returnUrl); 
    } 

    var membershipUser = Membership.GetUser(result.UserName); 

    // so user is new - then create new membership account 
    if (membershipUser == null) 
    { 
     MembershipCreateStatus createStatus; 
     membershipUser = Membership.CreateUser(username: result.UserName, password: this.GetTempPassword(), email: result.UserName, status: out createStatus); 
     if (createStatus == MembershipCreateStatus.Success) 
     { 
      this.emailService.SendWelcome(this, (Guid)membershipUser.ProviderUserKey); 

      // Associate social network account with created membership account 
      OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, result.UserName); 
      OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false); 

      return this.RedirectToLocal(returnUrl); 
     } 

     // The problem occured while creating membership account 
     this.ViewBag.Error = MembershipErrorNameProvider.FromErrorCode(createStatus); 
     return this.View("CreateMembershipAccountFailure"); 
    } 

    // If membership account already exists -> Associate Social network account with exists membership account 
    OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, result.UserName); 
    OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false); 

    return this.RedirectToLocal(returnUrl); 
} 

и OAuthWebSecurity является вспомогательным классом, который занимается всеми провайдерами вы поддерживаете:

public static class OAuthWebSecurity 
{ 
     .... 

    public static bool Login(string providerName, string providerUserId, bool createPersistentCookie) 
    { 
     var context = new HttpContextWrapper(HttpContext.Current); 
     var provider = GetOAuthClient(providerName); 
     var securityManager = new OpenAuthSecurityManager(context, provider, OAuthDataProvider); 
     return securityManager.Login(providerUserId, createPersistentCookie); 
    } 

    public static void CreateOrUpdateAccount(string openAuthProvider, string openAuthId, string userName) 
    { 
     var user = UserRepository.FindByName(userName); 
     if (user == null) 
     { 
      throw new MembershipUserNotFoundException(); 
     } 

     var userOAuthAccount = UserOAuthAccountRepository.Find(openAuthProvider, openAuthId); 
     if (userOAuthAccount == null) 
     { 
      UserOAuthAccountRepository.InsertOrUpdate(new UserOAuthAccount 
      { 
       OAuthProvider = openAuthProvider, 
       OAuthId = openAuthId, 
       UserId = user.Id 
      }); 
     } 
     else 
     { 
      userOAuthAccount.UserId = user.Id; 
     } 

     UserOAuthAccountRepository.Save(); 
    } 
} 
+0

Разве это не боль в прикладе, что MS играла это так упрощенно, даже с небольшим упоминанием о существующих пользователях? А для новых пользователей вместо любых регистрационных данных, которые вы обычно собираете, MS default просто получает имя пользователя/адрес электронной почты. Он должен перенаправляться на «Регистрация» для новых пользователей. – ProfK

+0

хорошо, каждый вариант использования отличается - я предпочитаю думать, что они создали структуру, которую вы относительно легко распространяете на свои нужды :) – avs099

1

Это нормальное поведение в шаблоне MVC, так как внешние логины пытаются создать пользователя, и если пользовательская электронная почта (или внешняя идентификация) уже существует, попытайтесь зарегистрироваться. Кроме того, поставщик внешнего входа пытается назначить дополнительный и уникальный идентификатор в вашем приложении (локально) отдельно от вашей внешней идентификации. Но следующий странно, как вы сказали:

Если я оставлю письмо как и нажмите кнопку «Регистрация», он говорит мне этот адрес уже занят, как я уже ранее зарегистрирован мой собственный провайдер входа в приложение.

который должен работать, так как внешний идентификатор каждого пользователя должен быть уникальным в других местах (я считаю), если вы не зарегистрированы несколько внешних счетов с одной и той же личности, поскольку таблица структуры выглядит следующим образом:

enter image description here

в столбце UserId будет согласован с UserId колонке сильфона таблицы:

enter image description here

Сообщение будет отображаться редко, когда пользователь пытается присвоить дубликат Username, который будет указан с вашей электронной почтой или идентификационным номером на других сайтах (например, [email protected] будет SomeName в Username)

+0

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

+0

@ProfK. Мой смысл относится к этому примеру: у меня есть два адреса электронной почты [email protected] и [email protected], и что произойдет в этом случае? –

+0

Тогда, если у вас есть локальные учетные записи для обоих, после того, как вы используете сторонний аккаунт с одним, вы также входите в систему локально и сопоставляете сторонний токен с зарегистрированным адресом. – ProfK

-1

Это следующая ссылка показывает, как построить ASP.NET MVC 5 веб-приложение, которое позволяет пользователям войти в систему с помощью OAuth 2.0 с учетными данными от внешнего поставщика проверки подлинности, таких как Facebook, Twitter, LinkedIn, Microsoft или Google. Для простоты в этом руководстве основное внимание уделяется работе с учетными данными из аккаунтов Facebook и Google.

см это:

MVC 5 App with Facebook, Twitter, LinkedIn and Google OAuth2 Sign-on (C#)

+1

У меня уже есть логины через Facebook и Google, спасибо, но это не то, о чем я прошу. Если вы следуете связанным учебным пособиям, вы можете столкнуться с той же проблемой, о которой я действительно спрашиваю. – ProfK

7

Я полностью второй вопросы, поднятые Pratt @ Крис Однако я не уверен, что код, используемый достаточно, чтобы делать то, что спросил ОП.

добавить этот код в блоке по умолчанию внутри ExternalLoginCallback должен сделать работу

ApplicationUser user = await UserManager.FindByNameAsync(loginInfo.Email); 
if (user != null) 
{ 
    var addLoginResult = await UserManager.AddLoginAsync(user.Id, loginInfo.Login); 
    if (addLoginResult.Succeeded) 
    { 
     await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); 
     return RedirectToLocal(returnUrl); 
    } 
} 
2

Вот как я был в состоянии решить эту проблему, что вы имеете. Это решение позволит вам зарегистрироваться на сайте с вашей электронной почтой, и если вы затем попытаетесь использовать Google для входа в систему с тем же адресом электронной почты, вам не будет предложено зарегистрироваться, и вы не будете генерировать никаких ошибок; вам будет разрешен вход в систему, если адрес электронной почты, с которым вы вошли в систему, совпадает с адресом электронной почты вашей учетной записи google. Я отредактировал код ExternalLoginCallBack по умолчанию, который VS2015 сгенерирован с помощью инструкции if/else, которая проверяет наличие существующего письма, которое соответствует электронной почте для входа. Надеюсь, это поможет вам с вашим вопросом, потому что у меня была такая же проблема, и я не мог найти ответ нигде. Мои многочисленные запросы на почту были проигнорированы, и, к счастью, я прочитал один из ответов с этой статьи, который привел меня к моему собственному решению, которое работает для меня на VS2015 Core.

[HttpGet] 
[AllowAnonymous] 
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null) 
{  

    if (remoteError != null) 
    { 
     ModelState.AddModelError(string.Empty, $"Error from external provider: {remoteError}"); 
     return View(nameof(Login)); 
    } 
    var info = await _signInManager.GetExternalLoginInfoAsync(); 
    if (info == null) 
    { 
     return RedirectToAction(nameof(Login)); 
    }    

    // Sign in the user with this external login provider if the user already has a login. 
    var email = info.Principal.FindFirstValue(ClaimTypes.Email); 
    var user = await _userManager.FindByNameAsync(email); 
    if (user != null) 
    { 
     await _signInManager.SignInAsync(user, isPersistent: false); 
     return RedirectToLocal(returnUrl); 
    } 
    else 
    { 
     // If user does not already exists, invite User to register. 
     var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false); 
     if (result.Succeeded) 
     { 
      _logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider); 
      return RedirectToLocal(returnUrl); 
     } 

     if (result.RequiresTwoFactor) 
     { 
      return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl }); 
     } 

     if (result.IsLockedOut) 
     { 
      return View("Lockout"); 
     } 
     else 
     { 
      // If the user does not have an account, then ask the user to create an account. 
      ViewData["ReturnUrl"] = returnUrl; 
      ViewData["LoginProvider"] = info.LoginProvider; 
      email = info.Principal.FindFirstValue(ClaimTypes.Email); 
      return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email }); 
     } 
    } 
} 
+2

Спасибо. Я изменил свой ответ и разместил решение вместо этого. – Johnny

Смежные вопросы