Google的间歇性ASP.NET oAuth问题,AuthenticationManager.GetExternalIdentityAsync返回null

时间:2014-05-12 16:16:40

标签: c# asp.net-mvc-5 google-oauth oauth-provider

在使用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);
    }
}

这也是here

这是一个非常间歇性的问题,重新部署应用程序通常会让它暂时起作用。

在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中将身份验证模式设置为non,并删除了表单身份验证模块。

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

这些网站托管在Azure上,有些在1个实例上运行,有些运行在2个。 他们有自定义域,但在自定义域和azurewebsites域以及http / https上仍然失败。

任何人都可以帮忙解决为什么会这样吗?

更新

Microsoft.Owin.Security.Google 3.0版于昨晚发布。要切换,看看是否能解决问题。

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

7 个答案:

答案 0 :(得分:2)

我忘了在google开发者控制台中启用“Google + API”。 Google登录似乎没问题,但GetExternalLoginInfoAsync返回null。

您可以点按此链接 https://stackoverflow.com/a/27631109/657926

答案 1 :(得分:2)

Microsoft的System.Web Owin实现中存在一个错误。在IIS上运行Owin应用程序时使用的那个。如果我们在ASP.NET MVC5中使用基于Owin的新身份验证处理,那么我们99%的人可能会这样做。

臭虫使Owin设置的cookie在某些情况下神秘地消失。

之前将此nuget放在https://github.com/KentorIT/owin-cookie-saver之前 app.UseGoogleAuthentication(...)

答案 2 :(得分:1)

Tom我使用REST API在我的asp.net应用程序中使用google-oauth。它工作正常,我没有遇到任何连接问题。

我正在执行以下步骤:

1.我在谷歌开发者控制台中创建了一个项目,因为我创建了“Web应用程序的客户端ID”设置,其中包含以下参数。

a)客户ID =&gt;它将由谷歌自动生成 b)电子邮件地址=&gt;它将由谷歌自动生成 c)客户秘密=&gt;它将由谷歌自动生成 d)重定向URI =&gt;需要指定将用于处理身份验证过程的网页的URL。在此页面中,我们可以进行身份​​验证,我们可以获取用户的基本信息。

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

我的用法:

  1. 我创建了一个示例项目,其中包含“Webpage1.aspx”和“Webpage2.aspx”。
  2. 我设置了“Webpage2.aspx”启动页面,我在“Webpage2.aspx”中构建了开放的auth url并重定向到google登录页面。

    Google Open Auth url Formation

    登录后,它将重定向到“Webpage1.aspx”以及访问代码。通过将此访问代码传递给“https://accounts.google.com/o/oauth2/token”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 :(得分:1)

我相信你不应该使用app.UseGoogleAuthentication();因为这是一个试图使用OpenID 2.0的电话has been deprecated。 您应该使用的是OAuth 2.0 for Login (OpenID Connect) 所以:

  1. 在Google Developers Console中注册您的应用
  2. 允许其访问Google+ API(即使您不打算直接使用Google+ - 它现在用作身份验证的手段)
  3. 以这种方式启用ASP.NET Identity的Google身份验证
  4. app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()  
    {  
        ClientId = "YOUR_CLIENT_ID",  
        ClientSecret = "YOUR_CLIENT_SECRET",  
    });
    

答案 4 :(得分:1)

我有同样的问题。我使用的是Visual Studio 2013,网站是在Azure上。社交日志一直没有问题停止工作,LinkLoginCallback在loginInfo中收到null。我重新发布项目没有代码更改或重建,然后loginInfo收到正确的数据,一切正常。没有意义,但你去了。

答案 5 :(得分:1)

昨天我遇到了这个问题而且我完全被抢走了! 而且,正如你所描述的那样,我毫无理由地得到了它。

我设法修复它(经过几个小时的比较2个项目 - 一个测试项目每次都没有问题,另一个是一个更严肃的项目 - 他们有完全相同的代码,但不同的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 :(得分:0)

确保已启用第三方Cookie。我发现如果您在尝试“注册”时没有登录谷歌。如果用户使用您的应用程序,它会重定向到登录页面,因为它会查找不存在的cookie,但仍然可以通过外部提供程序完成所需的操作。下次您尝试注册时,由于它已完成部分流程,因此不再需要查找外部cookie,因此第二次成功。