Browse Source

Replace ISession by IDistributedCache and extend request_id handling to GET requests

pull/152/head
Kévin Chalet 10 years ago
parent
commit
289b18045b
  1. 4
      samples/Mvc.Server/Startup.cs
  2. 1
      samples/Mvc.Server/project.json
  3. 70
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Authentication.cs
  4. 5
      src/OpenIddict.Core/OpenIddictExtensions.cs
  5. 7
      src/OpenIddict.Core/OpenIddictOptions.cs

4
samples/Mvc.Server/Startup.cs

@ -21,8 +21,6 @@ namespace Mvc.Server {
services.AddMvc(); services.AddMvc();
services.AddSession();
services.AddDbContext<ApplicationDbContext>(options => services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(configuration["Data:DefaultConnection:ConnectionString"])); options.UseSqlServer(configuration["Data:DefaultConnection:ConnectionString"]));
@ -117,8 +115,6 @@ namespace Mvc.Server {
ConsumerSecret = "Il2eFzGIrYhz6BWjYhVXBPQSfZuS4xoHpSSyD9PI" ConsumerSecret = "Il2eFzGIrYhz6BWjYhVXBPQSfZuS4xoHpSSyD9PI"
}); });
app.UseSession();
app.UseOpenIddict(); app.UseOpenIddict();
app.UseMvcWithDefaultRoute(); app.UseMvcWithDefaultRoute();

1
samples/Mvc.Server/project.json

@ -27,7 +27,6 @@
"Microsoft.AspNetCore.Mvc": "1.0.0", "Microsoft.AspNetCore.Mvc": "1.0.0",
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0", "Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
"Microsoft.AspNetCore.Session": "1.0.0",
"Microsoft.AspNetCore.StaticFiles": "1.0.0", "Microsoft.AspNetCore.StaticFiles": "1.0.0",
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.0", "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
"Microsoft.Extensions.Configuration.CommandLine": "1.0.0", "Microsoft.Extensions.Configuration.CommandLine": "1.0.0",

70
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Authentication.cs

@ -14,11 +14,9 @@ using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication; using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.WebUtilities; using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -53,25 +51,9 @@ namespace OpenIddict.Infrastructure {
} }
// If a request_id parameter can be found in the authorization request, // 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())) { if (!string.IsNullOrEmpty(context.Request.GetRequestId())) {
// Ensure session support has been enabled for this request. var payload = await services.Options.Cache.GetAsync(OpenIddictConstants.Environment.Request + context.Request.GetRequestId());
if (context.HttpContext.Features.Get<ISessionFeature>() == 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());
if (payload == null) { if (payload == null) {
services.Logger.LogError("The authorization request was rejected because an unknown " + services.Logger.LogError("The authorization request was rejected because an unknown " +
"or invalid request_id parameter was specified."); "or invalid request_id parameter was specified.");
@ -317,37 +299,31 @@ namespace OpenIddict.Infrastructure {
return; return;
} }
if (context.HttpContext.Request.Path == context.Options.AuthorizationEndpointPath && // If no request_id parameter can be found in the current request, assume the OpenID Connect request
string.Equals(context.HttpContext.Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) { // was not serialized yet and store the entire payload in the distributed cache to make it easier
// Ensure session support has been enabled for this request. // to flow across requests and internal/external authentication/registration workflows.
if (context.HttpContext.Features.Get<ISessionFeature>() == null) { if (string.IsNullOrEmpty(context.Request.GetRequestId())) {
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;
}
// Generate a request identifier. Note: using a crypto-secure // Generate a request identifier. Note: using a crypto-secure
// random number generator is not necessary in this case. // random number generator is not necessary in this case.
var identifier = Guid.NewGuid().ToString(); var identifier = Guid.NewGuid().ToString();
// Load the session from the session store. var options = new DistributedCacheEntryOptions {
await context.HttpContext.Session.LoadAsync(); AbsoluteExpiration = context.Options.SystemClock.UtcNow + TimeSpan.FromMinutes(30),
SlidingExpiration = TimeSpan.FromMinutes(10)
};
// Store the serialized authorization request parameters in the user session. // Store the serialized authorization request parameters in the distributed cache.
context.HttpContext.Session.Set(OpenIddictConstants.Environment.Request + identifier, context.Request.Export()); 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( var address = QueryHelpers.AddQueryString(
uri: context.HttpContext.Request.GetEncodedUrl(), uri: context.Options.AuthorizationEndpointPath,
name: OpenIdConnectConstants.Parameters.RequestId, value: identifier); name: OpenIdConnectConstants.Parameters.RequestId, value: identifier);
context.HttpContext.Response.Redirect(address); context.HttpContext.Response.Redirect(address);
// Mark the response as handled
// to skip the rest of the pipeline.
context.HandleResponse(); context.HandleResponse();
return; return;
@ -356,15 +332,15 @@ namespace OpenIddict.Infrastructure {
context.SkipToNextMiddleware(); context.SkipToNextMiddleware();
} }
public override Task ApplyAuthorizationResponse([NotNull] ApplyAuthorizationResponseContext context) { public override async Task ApplyAuthorizationResponse([NotNull] ApplyAuthorizationResponseContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>(); var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
// Remove the authorization request from the user session. // Remove the authorization request from the distributed cache.
if (!string.IsNullOrEmpty(context.Request.GetRequestId())) { if (!string.IsNullOrEmpty(context.Request.GetRequestId())) {
// Note: the ApplyAuthorizationResponse event is called for both successful // Note: the ApplyAuthorizationResponse event is called for both successful
// and errored authorization responses but discrimination is not necessary here, // and errored authorization responses but discrimination is not necessary here,
// as the authorization request must be removed from the user session in both cases. // as the authorization request must be removed from the distributed cache in both cases.
context.HttpContext.Session.Remove(OpenIddictConstants.Environment.Request + context.Request.GetRequestId()); await services.Options.Cache.RemoveAsync(OpenIddictConstants.Environment.Request + context.Request.GetRequestId());
} }
if (!string.IsNullOrEmpty(context.Response.Error) && services.Options.ErrorHandlingPath.HasValue) { if (!string.IsNullOrEmpty(context.Response.Error) && services.Options.ErrorHandlingPath.HasValue) {
@ -380,8 +356,6 @@ namespace OpenIddict.Infrastructure {
context.SkipToNextMiddleware(); context.SkipToNextMiddleware();
} }
return Task.FromResult(0);
} }
} }
} }

5
src/OpenIddict.Core/OpenIddictExtensions.cs

@ -7,6 +7,7 @@
using System; using System;
using System.Linq; using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -87,6 +88,10 @@ namespace Microsoft.AspNetCore.Builder {
// Resolve the OpenIddict options from the DI container. // Resolve the OpenIddict options from the DI container.
var options = app.ApplicationServices.GetRequiredService<IOptions<OpenIddictOptions>>().Value; var options = app.ApplicationServices.GetRequiredService<IOptions<OpenIddictOptions>>().Value;
if (options.Cache == null) {
options.Cache = app.ApplicationServices.GetRequiredService<IDistributedCache>();
}
// Get the modules registered by the application // Get the modules registered by the application
// and add the OpenID Connect server middleware. // and add the OpenID Connect server middleware.
var modules = options.Modules.ToList(); var modules = options.Modules.ToList();

7
src/OpenIddict.Core/OpenIddictOptions.cs

@ -8,6 +8,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using AspNet.Security.OpenIdConnect.Server; using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
namespace OpenIddict { namespace OpenIddict {
/// <summary> /// <summary>
@ -23,6 +24,12 @@ namespace OpenIddict {
AccessTokenLifetime = TimeSpan.FromMinutes(30); AccessTokenLifetime = TimeSpan.FromMinutes(30);
} }
/// <summary>
/// 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.
/// </summary>
public IDistributedCache Cache { get; set; }
/// <summary> /// <summary>
/// Gets or sets the path of the middleware responsible of rendering /// Gets or sets the path of the middleware responsible of rendering
/// the OpenID Connect errors occurred during interactive workflows. /// the OpenID Connect errors occurred during interactive workflows.

Loading…
Cancel
Save