From 289b18045bb8247598aea7f053ac34a764cad390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Sun, 3 Jul 2016 14:20:00 +0200 Subject: [PATCH] Replace ISession by IDistributedCache and extend request_id handling to GET requests --- samples/Mvc.Server/Startup.cs | 4 -- samples/Mvc.Server/project.json | 1 - .../OpenIddictProvider.Authentication.cs | 70 ++++++------------- src/OpenIddict.Core/OpenIddictExtensions.cs | 5 ++ src/OpenIddict.Core/OpenIddictOptions.cs | 7 ++ 5 files changed, 34 insertions(+), 53 deletions(-) diff --git a/samples/Mvc.Server/Startup.cs b/samples/Mvc.Server/Startup.cs index c451e50d..7388fb78 100644 --- a/samples/Mvc.Server/Startup.cs +++ b/samples/Mvc.Server/Startup.cs @@ -21,8 +21,6 @@ namespace Mvc.Server { services.AddMvc(); - services.AddSession(); - services.AddDbContext(options => options.UseSqlServer(configuration["Data:DefaultConnection:ConnectionString"])); @@ -117,8 +115,6 @@ namespace Mvc.Server { ConsumerSecret = "Il2eFzGIrYhz6BWjYhVXBPQSfZuS4xoHpSSyD9PI" }); - app.UseSession(); - app.UseOpenIddict(); app.UseMvcWithDefaultRoute(); diff --git a/samples/Mvc.Server/project.json b/samples/Mvc.Server/project.json index 630bc91a..44cc7ada 100644 --- a/samples/Mvc.Server/project.json +++ b/samples/Mvc.Server/project.json @@ -27,7 +27,6 @@ "Microsoft.AspNetCore.Mvc": "1.0.0", "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", - "Microsoft.AspNetCore.Session": "1.0.0", "Microsoft.AspNetCore.StaticFiles": "1.0.0", "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0", "Microsoft.Extensions.Configuration.CommandLine": "1.0.0", diff --git a/src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Authentication.cs b/src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Authentication.cs index ec85d001..61a60180 100644 --- a/src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Authentication.cs +++ b/src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Authentication.cs @@ -14,11 +14,9 @@ using AspNet.Security.OpenIdConnect.Server; using JetBrains.Annotations; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Authentication; -using Microsoft.AspNetCore.Http.Extensions; -using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -53,25 +51,9 @@ namespace OpenIddict.Infrastructure { } // If a request_id parameter can be found in the authorization request, - // restore the complete authorization request stored in the user session. + // restore the complete authorization request stored in the distributed cache. if (!string.IsNullOrEmpty(context.Request.GetRequestId())) { - // Ensure session support has been enabled for this request. - if (context.HttpContext.Features.Get() == null) { - services.Logger.LogError("The authorization request was rejected because the session middleware " + - "was not correctly registered. Session support must be enabled to allow " + - "processing authorization requests specifying a request_id parameter."); - - context.Reject( - error: OpenIdConnectConstants.Errors.InvalidRequest, - description: "The request_id parameter is not allowed by this authorization server."); - - return; - } - - // Load the session from the session store. - await context.HttpContext.Session.LoadAsync(); - - var payload = context.HttpContext.Session.Get(OpenIddictConstants.Environment.Request + context.Request.GetRequestId()); + var payload = await services.Options.Cache.GetAsync(OpenIddictConstants.Environment.Request + context.Request.GetRequestId()); if (payload == null) { services.Logger.LogError("The authorization request was rejected because an unknown " + "or invalid request_id parameter was specified."); @@ -317,37 +299,31 @@ namespace OpenIddict.Infrastructure { return; } - if (context.HttpContext.Request.Path == context.Options.AuthorizationEndpointPath && - string.Equals(context.HttpContext.Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) { - // Ensure session support has been enabled for this request. - if (context.HttpContext.Features.Get() == null) { - services.Logger.LogError("The authorization request was rejected because the session middleware " + - "was not correctly registered. Session support must be enabled to allow " + - "processing POST authorization requests."); - - context.Reject( - error: OpenIdConnectConstants.Errors.InvalidRequest, - description: "POST authorization requests are not allowed by this authorization server."); - - return; - } - + // If no request_id parameter can be found in the current request, assume the OpenID Connect request + // was not serialized yet and store the entire payload in the distributed cache to make it easier + // to flow across requests and internal/external authentication/registration workflows. + if (string.IsNullOrEmpty(context.Request.GetRequestId())) { // Generate a request identifier. Note: using a crypto-secure // random number generator is not necessary in this case. var identifier = Guid.NewGuid().ToString(); - // Load the session from the session store. - await context.HttpContext.Session.LoadAsync(); + var options = new DistributedCacheEntryOptions { + AbsoluteExpiration = context.Options.SystemClock.UtcNow + TimeSpan.FromMinutes(30), + SlidingExpiration = TimeSpan.FromMinutes(10) + }; - // Store the serialized authorization request parameters in the user session. - context.HttpContext.Session.Set(OpenIddictConstants.Environment.Request + identifier, context.Request.Export()); + // Store the serialized authorization request parameters in the distributed cache. + await services.Options.Cache.SetAsync(OpenIddictConstants.Environment.Request + identifier, context.Request.Export(), options); - // Add the request_id to the current URL. + // Create a new authorization request containing only the request_id parameter. var address = QueryHelpers.AddQueryString( - uri: context.HttpContext.Request.GetEncodedUrl(), + uri: context.Options.AuthorizationEndpointPath, name: OpenIdConnectConstants.Parameters.RequestId, value: identifier); context.HttpContext.Response.Redirect(address); + + // Mark the response as handled + // to skip the rest of the pipeline. context.HandleResponse(); return; @@ -356,15 +332,15 @@ namespace OpenIddict.Infrastructure { context.SkipToNextMiddleware(); } - public override Task ApplyAuthorizationResponse([NotNull] ApplyAuthorizationResponseContext context) { + public override async Task ApplyAuthorizationResponse([NotNull] ApplyAuthorizationResponseContext context) { var services = context.HttpContext.RequestServices.GetRequiredService>(); - // Remove the authorization request from the user session. + // Remove the authorization request from the distributed cache. if (!string.IsNullOrEmpty(context.Request.GetRequestId())) { // Note: the ApplyAuthorizationResponse event is called for both successful // and errored authorization responses but discrimination is not necessary here, - // as the authorization request must be removed from the user session in both cases. - context.HttpContext.Session.Remove(OpenIddictConstants.Environment.Request + context.Request.GetRequestId()); + // as the authorization request must be removed from the distributed cache in both cases. + await services.Options.Cache.RemoveAsync(OpenIddictConstants.Environment.Request + context.Request.GetRequestId()); } if (!string.IsNullOrEmpty(context.Response.Error) && services.Options.ErrorHandlingPath.HasValue) { @@ -380,8 +356,6 @@ namespace OpenIddict.Infrastructure { context.SkipToNextMiddleware(); } - - return Task.FromResult(0); } } } \ No newline at end of file diff --git a/src/OpenIddict.Core/OpenIddictExtensions.cs b/src/OpenIddict.Core/OpenIddictExtensions.cs index d90b9f5c..436605a8 100644 --- a/src/OpenIddict.Core/OpenIddictExtensions.cs +++ b/src/OpenIddict.Core/OpenIddictExtensions.cs @@ -7,6 +7,7 @@ using System; using System.Linq; using JetBrains.Annotations; +using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; @@ -87,6 +88,10 @@ namespace Microsoft.AspNetCore.Builder { // Resolve the OpenIddict options from the DI container. var options = app.ApplicationServices.GetRequiredService>().Value; + if (options.Cache == null) { + options.Cache = app.ApplicationServices.GetRequiredService(); + } + // Get the modules registered by the application // and add the OpenID Connect server middleware. var modules = options.Modules.ToList(); diff --git a/src/OpenIddict.Core/OpenIddictOptions.cs b/src/OpenIddict.Core/OpenIddictOptions.cs index 6c5fabac..eafc073f 100644 --- a/src/OpenIddict.Core/OpenIddictOptions.cs +++ b/src/OpenIddict.Core/OpenIddictOptions.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using AspNet.Security.OpenIdConnect.Server; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Caching.Distributed; namespace OpenIddict { /// @@ -23,6 +24,12 @@ namespace OpenIddict { AccessTokenLifetime = TimeSpan.FromMinutes(30); } + /// + /// Gets or sets the distributed cache used by OpenIddict. If no cache is explicitly + /// provided, the cache registered in the dependency injection container is used. + /// + public IDistributedCache Cache { get; set; } + /// /// Gets or sets the path of the middleware responsible of rendering /// the OpenID Connect errors occurred during interactive workflows.