Browse Source

Use Uri.TryCreate() to build the base and request URIs used by OpenIddict

pull/2283/head
Kévin Chalet 11 months ago
parent
commit
1beb7ab4c9
  1. 27
      src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.cs
  2. 27
      src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs
  3. 3
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.cs
  4. 3
      src/OpenIddict.Client/OpenIddictClientHandlers.cs
  5. 27
      src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs
  6. 27
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs
  7. 3
      src/OpenIddict.Server/OpenIddictServerHandlers.cs
  8. 27
      src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs
  9. 27
      src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlers.cs
  10. 79
      test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddictServerAspNetCoreIntegrationTests.cs
  11. 78
      test/OpenIddict.Server.Owin.IntegrationTests/OpenIddictServerOwinIntegrationTests.cs

27
src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.cs

@ -115,28 +115,17 @@ public static partial class OpenIddictClientAspNetCoreHandlers
// used to build an absolute base URI and a request URI that will be used to determine whether the
// received request matches one of the URIs assigned to an OpenIddict endpoint. If the request
// is later handled by OpenIddict, an additional check will be made to require the Host header.
var host = request.Host.HasValue ? request.Host : new HostString("localhost");
(context.BaseUri, context.RequestUri) = request.Host switch
{
{ HasValue: true } host => (
BaseUri: new Uri(request.Scheme + Uri.SchemeDelimiter + host + request.PathBase, UriKind.Absolute),
RequestUri: new Uri(request.GetEncodedUrl(), UriKind.Absolute)),
{ HasValue: false } => (
BaseUri: new UriBuilder
{
Scheme = request.Scheme,
Path = request.PathBase.ToUriComponent()
}.Uri,
RequestUri: new UriBuilder
{
Scheme = request.Scheme,
Path = (request.PathBase + request.Path).ToUriComponent(),
Query = request.QueryString.ToUriComponent()
}.Uri)
};
context.BaseUri = CreateUri(UriHelper.BuildAbsolute(request.Scheme, host, request.PathBase));
context.RequestUri = CreateUri(UriHelper.BuildAbsolute(request.Scheme, host, request.PathBase, request.Path, request.QueryString));
return default;
// Note: the BCL System.Uri class has strict rules (e.g it rejects specific characters and enforces a
// limit of 65519 characters for the complete URI representation). To ensure no exception is thrown if the
// URI cannot be built (which would also affect non-OpenIddict endpoints), Uri.TryCreate() is used here.
static Uri? CreateUri(string value) => Uri.TryCreate(value, UriKind.Absolute, out Uri? uri) ? uri : null;
}
}

27
src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs

@ -112,28 +112,17 @@ public static partial class OpenIddictClientOwinHandlers
// used to build an absolute base URI and a request URI that will be used to determine whether the
// received request matches one of the URIs assigned to an OpenIddict endpoint. If the request
// is later handled by OpenIddict, an additional check will be made to require the Host header.
var host = !string.IsNullOrEmpty(request.Host.Value) ? request.Host : new HostString("localhost");
(context.BaseUri, context.RequestUri) = request.Host switch
{
{ Value.Length: > 0 } host => (
BaseUri: new Uri(request.Scheme + Uri.SchemeDelimiter + host + request.PathBase, UriKind.Absolute),
RequestUri: request.Uri),
{ Value: null or { Length: 0 } } => (
BaseUri: new UriBuilder
{
Scheme = request.Scheme,
Path = request.PathBase.ToUriComponent()
}.Uri,
RequestUri: new UriBuilder
{
Scheme = request.Scheme,
Path = (request.PathBase + request.Path).ToUriComponent(),
Query = request.QueryString.ToUriComponent()
}.Uri)
};
context.BaseUri = CreateUri(request.Scheme + Uri.SchemeDelimiter + host + request.PathBase);
context.RequestUri = CreateUri(request.Scheme + Uri.SchemeDelimiter + host + request.PathBase + request.Path + request.QueryString);
return default;
// Note: the BCL System.Uri class has strict rules (e.g it rejects specific characters and enforces a
// limit of 65519 characters for the complete URI representation). To ensure no exception is thrown if the
// URI cannot be built (which would also affect non-OpenIddict endpoints), Uri.TryCreate() is used here.
static Uri? CreateUri(string value) => Uri.TryCreate(value, UriKind.Absolute, out Uri? uri) ? uri : null;
}
}

3
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.cs

@ -260,9 +260,10 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
throw new ArgumentNullException(nameof(context));
}
// If the base or request URIs couldn't be resolved, don't try to infer the endpoint type.
if (context is not { BaseUri.IsAbsoluteUri: true, RequestUri.IsAbsoluteUri: true })
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0127));
return default;
}
// If an endpoint was already inferred by the generic handler, don't override it.

3
src/OpenIddict.Client/OpenIddictClientHandlers.cs

@ -243,9 +243,10 @@ public static partial class OpenIddictClientHandlers
throw new ArgumentNullException(nameof(context));
}
// If the base or request URIs couldn't be resolved, don't try to infer the endpoint type.
if (context is not { BaseUri.IsAbsoluteUri: true, RequestUri.IsAbsoluteUri: true })
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0127));
return default;
}
context.EndpointType =

27
src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs

@ -100,28 +100,17 @@ public static partial class OpenIddictServerAspNetCoreHandlers
// used to build an absolute base URI and a request URI that will be used to determine whether the
// received request matches one of the URIs assigned to an OpenIddict endpoint. If the request
// is later handled by OpenIddict, an additional check will be made to require the Host header.
var host = request.Host.HasValue ? request.Host : new HostString("localhost");
(context.BaseUri, context.RequestUri) = request.Host switch
{
{ HasValue: true } host => (
BaseUri: new Uri(request.Scheme + Uri.SchemeDelimiter + host + request.PathBase, UriKind.Absolute),
RequestUri: new Uri(request.GetEncodedUrl(), UriKind.Absolute)),
{ HasValue: false } => (
BaseUri: new UriBuilder
{
Scheme = request.Scheme,
Path = request.PathBase.ToUriComponent()
}.Uri,
RequestUri: new UriBuilder
{
Scheme = request.Scheme,
Path = (request.PathBase + request.Path).ToUriComponent(),
Query = request.QueryString.ToUriComponent()
}.Uri)
};
context.BaseUri = CreateUri(UriHelper.BuildAbsolute(request.Scheme, host, request.PathBase));
context.RequestUri = CreateUri(UriHelper.BuildAbsolute(request.Scheme, host, request.PathBase, request.Path, request.QueryString));
return default;
// Note: the BCL System.Uri class has strict rules (e.g it rejects specific characters and enforces a
// limit of 65519 characters for the complete URI representation). To ensure no exception is thrown if the
// URI cannot be built (which would also affect non-OpenIddict endpoints), Uri.TryCreate() is used here.
static Uri? CreateUri(string value) => Uri.TryCreate(value, UriKind.Absolute, out Uri? uri) ? uri : null;
}
}

27
src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs

@ -98,28 +98,17 @@ public static partial class OpenIddictServerOwinHandlers
// used to build an absolute base URI and a request URI that will be used to determine whether the
// received request matches one of the URIs assigned to an OpenIddict endpoint. If the request
// is later handled by OpenIddict, an additional check will be made to require the Host header.
var host = !string.IsNullOrEmpty(request.Host.Value) ? request.Host : new HostString("localhost");
(context.BaseUri, context.RequestUri) = request.Host switch
{
{ Value.Length: > 0 } host => (
BaseUri: new Uri(request.Scheme + Uri.SchemeDelimiter + host + request.PathBase, UriKind.Absolute),
RequestUri: request.Uri),
{ Value: null or { Length: 0 } } => (
BaseUri: new UriBuilder
{
Scheme = request.Scheme,
Path = request.PathBase.ToUriComponent()
}.Uri,
RequestUri: new UriBuilder
{
Scheme = request.Scheme,
Path = (request.PathBase + request.Path).ToUriComponent(),
Query = request.QueryString.ToUriComponent()
}.Uri)
};
context.BaseUri = CreateUri(request.Scheme + Uri.SchemeDelimiter + host + request.PathBase);
context.RequestUri = CreateUri(request.Scheme + Uri.SchemeDelimiter + host + request.PathBase + request.Path + request.QueryString);
return default;
// Note: the BCL System.Uri class has strict rules (e.g it rejects specific characters and enforces a
// limit of 65519 characters for the complete URI representation). To ensure no exception is thrown if the
// URI cannot be built (which would also affect non-OpenIddict endpoints), Uri.TryCreate() is used here.
static Uri? CreateUri(string value) => Uri.TryCreate(value, UriKind.Absolute, out Uri? uri) ? uri : null;
}
}

3
src/OpenIddict.Server/OpenIddictServerHandlers.cs

@ -152,9 +152,10 @@ public static partial class OpenIddictServerHandlers
throw new ArgumentNullException(nameof(context));
}
// If the base or request URIs couldn't be resolved, don't try to infer the endpoint type.
if (context is not { BaseUri.IsAbsoluteUri: true, RequestUri.IsAbsoluteUri: true })
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0127));
return default;
}
context.EndpointType =

27
src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs

@ -98,28 +98,17 @@ public static partial class OpenIddictValidationAspNetCoreHandlers
// used to build an absolute base URI and a request URI that will be used to determine whether the
// received request matches one of the URIs assigned to an OpenIddict endpoint. If the request
// is later handled by OpenIddict, an additional check will be made to require the Host header.
var host = request.Host.HasValue ? request.Host : new HostString("localhost");
(context.BaseUri, context.RequestUri) = request.Host switch
{
{ HasValue: true } host => (
BaseUri: new Uri(request.Scheme + Uri.SchemeDelimiter + host + request.PathBase, UriKind.Absolute),
RequestUri: new Uri(request.GetEncodedUrl(), UriKind.Absolute)),
{ HasValue: false } => (
BaseUri: new UriBuilder
{
Scheme = request.Scheme,
Path = request.PathBase.ToUriComponent()
}.Uri,
RequestUri: new UriBuilder
{
Scheme = request.Scheme,
Path = (request.PathBase + request.Path).ToUriComponent(),
Query = request.QueryString.ToUriComponent()
}.Uri)
};
context.BaseUri = CreateUri(UriHelper.BuildAbsolute(request.Scheme, host, request.PathBase));
context.RequestUri = CreateUri(UriHelper.BuildAbsolute(request.Scheme, host, request.PathBase, request.Path, request.QueryString));
return default;
// Note: the BCL System.Uri class has strict rules (e.g it rejects specific characters and enforces a
// limit of 65519 characters for the complete URI representation). To ensure no exception is thrown if the
// URI cannot be built (which would also affect non-OpenIddict endpoints), Uri.TryCreate() is used here.
static Uri? CreateUri(string value) => Uri.TryCreate(value, UriKind.Absolute, out Uri? uri) ? uri : null;
}
}

27
src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlers.cs

@ -100,28 +100,17 @@ public static partial class OpenIddictValidationOwinHandlers
// used to build an absolute base URI and a request URI that will be used to determine whether the
// received request matches one of the URIs assigned to an OpenIddict endpoint. If the request
// is later handled by OpenIddict, an additional check will be made to require the Host header.
var host = !string.IsNullOrEmpty(request.Host.Value) ? request.Host : new HostString("localhost");
(context.BaseUri, context.RequestUri) = request.Host switch
{
{ Value.Length: > 0 } host => (
BaseUri: new Uri(request.Scheme + Uri.SchemeDelimiter + host + request.PathBase, UriKind.Absolute),
RequestUri: request.Uri),
{ Value: null or { Length: 0 } } => (
BaseUri: new UriBuilder
{
Scheme = request.Scheme,
Path = request.PathBase.ToUriComponent()
}.Uri,
RequestUri: new UriBuilder
{
Scheme = request.Scheme,
Path = (request.PathBase + request.Path).ToUriComponent(),
Query = request.QueryString.ToUriComponent()
}.Uri)
};
context.BaseUri = CreateUri(request.Scheme + Uri.SchemeDelimiter + host + request.PathBase);
context.RequestUri = CreateUri(request.Scheme + Uri.SchemeDelimiter + host + request.PathBase + request.Path + request.QueryString);
return default;
// Note: the BCL System.Uri class has strict rules (e.g it rejects specific characters and enforces a
// limit of 65519 characters for the complete URI representation). To ensure no exception is thrown if the
// URI cannot be built (which would also affect non-OpenIddict endpoints), Uri.TryCreate() is used here.
static Uri? CreateUri(string value) => Uri.TryCreate(value, UriKind.Absolute, out Uri? uri) ? uri : null;
}
}

79
test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddictServerAspNetCoreIntegrationTests.cs

@ -8,6 +8,7 @@ using System.Collections.Immutable;
using System.Security.Claims;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
@ -32,6 +33,84 @@ public partial class OpenIddictServerAspNetCoreIntegrationTests : OpenIddictServ
{
}
[Fact]
public async Task ProcessRequest_IgnoresInvalidBaseUris()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.AddEventHandler<ProcessRequestContext>(builder =>
{
builder.UseInlineHandler(context =>
{
var request = context.Transaction.GetHttpRequest()!;
request.Host = new HostString("fabrikam.com:100000");
return default;
});
builder.SetOrder(int.MinValue);
});
options.AddEventHandler<ProcessRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
// Assert
Assert.Null(context.BaseUri);
Assert.Null(context.RequestUri);
Assert.Equal(OpenIddictServerEndpointType.Unknown, context.EndpointType);
return default;
}));
});
await using var client = await server.CreateClientAsync();
// Act
await client.GetAsync("/.well-known/openid-configuration", new OpenIddictRequest());
}
[Fact]
public async Task ProcessRequest_IgnoresInvalidRequestUris()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.AddEventHandler<ProcessRequestContext>(builder =>
{
builder.UseInlineHandler(context =>
{
var request = context.Transaction.GetHttpRequest()!;
request.QueryString = new QueryString("?" + new string([.. Enumerable.Repeat('x', 100_000)]));
return default;
});
builder.SetOrder(int.MinValue);
});
options.AddEventHandler<ProcessRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
// Assert
Assert.NotNull(context.BaseUri);
Assert.Null(context.RequestUri);
Assert.Equal(OpenIddictServerEndpointType.Unknown, context.EndpointType);
return default;
}));
});
await using var client = await server.CreateClientAsync();
// Act
await client.GetAsync("/.well-known/openid-configuration", new OpenIddictRequest());
}
[Fact]
public async Task ProcessAuthentication_CreationDateIsMappedToIssuedUtc()
{

78
test/OpenIddict.Server.Owin.IntegrationTests/OpenIddictServerOwinIntegrationTests.cs

@ -29,6 +29,84 @@ public partial class OpenIddictServerOwinIntegrationTests : OpenIddictServerInte
{
}
[Fact]
public async Task ProcessRequest_IgnoresInvalidBaseUris()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.AddEventHandler<ProcessRequestContext>(builder =>
{
builder.UseInlineHandler(context =>
{
var request = context.Transaction.GetOwinRequest()!;
request.Host = new HostString("fabrikam.com:100000");
return default;
});
builder.SetOrder(int.MinValue);
});
options.AddEventHandler<ProcessRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
// Assert
Assert.Null(context.BaseUri);
Assert.Null(context.RequestUri);
Assert.Equal(OpenIddictServerEndpointType.Unknown, context.EndpointType);
return default;
}));
});
await using var client = await server.CreateClientAsync();
// Act
await client.GetAsync("/.well-known/openid-configuration", new OpenIddictRequest());
}
[Fact]
public async Task ProcessRequest_IgnoresInvalidRequestUris()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.AddEventHandler<ProcessRequestContext>(builder =>
{
builder.UseInlineHandler(context =>
{
var request = context.Transaction.GetOwinRequest()!;
request.QueryString = new QueryString("?" + new string([.. Enumerable.Repeat('x', 100_000)]));
return default;
});
builder.SetOrder(int.MinValue);
});
options.AddEventHandler<ProcessRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
// Assert
Assert.NotNull(context.BaseUri);
Assert.Null(context.RequestUri);
Assert.Equal(OpenIddictServerEndpointType.Unknown, context.EndpointType);
return default;
}));
});
await using var client = await server.CreateClientAsync();
// Act
await client.GetAsync("/.well-known/openid-configuration", new OpenIddictRequest());
}
[Fact]
public async Task ProcessAuthentication_CreationDateIsMappedToIssuedUtc()
{

Loading…
Cancel
Save