diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs index b542c2ef8..8b9c4683c 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs @@ -12,6 +12,7 @@ using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Authentication; using Microsoft.AspNet.Http.Features.Authentication; +using Microsoft.Framework.Caching.Distributed; using Microsoft.Framework.Internal; using Microsoft.Framework.Logging; using Microsoft.IdentityModel.Protocols; @@ -149,13 +150,18 @@ protected override async Task HandleUnauthorizedAsync([NotNull] ChallengeC if (Options.ProtocolValidator.RequireNonce) { message.Nonce = Options.ProtocolValidator.GenerateNonce(); - if (Options.NonceCache != null) + if (Options.CacheNonces) { - if (!Options.NonceCache.TryAddNonce(message.Nonce)) + if (await Options.NonceCache.GetAsync(message.Nonce) != null) { - Logger.LogError(Resources.OIDCH_0033_TryAddNonceFailed, message.Nonce); - throw new OpenIdConnectProtocolException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0033_TryAddNonceFailed, message.Nonce)); + Logger.LogError(Resources.OIDCH_0033_NonceAlreadyExists, message.Nonce); + throw new OpenIdConnectProtocolException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0033_NonceAlreadyExists, message.Nonce)); } + + await Options.NonceCache.SetAsync(message.Nonce, new byte[0], new DistributedCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = Options.ProtocolValidator.NonceLifetime + }); } else { @@ -389,11 +395,16 @@ public override async Task AuthenticateAsync() } string nonce = jwt.Payload.Nonce; - if (Options.NonceCache != null) + if (Options.CacheNonces) { - // if the nonce cannot be removed, it was used - if (!Options.NonceCache.TryRemoveNonce(nonce)) + if (await Options.NonceCache.GetAsync(nonce) != null) + { + await Options.NonceCache.RemoveAsync(nonce); + } + else { + // If the nonce cannot be removed, it was + // already used and MUST be rejected. nonce = null; } } diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs index 9353f385c..532ca9518 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs @@ -13,11 +13,13 @@ using Microsoft.AspNet.Builder; using Microsoft.AspNet.DataProtection; using Microsoft.AspNet.Http; +using Microsoft.Framework.Caching.Distributed; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.Internal; using Microsoft.Framework.Logging; using Microsoft.Framework.OptionsModel; -using Microsoft.IdentityModel.Protocols; -using Microsoft.Framework.Internal; using Microsoft.Framework.WebEncoders; +using Microsoft.IdentityModel.Protocols; namespace Microsoft.AspNet.Authentication.OpenIdConnect { @@ -42,6 +44,7 @@ public OpenIdConnectAuthenticationMiddleware( [NotNull] IDataProtectionProvider dataProtectionProvider, [NotNull] ILoggerFactory loggerFactory, [NotNull] IUrlEncoder encoder, + [NotNull] IServiceProvider services, [NotNull] IOptions externalOptions, [NotNull] IOptions options, ConfigureOptions configureOptions = null) @@ -125,6 +128,13 @@ public OpenIdConnectAuthenticationMiddleware( Options.ConfigurationManager = new ConfigurationManager(Options.MetadataAddress, httpClient); } } + + if (Options.CacheNonces && Options.NonceCache == null) + { + // Use the global distributed cache if the user has not provided his own instance. + // Note: GetRequiredService will throw an exception if caching services have not been registered. + Options.NonceCache = services.GetRequiredService(); + } } /// diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs index 8e78a5b09..05b33548c 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs @@ -8,6 +8,7 @@ using System.Net.Http; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Authentication; +using Microsoft.Framework.Caching.Distributed; using Microsoft.Framework.Internal; using Microsoft.IdentityModel.Protocols; @@ -173,7 +174,13 @@ public string Caption /// recommends adding a nonce to a request as a mitigation against replay attacks when requesting id_tokens. /// By default the runtime uses cookies with unique names generated from a hash of the nonce. /// - public INonceCache NonceCache { get; set; } + public IDistributedCache NonceCache { get; set; } + + /// + /// Gets or sets the value indicating whether nonces should be stored in the distributed cache or not. + /// The default value, false, is used to store nonces in client cookies. + /// + public bool CacheNonces { get; set; } /// /// Gets or sets the to notify when processing OpenIdConnect messages. diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs index b95de8318..c999f5999 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs @@ -141,11 +141,11 @@ internal static string OIDCH_0032_UsingCurrentUriRedirectUri } /// - /// OIDCH_0033: ProtocolValidator.RequireNonce == true. Options.NonceCache.TryAddNonce returned false. This usually indicates the nonce is not unique or has been used. The nonce is: '{0}'. + /// OIDCH_0033: ProtocolValidator.RequireNonce == true. The generated nonce already exists: this usually indicates the nonce is not unique or has been used. The nonce is: '{0}'. /// - internal static string OIDCH_0033_TryAddNonceFailed + internal static string OIDCH_0033_NonceAlreadyExists { - get { return ResourceManager.GetString("OIDCH_0033_TryAddNonceFailed"); } + get { return ResourceManager.GetString("OIDCH_0033_NonceAlreadyExists"); } } /// diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx index 454dae209..3f2ad6022 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx @@ -147,8 +147,8 @@ OIDCH_0032: using the CurrentUri for 'local redirect' post authentication: '{0}'. - - OIDCH_0033: ProtocolValidator.RequireNonce == true. Options.NonceCache.TryAddNonce returned false. This usually indicates the nonce is not unique or has been used. The nonce is: '{0}'. + + OIDCH_0033: ProtocolValidator.RequireNonce == true. The generated nonce already exists: this usually indicates the nonce is not unique or has been used. The nonce is: '{0}'. OIDCH_0034: redirectToIdentityProviderNotification.HandledResponse @@ -222,4 +222,4 @@ OIDCH_0020: 'id_token' received: '{0}' - + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json b/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json index 0bf9f2d9e..0c00d7e42 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json @@ -3,6 +3,7 @@ "description": "ASP.NET 5 middleware that enables an application to support OpenIdConnect authentication workflow.", "dependencies": { "Microsoft.AspNet.Authentication": "1.0.0-*", + "Microsoft.Framework.Caching.Abstractions": "1.0.0-*", "Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" }, "Microsoft.IdentityModel.Protocol.Extensions": "2.0.0-beta5-*" }, diff --git a/src/Microsoft.AspNet.Authentication/AuthenticationServiceCollectionExtensions.cs b/src/Microsoft.AspNet.Authentication/AuthenticationServiceCollectionExtensions.cs index fb6232354..a2b198ca3 100644 --- a/src/Microsoft.AspNet.Authentication/AuthenticationServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNet.Authentication/AuthenticationServiceCollectionExtensions.cs @@ -32,6 +32,5 @@ public static IServiceCollection ConfigureClaimsTransformation([NotNull] this IS { return services.Configure(o => o.Transformer = new ClaimsTransformer { TransformAsyncDelegate = asyncTransform }); } - } } diff --git a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs index 13357a162..8896767ee 100644 --- a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs +++ b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs @@ -437,6 +437,7 @@ private static TestServer CreateServer(IOptions { + services.AddAuthentication(); services.AddWebEncoders(); services.AddDataProtection(); } @@ -456,6 +457,7 @@ private static TestServer CreateServer(CustomConfigureOptions configureOptions, }, services => { + services.AddAuthentication(); services.AddWebEncoders(); services.AddDataProtection(); } @@ -571,11 +573,12 @@ public CustomOpenIdConnectAuthenticationMiddleware( IDataProtectionProvider dataProtectionProvider, ILoggerFactory loggerFactory, IUrlEncoder encoder, + IServiceProvider services, IOptions externalOptions, IOptions options, ConfigureOptions configureOptions = null ) - : base(next, dataProtectionProvider, loggerFactory, encoder, externalOptions, options, configureOptions) + : base(next, dataProtectionProvider, loggerFactory, encoder, services, externalOptions, options, configureOptions) { Logger = (loggerFactory as CustomLoggerFactory).Logger; }