-
Notifications
You must be signed in to change notification settings - Fork 597
Auth 2.0 Iteration 2 #1113
Auth 2.0 Iteration 2 #1113
Conversation
Hi @HaoK, I'm your friendly neighborhood .NET Foundation Pull Request Bot (You can call me DNFBOT). Thanks for your contribution! The agreement was validated by .NET Foundation and real humans are currently evaluating your PR. TTYL, DNFBOT; |
Rather than nuking the existing auth package first, requiring updating everything at once, implementation of the new stack will be in a Authentication.Impl package until everything is done..
|
@leastprivilege and I have a TODO item to look over and give feedback. We were hoping to have done this last week, but it didn't happen. Perhaps this weekend or next week. Thanks. |
Sure, you guys might want to look at the old PR first for better context/info, and it has more or less the same shape, it will take a bit for me to stitch everything back together again in this PR. |
Seems too confusing to have any dependency on the old HttpAuthentication feature, going to copy ChallengeBehavior and AuthenticationProperties instead, since we aren't supporting old and new together. |
@@ -851,68 +740,85 @@ private Task SignOutAsWrong(HttpContext context) | |||
} | |||
|
|||
[Fact] | |||
public async Task MapWillNotAffectChallenge() | |||
public async Task MapWillAffectChallenge() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: this is a behavior change from old stack to new stack (Map does affect redirect url for challenge now that its not tied to any middleware)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Be careful, it's most likely not a wanted change: if /page
is configured as the callback path and the user is actually redirected to /login/page
, all he'll get is a 404 response, as no login page will handle the request.
I assume it similarly impacts all the RAT-based middleware that use callback paths and have built-in endpoints? (OIDC, OAuth2, Twitter...)
Cookies and tests have been updated to use the new stack, and pull system clock from DI |
Assert.Equal("?ReturnUrl=%2F", location.Query); | ||
} | ||
|
||
[Fact] | ||
[ConditionalFact(Skip = "Revisit, exception no longer thrown")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hum, why? The InvalidOperationException
is there to inform the dev' that something is wrong with his setup. I don't think it's preferable to silently ignore the operation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the old test threw because it was doing an AutomaticChallenge followed by an second challenge, which no longer happens in the new stack, I'll have to debug the old test, to see what was triggering it
[Theory] | ||
[InlineData(true)] | ||
[InlineData(false)] | ||
public async Task MapWillAffectChallengeOnlyWithUseAuth(bool useAuth) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI @PinpointTownes fixed the map + resolve url behavior to work similar to before (using the path of the single middleware), via a new IAuthenticationFeature like we discussed @Tratcher
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really great news!
@@ -3,8 +3,8 @@ | |||
"windowsAuthentication": false, | |||
"anonymousAuthentication": true, | |||
"iisExpress": { | |||
"applicationUrl": "http://localhost:42023", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
revert, these samples need to stay on a specific port.
{ | ||
public AuthenticationScheme(string name, Type handlerType, Dictionary<string, object> settings) | ||
{ | ||
Name = name ?? throw new ArgumentNullException(nameof(name)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI: @Eilon has rejected the use of this syntax.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does that catch string.empty?
{ | ||
public class AuthenticationScheme | ||
{ | ||
public AuthenticationScheme(string name, Type handlerType, Dictionary<string, object> settings) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just take an IReadOnlyDictionary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, fixed
|
||
namespace Microsoft.AspNetCore.Authentication | ||
{ | ||
public enum ChallengeBehavior |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking to eliminate this. Let Challenge and Forbidden be separate code paths, and Automatic would be resolved at a higher level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah but we likely can't do this until there's a bunch of corresponding AuthZ changes, we should tackle that in a follow on PR specifically for AuthZ (see #901)
</PropertyGroup> | ||
<ItemGroup> | ||
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="1.2.0-*" /> | ||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="1.2.0-*" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What did you need from .HTTP? It's unusual for an Abstractions package to have this many concrete dependencies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, these were just carried over from eixsting Authentication.csproj, I switched them to just the Http.Abstractions package and things were fine.
|
||
namespace Microsoft.AspNetCore.Builder | ||
namespace Microsoft.AspNetCore.Authentication.Cookies |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'd used this namespace to improve intellisence in Startup, it's the same namespace the extensions were in. Should these move to the DI namespace?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that's necessary with lambdas, as intellisense knows the type from the signature, the namespace hack was only needed for new
@@ -23,5 +24,7 @@ public class BaseCookieContext : BaseContext | |||
} | |||
|
|||
public CookieAuthenticationOptions Options { get; } | |||
|
|||
public AuthenticationScheme Scheme { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would Scheme go on BaseAuthenticationContext?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to bridge the context's between the service and the handlers. The service doesn't care about the scheme object, only the string, but maybe we should just have a BaseAuthenticationHandlerContext that does have the scheme. I have a todo in the top comment to make a pass through all of our events/context objects since they have always been a bit of a hodgepodge mess
throw new ArgumentNullException(nameof(options)); | ||
} | ||
|
||
if (string.IsNullOrEmpty(Options.AppId)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These checks are missing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Grrr yup, I'll add a test for these as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved the checks to Validate in the options which at least causes an exception, but the current behavior isn't perfect, in that it blows up the first time the scheme is used in the auth service.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weren't we going to call Validate from the UseFooAuth extension too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I mispoke, it blows up when the Auth service is constructed as the schemes are built when SchemeProvider/HandlerResolvers are built. So maybe that's not that bad if the auth service itself cannot be resolved due to a configuration error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you share the callstack/exception?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typeof(System.ArgumentException): The 'ClientId' option must be provided.
Parameter name: ClientId
Stack Trace:
at Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions.Validate() in C:\GitHub\Security\src\Microsoft.AspNetCore.Authentication.OAuth\OAuthOptions.cs:line 33
at Microsoft.Extensions.DependencyInjection.AuthenticationServiceCollectionExtensions.<>c__DisplayClass2_0`2.<AddSchemeHandler>b__1(AuthenticationSchemeBuilder b) in C:\GitHub\Security\src\Microsoft.AspNetCore.Authentication.Impl\AuthenticationServiceCollectionExtensions.cs:line 66
at Microsoft.AspNetCore.Authentication.AuthenticationOptions.AddScheme(String name, Action`1 configureBuilder) in C:\GitHub\Security\src\Microsoft.AspNetCore.Authentication.Abstractions\AuthenticationOptions.cs:line 42
at Microsoft.Extensions.DependencyInjection.AuthenticationServiceCollectionExtensions.<>c__DisplayClass2_0`2.<AddSchemeHandler>b__0(AuthenticationOptions o) in C:\GitHub\Security\src\Microsoft.AspNetCore.Authentication.Impl\AuthenticationServiceCollectionExtensions.cs:line 56
at Microsoft.Extensions.Options.OptionsCache`1.CreateOptions()
at System.Threading.LazyInitializer.EnsureInitializedCore[T](T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory)
at Microsoft.Extensions.Options.OptionsCache`1.get_Value()
at Microsoft.AspNetCore.Authentication.DefaultAuthenticationSchemeProvider..ctor(IOptions`1 options) in C:\GitHub\Security\src\Microsoft.AspNetCore.Authentication.Impl\DefaultAuthenticationSchemeProvider.cs:line 18
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass17_0.<RealizeService>b__0(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.ChallengeAsync(HttpContext context, String scheme, AuthenticationProperties properties, ChallengeBehavior behavior) in C:\GitHub\Security\src\Microsoft.AspNetCore.Authentication.Abstractions\AuthenticationHttpContextExtensions.cs:line 23
at Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.ChallengeAsync(HttpContext context, String scheme, AuthenticationProperties properties) in C:\GitHub\Security\src\Microsoft.AspNetCore.Authentication.Abstractions\AuthenticationHttpContextExtensions.cs:line 20
at Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.ChallengeAsync(HttpContext context, String scheme) in C:\GitHub\Security\src\Microsoft.AspNetCore.Authentication.Abstractions\AuthenticationHttpContextExtensions.cs:line 17
at Microsoft.AspNetCore.Authentication.OAuth.OAuthTests.<>c__DisplayClass0_0.<ThrowsIfClientIdMissing>b__4() in C:\GitHub\Security\test\Microsoft.AspNetCore.Authentication.Test\OAuthTests.cs:line 33
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That only says OAuth, it doesn't tell you Facebook. And Facebook calls it AppId. This is going to cause confusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I copied a different exception failure for the above example from the oauth tests, the facebook one says app id and has a facebookoptions.validate at the top.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, good to know
protected virtual async Task FinishResponseAsync() | ||
{ | ||
// Only renew if requested, and neither sign in or sign out was called | ||
if (!_shouldRefresh || SignInAccepted || SignOutAccepted) | ||
if (!_shouldRefresh || SignInCalled || SignOutCalled) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test hole here, need to add a test that verifies cookies aren't renewed if SignIn or SignOut is called. (this was broken before this change)
We also had lost the OauthOptions validations too which had no test coverage :/ I added some basic validation tests for those, also compact'ed the test structure, I nuked all the folders that had a single file, so basically only OpenIdConnect has a subfolder in Auth.Test now. Also started dropping middleware from the file names/comments where I see it. |
Hi all -- again, no time yet to have a look, but I wanted to let you know how we're integrating with the current security middleware in IdentityServer. In short, we're registering our own authentication middleware to be able to do a decorator pattern like feature to applications hosting us. We'd like to know if we can do something similar in this v2 work. Here's the main handler we have now: |
@brockallen the "authentication middleware" chain is no longer a thing in this prototype, so you won't be able to port your handler as-is. That said, I guess you could achieve something similar by replacing |
Right, but then we'd probably need to decorate whatever implementation is provided by default. Too bad the core DI system is so lacking in so many useful features. |
There a few workarounds to make the default DI stack (kinda) work with the decorator pattern, but it's indeed not ideal... I guess that's the price to pay for having full DI here
|
Well, the original chained handler design in katana was essentially an attempt at designing a poor man's decorator pattern in a system that had no DI. Moving to a service oriented approach in a DI system with missing features is just changing the surrounding code. I think I agree that it's not addressing the fundamental requirement. I've yet to look into the other changes to see if they've done anything to make the overall design better. |
@brockallen it looks like you could just replace the DefaultAuthenticationService with your own derived one, something like this: public class AuthenticationService : DefaultAuthenticationService, IAuthenticationService
{
private readonly IdentityServerOptions _options;
private readonly IHttpContextAccessor _context;
private readonly ISessionIdService _sessionId;
private readonly ILogger<AuthenticationHandler> _logger;
private readonly IEventService _events;
public AuthenticationService(
IAuthenticationHandlerResolver cache,
IClaimsTransformation transform,
IHttpContextAccessor context,
IdentityServerOptions options,
ISessionIdService sessionId,
IEventService events,
ILogger<AuthenticationService> logger) : base(cache, transform)
{
_context = context;
_options = options;
_sessionId = sessionId;
_events = events;
_logger = logger;
}
public override async Task SignInAsync(SignInContext context)
{
if (context.AuthenticationScheme == _options.Authentication.EffectiveAuthenticationScheme)
{
AugmentContext(context);
await RaiseSignInEventAsync(context.Principal);
}
await base.SignInAsync(context);
}
private void AugmentContext(SignInContext context)
{
_logger.LogDebug("Augmenting SignInContext");
context.Principal.AssertRequiredClaims();
context.Principal.AugmentMissingClaims();
_sessionId.CreateSessionId(context);
}
public override async Task SignOutAsync(SignOutContext context)
{
if (context.AuthenticationScheme == _options.Authentication.EffectiveAuthenticationScheme)
{
_sessionId.RemoveCookie();
await RaiseSignOutEventAsync();
}
await base.SignOutAsync(context);
}
// Would need to move to your own middleware most likely
internal async Task InitAsync()
{
var auth = GetAuthentication();
_handler = auth.Handler;
auth.Handler = this;
await _sessionId.EnsureSessionCookieAsync();
}
internal void Cleanup()
{
var auth = GetAuthentication();
auth.Handler = _handler;
}
// No longer needed, DI registration
IHttpAuthenticationFeature GetAuthentication()
{
var auth = _context.HttpContext.Features.Get<IHttpAuthenticationFeature>();
if (auth == null)
{
auth = new HttpAuthenticationFeature();
_context.HttpContext.Features.Set(auth);
}
return auth;
}
private async Task RaiseSignInEventAsync(ClaimsPrincipal principal)
{
await _events.RaiseLoginEventAsync(principal);
}
private async Task RaiseSignOutEventAsync()
{
var principal = await _context.HttpContext.GetIdentityServerUserAsync();
if (principal.IsAuthenticated())
{
await _events.RaiseLogoutEventAsync(principal);
}
}
}
} |
Updated: schemes must now explicitly register callback paths in their scheme data. Schemes ProcessRequest will only be called if Request.Path matches a callback path they specify. Will probably remove HandleRemoteAuthenticate since its no longer needed. Also will change the base ProcessRequest to throw now that it shouldn't be invoked anymore |
How terrible would it be to pull in actual routing? |
Well we could add something like "routes.MapAuthenticationRoutes()". We'd still have to have some internal plumbing in the schemes to store the routes for this method to consume. So the required startup pattern would then become:
Doesn't look too unreasonable, but is it reasonable for us to require routing for auth to work? |
Not bad. How well would that work with dynamically added/removed auth schemes? |
I'm not sure, depends on how well routing supports dynamically adding routes :) Let me see |
Forking my comment as it was hidden by GitHub: @HaoK point taken, tho' I'm not sure why you'd want to support scenarios with "hundreds/thousands of schemes" if there's no real support in the options stack (I'm not sure having hundreds of schemes + hundreds of handlers + hundreds of options instances is really scalable). Not being able to invoke my handler for every path is really annoying for me. As a compromise, would you consider bringing back your per-scheme "promiscuous mode"? (aka |
I'm not an routing expert, but it looks if we forced a pattern on the callback paths to fit into a route constraint, say |
@PinpointTownes its going to end up being a question of how easy we want to make it to have a global handler like that. We certainly could have a handleAllRequests flag on AuthenticationScheme that defaults to false. |
Started cleaning up some of the legacy handler processing.
I'm considering introducing a new ProcessRequestResult which would let us elimate the funky request processing that shows up in AuthenticateResult today since that's the main return type for Auth as a whole. |
That would be much appreciated 😅
Hum, but then, what's the difference between |
Difference between HandledResponse and StopProcessing is whether ProcessRequest returns true/false, same as today's handledResponse/Skip. Controls whether the middleware continues to invoke other handlers or not via the ghetto routing :) |
I'm confused now. When Security/src/Microsoft.AspNetCore.Authentication.Impl/RemoteAuthenticationHandler.cs Line 116 in d80b0d3
Yet, returning Security/src/Microsoft.AspNetCore.Authentication.Impl/AuthenticationMiddleware.cs Lines 49 to 52 in d80b0d3
So AFAICT, your renamed |
Fair enough, it was mostly the SkipToNextMiddleware that I was renaming, since the emphasis should just be that its skipping any further processing in this handler. What happens after that is the normal execution. We can keep it as Continue/Handled/Skip as the three states which correspond to (false, true, false for the AuthMiddleware invoking the rest of the pipeline) |
The confusing part is that StopProcessing is the reasonable name from the perspective of the individual handler's usage of this state, since it lives in the BaseControlContext. I'll leave that method name still as ProcessingCompleted since for both HandledResponse + Skipped, that means true for the purposes of the handler who is using the context. All of this smells a bit to me which is why I'm playing around with this area right now... |
Ok I revereted the StopProcessing state back to Skip. So the net result of these changes are: AuthenticateResult.Skip => AuthenticateResult.None to signify no failure, no ticket, nothing |
Some implications if we try to use the routing middleware instead of our own ghetto routing:
Or Without MVC, this overload could add routing and configure it?
|
See #1144 for an iteration using IOptionsFactory which provides named options (and potentially validation). It also split the ProcessRequest into a different interface IAuthenticationRequestHandler which is used instead of CallbackPaths to determine which schemes get called by the authentication middleware |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some feedback. Still reviewing.
if (user?.Identity?.IsAuthenticated ?? false) | ||
{ | ||
await next(); | ||
} | ||
else | ||
{ | ||
// We can do this because of options.AutomaticChallenge = true; | ||
await context.Authentication.ChallengeAsync(); | ||
await context.ChallengeAsync(JwtBearerDefaults.AuthenticationScheme); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No more generic Challenge? It should work if there's only one or its the default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can preserve it, it would line up with naked Authorize with no DefaultPolicy then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well if we add an overload for Challenge that omits scheme, we should probably do the same for authenticate and SignIn, basically null scheme will mean use default (since the overloads are implemented as extension methods)
@@ -39,7 +36,7 @@ public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory) | |||
claims.Add(new Claim(ClaimTypes.Role, "SomeRandomGroup" + i, ClaimValueTypes.String, "IssuedByBob", "OriginalIssuerJoe")); | |||
} | |||
|
|||
await context.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, | |||
await context.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So AuthenticationManager is completely obsolete and you're rooting all of the extensions on HttpContext?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah we should deprecate the Authentication versions as part of this
services.AddAuthentication(sharedOptions => | ||
sharedOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme); | ||
sharedOptions.DefaultAuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DefaultSignInScheme. You also need to set DefaultAuthenticationScheme and DefaultChallengeScheme right?
Same for the AzureAd sample.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, updating these in the other PR
SaveTokens = true, | ||
o.AppId = Configuration["facebook:appid"]; | ||
o.AppSecret = Configuration["facebook:appsecret"]; | ||
o.Scope.Clear(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, syntax usage regression.
I don't think the old synatax was Clearing, just Adding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clears removed
ClientId = Configuration["microsoftaccount:clientid"], | ||
ClientSecret = Configuration["microsoftaccount:clientsecret"], | ||
SaveTokens = true | ||
o.DisplayName = "MicrosoftAccount"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the display name set for this one? Isn't this the default value.
public Type HandlerType { get; } | ||
|
||
/// <summary> | ||
/// If true, the AuthenticationMiddleware will call the handler's HandleRequestAsync method. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrong comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many of the comments were already obsolete, we'll need to do a full scrub as part of this
|
||
// Holds things like the configured options instances for the handler | ||
// Also replacement for AuthenticationDescription | ||
public IReadOnlyDictionary<string, object> Settings { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really weird. Are we using it for anything besides Options and Description? Why not just expose those as first class properties?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, but I nuked AuthenticationDescription which was similar and unused mostly. We can put the DisplayName as a property on the scheme itself or inside of here like Description used to...
/// <summary> | ||
/// Gets the claims-principal with authenticated user identities. | ||
/// </summary> | ||
public ClaimsPrincipal Principal{ get; private set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
formatting
public AuthenticationTicket(ClaimsPrincipal principal, AuthenticationProperties properties, string authenticationScheme) | ||
{ | ||
AuthenticationScheme = authenticationScheme; | ||
Principal = principal ?? throw new ArgumentNullException(nameof(principal)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rejected syntax
public class AuthenticationToken | ||
{ | ||
public string Name { get; set; } | ||
public string Value { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make immutable and set via constructor?
private readonly AuthenticationOptions _options; | ||
private readonly object _lock = new object(); | ||
|
||
private IDictionary<string, AuthenticationScheme> _map = new Dictionary<string, AuthenticationScheme>(); // case sensitive? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specify comparer. Ordinal?
{ | ||
if (!_handlerMap.ContainsKey(path)) | ||
{ | ||
_handlerMap[path] = new List<AuthenticationScheme>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
List? I thought the point was that it would be a unique mapping. Urls usually map to a unique resource.
/// <summary> | ||
/// Builds the actual AuthenticationScheme instances from the AuthenticationOptions. | ||
/// </summary> | ||
public class DefaultAuthenticationSchemeProvider : IAuthenticationSchemeProvider |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AuthenticationSchemeProvider, remove Default
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call, dropped Default prefix across the board
{ | ||
throw new InvalidOperationException("Scheme already exists: " + scheme.Name); | ||
} | ||
lock (_lock) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Locking on modifications is insufficient, you also have to lock on lookups.
await handler.SignOutAsync(signOutContext); | ||
} | ||
|
||
public virtual Task SignInAsync(HttpContext httpContext, string authenticationScheme, ClaimsPrincipal principal) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why isn't this an extension?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah unneeded these already exists as extensions on httpContext, I'll remove
return SignInAsync(httpContext, authenticationScheme, principal, properties: null); | ||
} | ||
|
||
public virtual Task ForbidAsync(HttpContext httpContext, string authenticationScheme) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extension?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup nuked
/// <summary> | ||
/// Default claims transformation is a no-op. | ||
/// </summary> | ||
public class DefaultClaimsTransformation : IClaimsTransformation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NoOpClaimsTransformation
|
||
namespace Microsoft.AspNetCore.Authentication | ||
{ | ||
public class DefaultAuthenticationService : IAuthenticationService |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AuthenticationService
Replaced with #1151 |
Test showing dynamic add/remove of schemes: d92d923#diff-220317c67a0ea163cf751157519c7f66R29
cc @Eilon @Tratcher @davidfowl
Follow up items:
Test holes: