Require ClaimsPrincipal.Identity.IsAuthenticated for SignIn by default (#9482)

This commit is contained in:
Hao Kung 2019-05-31 15:43:00 -07:00 committed by GitHub
parent 2d2806b083
commit 5d6c2edab0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 91 additions and 28 deletions

View File

@ -51,6 +51,7 @@ namespace Microsoft.AspNetCore.Authentication
public string DefaultScheme { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public string DefaultSignInScheme { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public string DefaultSignOutScheme { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public bool RequireAuthenticatedSignIn { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public System.Collections.Generic.IDictionary<string, Microsoft.AspNetCore.Authentication.AuthenticationSchemeBuilder> SchemeMap { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Authentication.AuthenticationSchemeBuilder> Schemes { get { throw null; } }
public void AddScheme(string name, System.Action<Microsoft.AspNetCore.Authentication.AuthenticationSchemeBuilder> configureBuilder) { }

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Security.Claims;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication
@ -89,5 +90,10 @@ namespace Microsoft.AspNetCore.Authentication
/// Used as the default scheme by <see cref="IAuthenticationService.ForbidAsync(HttpContext, string, AuthenticationProperties)"/>.
/// </summary>
public string DefaultForbidScheme { get; set; }
/// <summary>
/// If true, SignIn should throw if attempted with a ClaimsPrincipal.Identity.IsAuthenticated = false.
/// </summary>
public bool RequireAuthenticatedSignIn { get; set; } = true;
}
}

View File

@ -33,8 +33,9 @@ namespace Microsoft.AspNetCore.Authentication
}
public partial class AuthenticationService : Microsoft.AspNetCore.Authentication.IAuthenticationService
{
public AuthenticationService(Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider schemes, Microsoft.AspNetCore.Authentication.IAuthenticationHandlerProvider handlers, Microsoft.AspNetCore.Authentication.IClaimsTransformation transform) { }
public AuthenticationService(Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider schemes, Microsoft.AspNetCore.Authentication.IAuthenticationHandlerProvider handlers, Microsoft.AspNetCore.Authentication.IClaimsTransformation transform, Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Authentication.AuthenticationOptions> options) { }
public Microsoft.AspNetCore.Authentication.IAuthenticationHandlerProvider Handlers { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public Microsoft.AspNetCore.Authentication.AuthenticationOptions Options { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider Schemes { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public Microsoft.AspNetCore.Authentication.IClaimsTransformation Transform { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
[System.Diagnostics.DebuggerStepThroughAttribute]

View File

@ -6,6 +6,7 @@ using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Authentication
{
@ -20,11 +21,13 @@ namespace Microsoft.AspNetCore.Authentication
/// <param name="schemes">The <see cref="IAuthenticationSchemeProvider"/>.</param>
/// <param name="handlers">The <see cref="IAuthenticationRequestHandler"/>.</param>
/// <param name="transform">The <see cref="IClaimsTransformation"/>.</param>
public AuthenticationService(IAuthenticationSchemeProvider schemes, IAuthenticationHandlerProvider handlers, IClaimsTransformation transform)
/// <param name="options">The <see cref="AuthenticationOptions"/>.</param>
public AuthenticationService(IAuthenticationSchemeProvider schemes, IAuthenticationHandlerProvider handlers, IClaimsTransformation transform, IOptions<AuthenticationOptions> options)
{
Schemes = schemes;
Handlers = handlers;
Transform = transform;
Options = options.Value;
}
/// <summary>
@ -42,6 +45,11 @@ namespace Microsoft.AspNetCore.Authentication
/// </summary>
public IClaimsTransformation Transform { get; }
/// <summary>
/// The <see cref="AuthenticationOptions"/>.
/// </summary>
public AuthenticationOptions Options { get; }
/// <summary>
/// Authenticate for the specified authentication scheme.
/// </summary>
@ -146,6 +154,18 @@ namespace Microsoft.AspNetCore.Authentication
throw new ArgumentNullException(nameof(principal));
}
if (Options.RequireAuthenticatedSignIn)
{
if (principal.Identity == null)
{
throw new InvalidOperationException("SignInAsync when principal.Identity == null is not allowed when AuthenticationOptions.RequireAuthenticatedSignIn is true.");
}
if (!principal.Identity.IsAuthenticated)
{
throw new InvalidOperationException("SignInAsync when principal.Identity.IsAuthenticated is false is not allowed when AuthenticationOptions.RequireAuthenticatedSignIn is true.");
}
}
if (scheme == null)
{
var defaultScheme = await Schemes.GetDefaultSignInSchemeAsync();

View File

@ -57,6 +57,35 @@ namespace Microsoft.AspNetCore.Authentication
Assert.Contains("base", ex.Message);
}
[Fact]
public async Task CanOnlySignInWithIsAuthenticated()
{
var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o =>
{
o.AddScheme<SignInHandler>("signin", "whatever");
}).BuildServiceProvider();
var context = new DefaultHttpContext();
context.RequestServices = services;
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignInAsync("signin", new ClaimsPrincipal(), null));
await context.SignInAsync("signin", new ClaimsPrincipal(new ClaimsIdentity("whatever")), null);
}
[Fact]
public async Task CanSignInWithoutIsAuthenticated()
{
var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o =>
{
o.AddScheme<SignInHandler>("signin", "whatever");
o.RequireAuthenticatedSignIn = false;
}).BuildServiceProvider();
var context = new DefaultHttpContext();
context.RequestServices = services;
await context.SignInAsync("signin", new ClaimsPrincipal(), null);
await context.SignInAsync("signin", new ClaimsPrincipal(new ClaimsIdentity("whatever")), null);
}
[Fact]
public async Task CanOnlySignInIfSupported()
{
@ -70,12 +99,12 @@ namespace Microsoft.AspNetCore.Authentication
var context = new DefaultHttpContext();
context.RequestServices = services;
await context.SignInAsync("uber", new ClaimsPrincipal(), null);
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignInAsync("base", new ClaimsPrincipal(), null));
await context.SignInAsync("uber", new ClaimsPrincipal(new ClaimsIdentity("whatever")), null);
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignInAsync("base", new ClaimsPrincipal(new ClaimsIdentity("whatever")), null));
Assert.Contains("uber", ex.Message);
Assert.Contains("signin", ex.Message);
await context.SignInAsync("signin", new ClaimsPrincipal(), null);
ex = await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignInAsync("signout", new ClaimsPrincipal(), null));
await context.SignInAsync("signin", new ClaimsPrincipal(new ClaimsIdentity("whatever")), null);
ex = await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignInAsync("signout", new ClaimsPrincipal(new ClaimsIdentity("whatever")), null));
Assert.Contains("uber", ex.Message);
Assert.Contains("signin", ex.Message);
}
@ -117,7 +146,7 @@ namespace Microsoft.AspNetCore.Authentication
await context.ForbidAsync();
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignOutAsync());
Assert.Contains("cannot be used for SignOutAsync", ex.Message);
ex = await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignInAsync(new ClaimsPrincipal()));
ex = await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity("whatever"))));
Assert.Contains("cannot be used for SignInAsync", ex.Message);
}
@ -136,7 +165,7 @@ namespace Microsoft.AspNetCore.Authentication
await context.ChallengeAsync();
await context.ForbidAsync();
await context.SignOutAsync();
await context.SignInAsync(new ClaimsPrincipal());
await context.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity("whatever")));
}
[Fact]
@ -154,7 +183,7 @@ namespace Microsoft.AspNetCore.Authentication
await context.ChallengeAsync();
await context.ForbidAsync();
await context.SignOutAsync();
await context.SignInAsync(new ClaimsPrincipal());
await context.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity("whatever")));
}
[Fact]
@ -172,7 +201,7 @@ namespace Microsoft.AspNetCore.Authentication
await context.ChallengeAsync();
await context.ForbidAsync();
await context.SignOutAsync();
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignInAsync(new ClaimsPrincipal()));
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity("whatever"))));
Assert.Contains("cannot be used for SignInAsync", ex.Message);
}

View File

@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
request.Headers.Add("Cookie", authCookie);
response = await Client.SendAsync(request);
await AssertAuthorizeResponse(response);
await AssertForbiddenResponse(response);
authCookie = await GetAuthCookieAsync("LoginClaimAB");
request = new HttpRequestMessage(HttpMethod.Get, action);
@ -243,7 +243,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
request.Headers.Add("Cookie", authCookie);
response = await Client.SendAsync(request);
await AssertAuthorizeResponse(response);
await AssertForbiddenResponse(response);
authCookie = await GetAuthCookieAsync("LoginClaimAB");
request = new HttpRequestMessage(HttpMethod.Get, url);
@ -259,6 +259,12 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
Assert.Equal("/Account/Login", response.Headers.Location.LocalPath);
}
private async Task AssertForbiddenResponse(HttpResponseMessage response)
{
await response.AssertStatusCodeAsync(HttpStatusCode.Redirect);
Assert.Equal("/Account/AccessDenied", response.Headers.Location.LocalPath);
}
private async Task<string> GetAuthCookieAsync(string action)
{
var response = await Client.PostAsync($"Login/{action}", null);

View File

@ -24,7 +24,7 @@ namespace SecurityWebSite.Controllers
[HttpPost]
public async Task<IActionResult> LoginClaimA()
{
var identity = new ClaimsIdentity(new[] { new Claim("ClaimA", "Value") });
var identity = new ClaimsIdentity(new[] { new Claim("ClaimA", "Value") }, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
return Ok();
}
@ -32,7 +32,7 @@ namespace SecurityWebSite.Controllers
[HttpPost]
public async Task<IActionResult> LoginClaimAB()
{
var identity = new ClaimsIdentity(new[] { new Claim("ClaimA", "Value"), new Claim("ClaimB", "Value") });
var identity = new ClaimsIdentity(new[] { new Claim("ClaimA", "Value"), new Claim("ClaimB", "Value") }, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
return Ok();
}

View File

@ -1001,7 +1001,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
.Configure(app =>
{
app.UseAuthentication();
app.Run(context => context.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(new ClaimsIdentity())));
app.Run(context => context.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(new ClaimsIdentity("whatever"))));
})
.ConfigureServices(services =>
{
@ -1024,7 +1024,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
.Configure(app =>
{
app.UseAuthentication();
app.Run(context => context.SignInAsync("Cookie1", new ClaimsPrincipal(new ClaimsIdentity())));
app.Run(context => context.SignInAsync("Cookie1", new ClaimsPrincipal(new ClaimsIdentity("whatever"))));
})
.ConfigureServices(services =>
{
@ -1048,7 +1048,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
{
app.UseAuthentication();
app.Map("/notlogin", signoutApp => signoutApp.Run(context => context.SignInAsync("Cookies",
new ClaimsPrincipal())));
new ClaimsPrincipal(new ClaimsIdentity("whatever")))));
})
.ConfigureServices(services => services.AddAuthentication().AddCookie(o => o.LoginPath = new PathString("/login")));
var server = new TestServer(builder);
@ -1065,7 +1065,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
.Configure(app =>
{
app.UseAuthentication();
app.Map("/login", signoutApp => signoutApp.Run(context => context.SignInAsync("Cookies", new ClaimsPrincipal())));
app.Map("/login", signoutApp => signoutApp.Run(context => context.SignInAsync("Cookies", new ClaimsPrincipal(new ClaimsIdentity("whatever")))));
})
.ConfigureServices(services => services.AddAuthentication().AddCookie(o => o.LoginPath = new PathString("/login")));

View File

@ -101,7 +101,7 @@ namespace Microsoft.AspNetCore.Authentication
Assert.Equal(1, handler1.SignOutCount);
Assert.Equal(0, handler2.SignOutCount);
await context.SignInAsync("forward", new ClaimsPrincipal());
await context.SignInAsync("forward", new ClaimsPrincipal(new ClaimsIdentity("whatever")));
Assert.Equal(1, handler1.SignInCount);
Assert.Equal(0, handler2.SignInCount);
}
@ -156,7 +156,7 @@ namespace Microsoft.AspNetCore.Authentication
Assert.Equal(1, handler1.SignOutCount);
Assert.Equal(0, handler2.SignOutCount);
await context.SignInAsync("forward", new ClaimsPrincipal());
await context.SignInAsync("forward", new ClaimsPrincipal(new ClaimsIdentity("whatever")));
Assert.Equal(1, handler1.SignInCount);
Assert.Equal(0, handler2.SignInCount);
}
@ -216,7 +216,7 @@ namespace Microsoft.AspNetCore.Authentication
Assert.Equal(1, handler1.SignOutCount);
Assert.Equal(0, handler2.SignOutCount);
await context.SignInAsync("forward", new ClaimsPrincipal());
await context.SignInAsync("forward", new ClaimsPrincipal(new ClaimsIdentity("whatever")));
Assert.Equal(1, handler1.SignInCount);
Assert.Equal(0, handler2.SignInCount);
}
@ -268,7 +268,7 @@ namespace Microsoft.AspNetCore.Authentication
Assert.Equal(1, handler1.SignOutCount);
Assert.Equal(0, handler2.SignOutCount);
await context.SignInAsync("forward", new ClaimsPrincipal());
await context.SignInAsync("forward", new ClaimsPrincipal(new ClaimsIdentity("whatever")));
Assert.Equal(1, handler1.SignInCount);
Assert.Equal(0, handler2.SignInCount);
}
@ -325,7 +325,7 @@ namespace Microsoft.AspNetCore.Authentication
Assert.Equal(1, handler1.SignOutCount);
Assert.Equal(0, handler2.SignOutCount);
await context.SignInAsync("forward", new ClaimsPrincipal());
await context.SignInAsync("forward", new ClaimsPrincipal(new ClaimsIdentity("whatever")));
Assert.Equal(0, handler1.SignInCount);
Assert.Equal(1, handler2.SignInCount);
}

View File

@ -70,7 +70,7 @@ namespace Microsoft.AspNetCore.Authentication
if (SupportsSignIn)
{
await context.SignInAsync(new ClaimsPrincipal());
await context.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity("whatever")));
Assert.Equal(1, forwardDefault.SignInCount);
}
else
@ -107,7 +107,7 @@ namespace Microsoft.AspNetCore.Authentication
var context = new DefaultHttpContext();
context.RequestServices = sp;
await context.SignInAsync(new ClaimsPrincipal());
await context.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity("whatever")));
Assert.Equal(1, specific.SignInCount);
Assert.Equal(0, specific.AuthenticateCount);
Assert.Equal(0, specific.ForbidCount);
@ -330,7 +330,7 @@ namespace Microsoft.AspNetCore.Authentication
if (SupportsSignIn)
{
await context.SignInAsync(new ClaimsPrincipal());
await context.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity("whatever")));
Assert.Equal(1, selector.SignInCount);
}
else
@ -399,7 +399,7 @@ namespace Microsoft.AspNetCore.Authentication
if (SupportsSignIn)
{
await context.SignInAsync(new ClaimsPrincipal());
await context.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity("whatever")));
Assert.Equal(1, forwardDefault.SignInCount);
}
else
@ -473,7 +473,7 @@ namespace Microsoft.AspNetCore.Authentication
if (SupportsSignIn)
{
await context.SignInAsync(new ClaimsPrincipal());
await context.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity("whatever")));
Assert.Equal(1, specific.SignInCount);
}
else