Подтвердить что ты не робот

Прерывистая проблема ASP.NET oAuth с Google, AuthenticationManager.GetExternalIdentityAsync возвращает значение null

Я пытаюсь устранить прерывистую проблему при использовании Google в качестве внешнего поставщика входа.

При попытке входа в систему пользователь перенаправляется обратно на страницу входа, а не на проверку подлинности.

Проблема возникает в этой строке (строка 55 ссылки ниже), GetExternalIdentityAsync возвращает null.

var externalIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);

Полный код:

[Authorize]
public abstract class GoogleAccountController<TUser> : Controller where TUser : Microsoft.AspNet.Identity.IUser
{
    public IAuthenticationManager AuthenticationManager
    {
        get
        {
            return HttpContext.GetOwinContext().Authentication;
        }
    }

    public abstract UserManager<TUser> UserManager { get; set; }

    [AllowAnonymous]
    [HttpGet]
    [Route("login")]
    public ActionResult Login(string returnUrl)
    {
        ViewData.Model = new LoginModel()
        {
            Message = TempData["message"] as string,
            Providers = HttpContext.GetOwinContext().Authentication.GetExternalAuthenticationTypes(),
            ReturnUrl = returnUrl
        };

        return View();
    }

    [AllowAnonymous]
    [HttpPost]
    [ValidateAntiForgeryToken]
    [Route("login")]
    public ActionResult Login(string provider, string returnUrl)
    {
        return new ChallengeResult(provider, Url.Action("Callback", "Account", new { ReturnUrl = returnUrl }));
    }

    [AllowAnonymous]
    [Route("authenticate")]
    public async Task<ActionResult> Callback(string returnUrl)
    {
        var externalIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);

        if (externalIdentity == null)
        {
            return RedirectToAction("Login", new { ReturnUrl = returnUrl });
        }

        var emailAddress = externalIdentity.FindFirstValue(ClaimTypes.Email);
        var user = await UserManager.FindByNameAsync(emailAddress);

        if (user != null)
        {
            await SignInAsync(user, false);

            return RedirectToLocal(returnUrl);
        }
        else
        {
            TempData.Add("message", string.Format("The account {0} is not approved.", emailAddress));

            return RedirectToAction("Login", new { ReturnUrl = returnUrl });
        }
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    [Route("logout")]
    public ActionResult Logout(string returnUrl)
    {
        AuthenticationManager.SignOut();

        return RedirectToLocal(returnUrl);
    }

    private async Task SignInAsync(TUser user, bool isPersistent)
    {
        AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);

        var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
        var authenticationProperties = new AuthenticationProperties()
        {
            IsPersistent = isPersistent
        };

        AuthenticationManager.SignIn(authenticationProperties, identity);
    }

    private ActionResult RedirectToLocal(string returnUrl)
    {
        if (Url.IsLocalUrl(returnUrl))
        {
            return Redirect(returnUrl);
        }
        else
        {
            return RedirectToAction("Index", "Home");
        }
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing && UserManager != null)
        {
            UserManager.Dispose();
            UserManager = null;
        }

        base.Dispose(disposing);
    }
}

Что также здесь.

Это очень непростая проблема, и повторное развертывание приложения часто заставит его работать временно.

Глядя в Fiddler, я вижу, что вызов выполняется для подписывания google перед предыдущим методом проверки подлинности, в котором он не может найти файл cookie.

Fiddler screenshot

Приложение использует следующий код для инициализации входа в google

app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString("/login")
    });
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseGoogleAuthentication();

Я установил режим проверки подлинности не в файл web.config и удалил модуль проверки форм.

<system.web>
    <authentication mode="None" />
</system.web>    
<system.webServer>
    <validation validateIntegratedModeConfiguration="false" />    
    <modules runAllManagedModulesForAllRequests="true">
      <remove name="FormsAuthenticationModule" />
    </modules>
</system.webServer>

Сайты размещены на Azure, некоторые из них работают на одном экземпляре, а некоторые - на 2. У них есть пользовательские домены, хотя они по-прежнему терпят неудачу как в домене пользовательского домена, так и в домене azurewebsites и http/https.

Может ли кто-нибудь помочь, почему это может произойти?

Обновление

Версия 3.0 Microsoft.Owin.Security.Google была выпущена прошлой ночью. Переходим к переходу и видим, устраняет ли это проблему.

https://www.nuget.org/packages/Microsoft.Owin.Security.Google

4b9b3361

Ответ 1

Я забыл включить "Google + API" в консоли разработчика Google. Вход в Google выглядит нормально, но GetExternalLoginInfoAsync возвращает null.

Вы можете перейти по этой ссылке fooobar.com/questions/239028/...

Ответ 2

Том. Я использую google-oauth в моем приложении asp.net с помощью REST API. он работает нормально, и я не сталкиваюсь с проблемами связи.

Следующие шаги, которые я выполняю:

1. Я создал один проект в консоли разработчика Google, в котором я создал настройки "Идентификатор клиента для веб-приложения", который будет содержать следующие параметры.

a) Идентификатор клиента = > Он будет автоматически сгенерирован Google b) Адрес электронной почты = > Он будет автоматически сгенерирован Google c) Клиентский секрет = > Он будет автоматически сгенерирован Google d) Перенаправить URIs = > Необходимо указать URL-адрес веб-страницы, который будет использоваться для обработки процесса аутентификации. На этой странице мы можем аутентифицироваться, и мы можем получить основную информацию пользователя.

my url: "http://localhost:1822/WebForm1.aspx/code"

Мое использование:

  • Я создал один образец проекта, который будет содержать "Webpage1.aspx" и "Webpage2.aspx" .

Я установил стартовую страницу "Webpage2.aspx" , и я формирую открытый auth url в "Webpage2.aspx" и перенаправляю на страницу входа в google.

Google Open Auth url Formation

После входа в систему он будет перенаправлен на "Webpage1.aspx" вместе с кодом доступа. Пропустив этот код доступа на " https://accounts.google.com/o/oauth2/token" url, я получаю токен доступа вместе с типом токена и временем истечения маркера. После этого, передав этот доступ к URL-адресу https://www.googleapis.com/oauth2/v2/userinfo, я получаю базовую информацию пользователя, такую ​​как "электронная почта, имя, пол, фотография", и т.д.)

Пример кода

    public class GoogleAuthorizationData
    {
        public string access_token { get; set; }
        public int expires_in { get; set; }
        public string token_type { get; set; }

    }

  public class GoogleUserInfo
    {
        public string name { get; set; }
        public string family_name { get; set; }
        public string gender { get; set; }
        public string email { get; set; }
        public string given_name { get; set; }
        public string picture { get; set; }
        public string link { get; set; }
        public string id { get; set; }

    }

  Webpage1.aspx
  ============
 protected void Page_Load(object sender, EventArgs e)
        {
            string code = Request.QueryString["code"].ToString();
            string scope = Request.QueryString["scope"].ToString();
            string url = "https://accounts.google.com/o/oauth2/token";
            string postString = "code=" + code + "&client_id=" + ConfigurationManager.AppSettings["GoogleClientID"].ToString() + "&client_secret=" + ConfigurationManager.AppSettings["GoogleSecretKey"].ToString() + "&redirect_uri=" + ConfigurationManager.AppSettings["ResponseUrl"].ToString() + "&grant_type=authorization_code";

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url.ToString());
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";

            UTF8Encoding utfenc = new UTF8Encoding();
            byte[] bytes = utfenc.GetBytes(postString);
            Stream os = null;
            try
            {
                request.ContentLength = bytes.Length;
                os = request.GetRequestStream();
                os.Write(bytes, 0, bytes.Length);
            }
            catch
            { }

            try
            {
                HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse();
                Stream responseStream = webResponse.GetResponseStream();
                StreamReader responseStreamReader = new StreamReader(responseStream);
                var result = responseStreamReader.ReadToEnd();//
                var json = new JavaScriptSerializer();

                GoogleAuthorizationData authData = json.Deserialize<GoogleAuthorizationData>(result);

                HttpWebRequest request1 = (HttpWebRequest)WebRequest.Create("https://www.googleapis.com/oauth2/v2/userinfo");
                request1.Method = "GET";
                request1.ContentLength = 0;
                request1.Headers.Add("Authorization", string.Format("{0} {1}", authData.token_type, authData.access_token));
                HttpWebResponse webResponse1 = (HttpWebResponse)request1.GetResponse();
                Stream responseStream1 = webResponse1.GetResponseStream();
                StreamReader responseStreamReader1 = new StreamReader(responseStream1);
                GoogleUserInfo userinfo = json.Deserialize<GoogleUserInfo>(responseStreamReader1.ReadToEnd());
               Response.Write(userinfo.email);

            }
            catch (Exception eX)
            {
                throw eX;
            }





        }

Ответ 3

Я считаю, что вы не должны использовать app.UseGoogleAuthentication(); как вызов, который попытается использовать OpenID 2.0, который устарел.
Вместо этого вы должны использовать OAuth 2.0 для входа (OpenID Connect).
Итак:

  • зарегистрируйте свое приложение в Google Developers Console.
  • позволяет ему получить доступ к API Google+ (даже если вы не намерены напрямую использовать Google+ - теперь он используется как средство аутентификации).
  • включить аутентификацию ASP.NET Identity Google таким образом
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()  
{  
    ClientId = "YOUR_CLIENT_ID",  
    ClientSecret = "YOUR_CLIENT_SECRET",  
});

Ответ 4

У меня та же проблема. Я использую Visual Studio 2013, а веб-сайт находится на Azure. Социальный журнал, который работал без проблем, прекратил работу, и LinkLoginCallback получал null в loginInfo. Я переиздал проект без изменения кода или перестройки, а затем loginInfo получил правильные данные, и все работает нормально. Не имеет смысла, но вы идете.

Ответ 5

У меня появилась эта проблема вчера, и я ПОЛНОСТЬЮ СТУК! И, как вы описали, я получил это просто так без причины.

Мне удалось это исправить (после нескольких часов сравнения двух проектов - 1 тестовый проект, который каждый раз работал без проблем, а другой был более серьезным проектом - и у них был тот же самый код, но разные версии dll)

Проблема связана с DLL - ссылочными пакетами от Nuget. Убедитесь, что у вас есть последние пакеты, а также проверьте время выполнения в web.config.

После того, как я обновил все связанные с Owin пакеты и Microsoft.Owin и добавил:

<assemblyBinding>
  <dependentAssembly>
    <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
    <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
  </dependentAssembly>
  <dependentAssembly>
    <assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
    <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
  </dependentAssembly>
  <dependentAssembly>
    <assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
    <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
  </dependentAssembly>
  <dependentAssembly>
    <assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
    <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
  </dependentAssembly>
</assemblyBinding>

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

Ответ 6

В Microsoft Owin есть ошибка в System.Web. Тот, который используется при запуске приложений Owin в IIS. Это, вероятно, 99% из нас, если мы используем новую проверку подлинности на основе Owin с помощью ASP.NET MVC5.

Ошибка в том, что cookie, установленный Owin, загадочно исчезает в некоторых случаях.

Поместите этот nuget перед https://github.com/KentorIT/owin-cookie-saver перед app.UseGoogleAuthentication(...)

Ответ 7

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