diff --git a/src/Microsoft.Owin.Security.ActiveDirectory/WsFedCachingSecurityTokenProvider.cs b/src/Microsoft.Owin.Security.ActiveDirectory/WsFedCachingSecurityTokenProvider.cs index f7b00316..6039c9df 100644 --- a/src/Microsoft.Owin.Security.ActiveDirectory/WsFedCachingSecurityTokenProvider.cs +++ b/src/Microsoft.Owin.Security.ActiveDirectory/WsFedCachingSecurityTokenProvider.cs @@ -19,8 +19,6 @@ namespace Microsoft.Owin.Security.ActiveDirectory { private readonly TimeSpan _refreshInterval = new TimeSpan(1, 0, 0, 0); - private readonly ReaderWriterLockSlim _synclock = new ReaderWriterLockSlim(); - private readonly string _metadataEndpoint; private readonly TimeSpan _backchannelTimeout; @@ -63,16 +61,8 @@ namespace Microsoft.Owin.Security.ActiveDirectory { get { - RetrieveMetadata(); - _synclock.EnterReadLock(); - try - { - return _issuer; - } - finally - { - _synclock.ExitReadLock(); - } + RefreshMetadata(); + return _issuer; } } @@ -86,39 +76,40 @@ namespace Microsoft.Owin.Security.ActiveDirectory { get { - RetrieveMetadata(); - _synclock.EnterReadLock(); - try - { - return _tokens; - } - finally - { - _synclock.ExitReadLock(); - } + RefreshMetadata(); + return _tokens; } } - private void RetrieveMetadata() + private void RefreshMetadata() { if (_syncAfter >= DateTimeOffset.UtcNow) { return; } - _synclock.EnterWriteLock(); - try + // Queue a refresh, but discourage other threads from doing so. + _syncAfter = DateTimeOffset.UtcNow + _refreshInterval; + ThreadPool.UnsafeQueueUserWorkItem(state => { - IssuerSigningKeys metaData = WsFedMetadataRetriever.GetSigningKeys(_metadataEndpoint, - _backchannelTimeout, _backchannelHttpHandler); - _issuer = metaData.Issuer; - _tokens = metaData.Tokens; - _syncAfter = DateTimeOffset.UtcNow + _refreshInterval; - } - finally - { - _synclock.ExitWriteLock(); - } + try + { + RetrieveMetadata(); + } + catch (Exception) + { + // Don't throw exceptions on background threads. + } + }, state: null); + } + + private void RetrieveMetadata() + { + _syncAfter = DateTimeOffset.UtcNow + _refreshInterval; + IssuerSigningKeys metaData = WsFedMetadataRetriever.GetSigningKeys(_metadataEndpoint, + _backchannelTimeout, _backchannelHttpHandler); + _issuer = metaData.Issuer; + _tokens = metaData.Tokens; } } } diff --git a/src/Microsoft.Owin.Security.Google/GoogleOAuth2AuthenticationHandler.cs b/src/Microsoft.Owin.Security.Google/GoogleOAuth2AuthenticationHandler.cs index b0e24392..2b1e85aa 100644 --- a/src/Microsoft.Owin.Security.Google/GoogleOAuth2AuthenticationHandler.cs +++ b/src/Microsoft.Owin.Security.Google/GoogleOAuth2AuthenticationHandler.cs @@ -195,7 +195,9 @@ namespace Microsoft.Owin.Security.Google AddQueryString(queryStrings, properties, "access_type", Options.AccessType); AddQueryString(queryStrings, properties, "approval_prompt"); + AddQueryString(queryStrings, properties, "prompt"); AddQueryString(queryStrings, properties, "login_hint"); + AddQueryString(queryStrings, properties, "include_granted_scopes"); string state = Options.StateDataFormat.Protect(properties); queryStrings.Add("state", state); diff --git a/tests/Katana.Sandbox.WebServer/Startup.cs b/tests/Katana.Sandbox.WebServer/Startup.cs index 90c6ad59..bfbd67f2 100644 --- a/tests/Katana.Sandbox.WebServer/Startup.cs +++ b/tests/Katana.Sandbox.WebServer/Startup.cs @@ -214,7 +214,10 @@ namespace Katana.Sandbox.WebServer { map.Run(context => { - context.Authentication.Challenge(new AuthenticationProperties() { RedirectUri = "/" }, context.Request.Query["scheme"]); + var properties = new AuthenticationProperties(); + properties.RedirectUri = "/"; // Go back to the home page after authenticating. + properties.Dictionary["prompt"] = "select_account"; // Google + context.Authentication.Challenge(properties, context.Request.Query["scheme"]); return Task.FromResult(0); }); }); diff --git a/tests/Microsoft.Owin.Security.Tests/Google/GoogleOAuth2MiddlewareTests.cs b/tests/Microsoft.Owin.Security.Tests/Google/GoogleOAuth2MiddlewareTests.cs index e546266d..b3838a3d 100644 --- a/tests/Microsoft.Owin.Security.Tests/Google/GoogleOAuth2MiddlewareTests.cs +++ b/tests/Microsoft.Owin.Security.Tests/Google/GoogleOAuth2MiddlewareTests.cs @@ -47,7 +47,9 @@ namespace Microsoft.Owin.Security.Tests.Google location.ShouldNotContain("access_type="); location.ShouldNotContain("approval_prompt="); + location.ShouldNotContain("prompt="); location.ShouldNotContain("login_hint="); + location.ShouldNotContain("include_granted_scopes="); } [Fact] @@ -162,7 +164,9 @@ namespace Microsoft.Owin.Security.Tests.Google { "scope", "https://www.googleapis.com/auth/plus.login" }, { "access_type", "offline" }, { "approval_prompt", "force" }, - { "login_hint", "test@example.com" } + { "prompt", "consent" }, + { "login_hint", "test@example.com" }, + { "include_granted_scopes", "true" } }), "Google"); res.StatusCode = 401; } @@ -175,7 +179,9 @@ namespace Microsoft.Owin.Security.Tests.Google query.ShouldContain("scope=" + Uri.EscapeDataString("https://www.googleapis.com/auth/plus.login")); query.ShouldContain("access_type=offline"); query.ShouldContain("approval_prompt=force"); + query.ShouldContain("prompt=consent"); query.ShouldContain("login_hint=" + Uri.EscapeDataString("test@example.com")); + query.ShouldContain("include_granted_scopes=true"); } [Fact]