Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions samples/AuthRemoteIdentity/AuthRemoteIdentityCore/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,32 @@

app.UseRouting();

app.UseAuthenticationEvents();
app.UseAuthentication();
app.UseAuthorizationEvents();
app.UseAuthenticationEvents();
app.UseAuthorization();
app.UseAuthorizationEvents();

app.UseSystemWebAdapters();

app.MapDefaultControllerRoute();

app.Map("/user", () =>
{
var user = ClaimsPrincipal.Current;

if (user is null)
{
return Results.Problem("Empty ClaimsPrincipal");
}

return Results.Json(new
{
IsAuthenticated = user.Identity?.IsAuthenticated ?? false,
Name = user.Identity?.Name,
Claims = user.Claims.Select(c => new { c.Type, c.Value })
});
}).WithMetadata(new SetThreadCurrentPrincipalAttribute()); ;

app.MapRemoteAppFallback()
.ShortCircuit();

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

namespace Microsoft.AspNetCore.SystemWebAdapters;
namespace Microsoft.AspNetCore.SystemWebAdapters.Middleware;

internal sealed class CachePolicyMiddleware(RequestDelegate next)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.SystemWebAdapters.Features;

namespace Microsoft.AspNetCore.SystemWebAdapters.Middleware;

internal sealed class CurrentPrincipalMiddleware(RequestDelegate next)
{
public Task InvokeAsync(HttpContextCore context)
{
if (context.GetEndpoint()?.Metadata.GetMetadata<SetThreadCurrentPrincipalAttribute>() is { IsDisabled: false })
{
context.Features.GetRequiredFeature<IRequestUserFeature>().EnableStaticAccessors();
}

return next(context);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.SystemWebAdapters.Features;

namespace Microsoft.AspNetCore.SystemWebAdapters;
namespace Microsoft.AspNetCore.SystemWebAdapters.Middleware;

internal partial class PreBufferRequestStreamMiddleware
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.SystemWebAdapters.Features;

namespace Microsoft.AspNetCore.SystemWebAdapters.Middleware;

internal sealed class RequestFeaturesMiddleware(RequestDelegate next)
{
public async Task InvokeAsync(HttpContextCore context)
{
var existing = context.Features.GetRequiredFeature<IHttpRequestFeature>();
var existingPipe = context.Features.Get<IRequestBodyPipeFeature>();

using var inputStreamFeature = new HttpRequestInputStreamFeature(existing);

context.Features.Set<IHttpRequestFeature>(inputStreamFeature);
context.Features.Set<IHttpRequestInputStreamFeature>(inputStreamFeature);
context.Features.Set<IRequestBodyPipeFeature>(inputStreamFeature);
context.Features.Set<IHttpRequestPathFeature>(inputStreamFeature);

try
{
await next(context);
}
finally
{
context.Features.Set<IHttpRequestFeature>(existing);
context.Features.Set<IRequestBodyPipeFeature>(existingPipe);
context.Features.Set<IHttpRequestInputStreamFeature>(null);
context.Features.Set<IHttpRequestPathFeature>(null);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features.Authentication;
using Microsoft.AspNetCore.SystemWebAdapters.Features;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.SystemWebAdapters.Middleware;

internal sealed partial class RequestUserFeaturesMiddleware(RequestDelegate next, IOptions<SystemWebAdaptersOptions> options, ILoggerFactory loggerFactory)
{
private readonly ILogger _logger = loggerFactory.CreateLogger<RequestUserFeature>();
private readonly bool _setCurrentAccessors = options.Value.EnableStaticUserAccessors;

public async Task InvokeAsync(HttpContextCore context, IOptions<SystemWebAdaptersOptions> options)
{
using var userFeature = new RequestUserFeature(_logger, _setCurrentAccessors) { User = context.User };

context.Features.Set<IRequestUserFeature>(userFeature);
context.Features.Set<IHttpAuthenticationFeature>(userFeature);

try
{
await next(context);
}
finally
{
context.Features.Set<IRequestUserFeature>(null);
context.Features.Set<IHttpAuthenticationFeature>(null);
}
}

private sealed partial class RequestUserFeature(ILogger logger, bool setCurrentAccessors) : IRequestUserFeature, IHttpAuthenticationFeature, IDisposable
{
private readonly ILogger logger = logger;

[LoggerMessage(0, LogLevel.Debug, "A custom principal {PrincipalType} is being used and should be replaced with a ClaimsPrincipal derived type.")]
private partial void LogNonClaimsPrincipal(Type principalType);

[LoggerMessage(1, LogLevel.Trace, "Thread.CurrentPrincipal has been set with the current user")]
private partial void LogCurrentPrincipalUsage();

public IPrincipal? User { get; set; }

WindowsIdentity? IRequestUserFeature.LogonUserIdentity => User?.Identity as WindowsIdentity;

ClaimsPrincipal? IHttpAuthenticationFeature.User
{
get => GetOrCreateClaims(User);
set
{
User = value;
EnsureCurrentPrincipalSetIfRequired();
}
}

private ClaimsPrincipal? GetOrCreateClaims(IPrincipal? principal)
{
if (principal is null)
{
return null;
}

if (principal is ClaimsPrincipal claimsPrincipal)
{
return claimsPrincipal;
}

LogNonClaimsPrincipal(principal.GetType());

return new ClaimsPrincipal(principal);
}

public void EnableStaticAccessors()
{
LogCurrentPrincipalUsage();
setCurrentAccessors = true;
EnsureCurrentPrincipalSetIfRequired();
}

private void EnsureCurrentPrincipalSetIfRequired()
{
if (setCurrentAccessors)
{
var claimsPrincipal = GetOrCreateClaims(User);
Thread.CurrentPrincipal = claimsPrincipal;
}
}

public void Dispose()
{
if (setCurrentAccessors)
{
Thread.CurrentPrincipal = null;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.SystemWebAdapters.Features;

namespace Microsoft.AspNetCore.SystemWebAdapters.Middleware;

internal sealed class ResponseFeaturesMiddleware(RequestDelegate next)
{
public async Task InvokeAsync(HttpContextCore context)
{
var responseBodyFeature = context.Features.GetRequiredFeature<IHttpResponseBodyFeature>();

using var adapterFeature = new HttpResponseAdapterFeature(responseBodyFeature);

context.Features.Set<IHttpResponseBodyFeature>(adapterFeature);
context.Features.Set<IHttpResponseBufferingFeature>(adapterFeature);
context.Features.Set<IHttpResponseEndFeature>(adapterFeature);
context.Features.Set<IHttpResponseContentFeature>(adapterFeature);

try
{
await next(context);
}
finally
{
// The buffering feature may be removed if the response has ended i.e in usage with YARP
if (context.Features.Get<IHttpResponseBufferingFeature>() is { } buffer)
{
await buffer.FlushAsync();
}

context.Features.Set<IHttpResponseBodyFeature>(responseBodyFeature);
context.Features.Set<IHttpResponseBufferingFeature>(null);
context.Features.Set<IHttpResponseEndFeature>(null);
context.Features.Set<IHttpResponseContentFeature>(null);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.SystemWebAdapters.Features;

namespace Microsoft.AspNetCore.SystemWebAdapters;
namespace Microsoft.AspNetCore.SystemWebAdapters.Middleware;

internal sealed class SessionEventsMiddleware
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using Microsoft.AspNetCore.SystemWebAdapters.Features;
using Microsoft.AspNetCore.SystemWebAdapters.SessionState;

namespace Microsoft.AspNetCore.SystemWebAdapters;
namespace Microsoft.AspNetCore.SystemWebAdapters.Middleware;

internal sealed class SessionStateMiddleware
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SystemWebAdapters.Features;

namespace Microsoft.AspNetCore.SystemWebAdapters;
namespace Microsoft.AspNetCore.SystemWebAdapters.Middleware;

internal class SetHttpContextTimestampMiddleware
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

namespace Microsoft.AspNetCore.SystemWebAdapters;
namespace Microsoft.AspNetCore.SystemWebAdapters.Middleware;

internal class SingleThreadedRequestMiddleware
{
Expand Down
Loading