[Fixes #1133] Limit the path on the nonce and correlation id cookies
This commit is contained in:
parent
200ce72312
commit
879f0b7f40
|
|
@ -886,16 +886,21 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
|
|||
throw new ArgumentNullException(nameof(nonce));
|
||||
}
|
||||
|
||||
var options = new CookieOptions
|
||||
{
|
||||
HttpOnly = true,
|
||||
SameSite = Http.SameSiteMode.None,
|
||||
Path = OriginalPathBase + Options.CallbackPath,
|
||||
Secure = Request.IsHttps,
|
||||
Expires = Clock.UtcNow.Add(Options.ProtocolValidator.NonceLifetime)
|
||||
};
|
||||
|
||||
Options.ConfigureNonceCookie?.Invoke(Context, options);
|
||||
|
||||
Response.Cookies.Append(
|
||||
OpenIdConnectDefaults.CookieNoncePrefix + Options.StringDataFormat.Protect(nonce),
|
||||
NonceProperty,
|
||||
new CookieOptions
|
||||
{
|
||||
HttpOnly = true,
|
||||
SameSite = Http.SameSiteMode.None,
|
||||
Secure = Request.IsHttps,
|
||||
Expires = Clock.UtcNow.Add(Options.ProtocolValidator.NonceLifetime)
|
||||
});
|
||||
options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -924,10 +929,13 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
|
|||
var cookieOptions = new CookieOptions
|
||||
{
|
||||
HttpOnly = true,
|
||||
Path = OriginalPathBase + Options.CallbackPath,
|
||||
SameSite = Http.SameSiteMode.None,
|
||||
Secure = Request.IsHttps
|
||||
};
|
||||
|
||||
Options.ConfigureNonceCookie?.Invoke(Context, cookieOptions);
|
||||
|
||||
Response.Cookies.Delete(nonceKey, cookieOptions);
|
||||
return nonce;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -262,5 +262,11 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
|
|||
/// remote OpenID Connect provider as an authorization/logout request parameter.
|
||||
/// </summary>
|
||||
public bool DisableTelemetry { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an action that can override the nonce cookie options before the
|
||||
/// cookie gets added to the response.
|
||||
/// </summary>
|
||||
public Action<HttpContext, CookieOptions> ConfigureNonceCookie { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,6 +87,8 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
|
|||
Secure = Request.IsHttps
|
||||
};
|
||||
|
||||
Options.ConfigureStateCookie?.Invoke(Context, cookieOptions);
|
||||
|
||||
Response.Cookies.Delete(StateCookie, cookieOptions);
|
||||
|
||||
var accessToken = await ObtainAccessTokenAsync(requestToken, oauthVerifier);
|
||||
|
|
@ -159,6 +161,8 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
|
|||
Expires = Clock.UtcNow.Add(Options.RemoteAuthenticationTimeout),
|
||||
};
|
||||
|
||||
Options.ConfigureStateCookie?.Invoke(Context, cookieOptions);
|
||||
|
||||
Response.Cookies.Append(StateCookie, Options.StateDataFormat.Protect(requestToken), cookieOptions);
|
||||
|
||||
var redirectContext = new TwitterRedirectToAuthorizationEndpointContext(Context, Scheme, Options, properties, twitterAuthenticationEndpoint);
|
||||
|
|
|
|||
|
|
@ -58,6 +58,12 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
|
|||
/// </summary>
|
||||
public ISecureDataFormat<RequestToken> StateDataFormat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an action that can override the state cookie options before the
|
||||
/// cookie gets added to the response.
|
||||
/// </summary>
|
||||
public Action<HttpContext, CookieOptions> ConfigureStateCookie { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="TwitterEvents"/> used to handle authentication events.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -205,9 +205,12 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
HttpOnly = true,
|
||||
SameSite = SameSiteMode.None,
|
||||
Secure = Request.IsHttps,
|
||||
Path = OriginalPathBase + Options.CallbackPath,
|
||||
Expires = Clock.UtcNow.Add(Options.RemoteAuthenticationTimeout),
|
||||
};
|
||||
|
||||
Options.ConfigureCorrelationIdCookie?.Invoke(Context, cookieOptions);
|
||||
|
||||
properties.Items[CorrelationProperty] = correlationId;
|
||||
|
||||
var cookieName = CorrelationPrefix + Scheme.Name + "." + correlationId;
|
||||
|
|
@ -243,9 +246,13 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
var cookieOptions = new CookieOptions
|
||||
{
|
||||
HttpOnly = true,
|
||||
Path = OriginalPathBase + Options.CallbackPath,
|
||||
SameSite = SameSiteMode.None,
|
||||
Secure = Request.IsHttps
|
||||
};
|
||||
|
||||
Options.ConfigureCorrelationIdCookie?.Invoke(Context, cookieOptions);
|
||||
|
||||
Response.Cookies.Delete(cookieName, cookieOptions);
|
||||
|
||||
if (!string.Equals(correlationCookie, CorrelationMarker, StringComparison.Ordinal))
|
||||
|
|
|
|||
|
|
@ -87,5 +87,11 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
/// the size of the final authentication cookie.
|
||||
/// </summary>
|
||||
public bool SaveTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an action that can override the correlation id cookie options before the
|
||||
/// cookie gets added to the response.
|
||||
/// </summary>
|
||||
public Action<HttpContext, CookieOptions> ConfigureCorrelationIdCookie { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
|
@ -165,6 +166,70 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
|
|||
Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RedirectToIdentityProvider_SetsCorrelationIdCookiePath_ToCallBackPath()
|
||||
{
|
||||
var server = CreateServer(
|
||||
app => { },
|
||||
s => s.AddOAuthAuthentication(
|
||||
"Weblie",
|
||||
opt =>
|
||||
{
|
||||
opt.ClientId = "Test Id";
|
||||
opt.ClientSecret = "secret";
|
||||
opt.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||
opt.AuthorizationEndpoint = "https://example.com/provider/login";
|
||||
opt.TokenEndpoint = "https://example.com/provider/token";
|
||||
opt.CallbackPath = "/oauth-callback";
|
||||
}),
|
||||
ctx =>
|
||||
{
|
||||
ctx.ChallengeAsync("Weblie").ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
return true;
|
||||
});
|
||||
|
||||
var transaction = await server.SendAsync("https://www.example.com/challenge");
|
||||
var res = transaction.Response;
|
||||
|
||||
Assert.Equal(HttpStatusCode.Redirect, res.StatusCode);
|
||||
Assert.NotNull(res.Headers.Location);
|
||||
var setCookie = Assert.Single(res.Headers, h => h.Key == "Set-Cookie");
|
||||
var correlation = Assert.Single(setCookie.Value, v => v.StartsWith(".AspNetCore.Correlation."));
|
||||
Assert.Contains("path=/oauth-callback", correlation);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RedirectToAuthorizeEndpoint_CorrelationIdCookieOptions_CanBeOverriden()
|
||||
{
|
||||
var server = CreateServer(
|
||||
app => { },
|
||||
s => s.AddOAuthAuthentication(
|
||||
"Weblie",
|
||||
opt =>
|
||||
{
|
||||
opt.ClientId = "Test Id";
|
||||
opt.ClientSecret = "secret";
|
||||
opt.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||
opt.AuthorizationEndpoint = "https://example.com/provider/login";
|
||||
opt.TokenEndpoint = "https://example.com/provider/token";
|
||||
opt.CallbackPath = "/oauth-callback";
|
||||
opt.ConfigureCorrelationIdCookie = (ctx, options) => options.Path = "/";
|
||||
}),
|
||||
ctx =>
|
||||
{
|
||||
ctx.ChallengeAsync("Weblie").ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
return true;
|
||||
});
|
||||
|
||||
var transaction = await server.SendAsync("https://www.example.com/challenge");
|
||||
var res = transaction.Response;
|
||||
|
||||
Assert.Equal(HttpStatusCode.Redirect, res.StatusCode);
|
||||
Assert.NotNull(res.Headers.Location);
|
||||
var setCookie = Assert.Single(res.Headers, h => h.Key == "Set-Cookie");
|
||||
var correlation = Assert.Single(setCookie.Value, v => v.StartsWith(".AspNetCore.Correlation."));
|
||||
Assert.Contains("path=/", correlation);
|
||||
}
|
||||
|
||||
private static TestServer CreateServer(Action<IApplicationBuilder> configure, Action<IServiceCollection> configureServices, Func<HttpContext, bool> handler)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -60,6 +60,108 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
|
|||
OpenIdConnectParameterNames.VersionTelemetry);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RedirectToIdentityProvider_SetsNonceCookiePath_ToCallBackPath()
|
||||
{
|
||||
var setting = new TestSettings(opt =>
|
||||
{
|
||||
opt.ClientId = "Test Id";
|
||||
opt.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||
opt.Configuration = new OpenIdConnectConfiguration
|
||||
{
|
||||
AuthorizationEndpoint = "https://example.com/provider/login"
|
||||
};
|
||||
});
|
||||
|
||||
var server = setting.CreateTestServer();
|
||||
|
||||
var transaction = await server.SendAsync(DefaultHost + TestServerBuilder.Challenge);
|
||||
var res = transaction.Response;
|
||||
|
||||
Assert.Equal(HttpStatusCode.Redirect, res.StatusCode);
|
||||
Assert.NotNull(res.Headers.Location);
|
||||
var setCookie = Assert.Single(res.Headers, h => h.Key == "Set-Cookie");
|
||||
var nonce = Assert.Single(setCookie.Value, v => v.StartsWith(OpenIdConnectDefaults.CookieNoncePrefix));
|
||||
Assert.Contains("path=/signin-oidc", nonce);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RedirectToIdentityProvider_NonceCookieOptions_CanBeOverriden()
|
||||
{
|
||||
var setting = new TestSettings(opt =>
|
||||
{
|
||||
opt.ClientId = "Test Id";
|
||||
opt.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||
opt.Configuration = new OpenIdConnectConfiguration
|
||||
{
|
||||
AuthorizationEndpoint = "https://example.com/provider/login"
|
||||
};
|
||||
opt.ConfigureNonceCookie = (ctx, options) => options.Path = "/";
|
||||
});
|
||||
|
||||
var server = setting.CreateTestServer();
|
||||
|
||||
var transaction = await server.SendAsync(DefaultHost + TestServerBuilder.Challenge);
|
||||
var res = transaction.Response;
|
||||
|
||||
Assert.Equal(HttpStatusCode.Redirect, res.StatusCode);
|
||||
Assert.NotNull(res.Headers.Location);
|
||||
var setCookie = Assert.Single(res.Headers, h => h.Key == "Set-Cookie");
|
||||
var nonce = Assert.Single(setCookie.Value, v => v.StartsWith(OpenIdConnectDefaults.CookieNoncePrefix));
|
||||
Assert.Contains("path=/", nonce);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RedirectToIdentityProvider_SetsCorrelationIdCookiePath_ToCallBackPath()
|
||||
{
|
||||
var setting = new TestSettings(opt =>
|
||||
{
|
||||
opt.ClientId = "Test Id";
|
||||
opt.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||
opt.Configuration = new OpenIdConnectConfiguration
|
||||
{
|
||||
AuthorizationEndpoint = "https://example.com/provider/login"
|
||||
};
|
||||
});
|
||||
|
||||
var server = setting.CreateTestServer();
|
||||
|
||||
var transaction = await server.SendAsync(DefaultHost + TestServerBuilder.Challenge);
|
||||
var res = transaction.Response;
|
||||
|
||||
Assert.Equal(HttpStatusCode.Redirect, res.StatusCode);
|
||||
Assert.NotNull(res.Headers.Location);
|
||||
var setCookie = Assert.Single(res.Headers, h => h.Key == "Set-Cookie");
|
||||
var correlation = Assert.Single(setCookie.Value, v => v.StartsWith(".AspNetCore.Correlation."));
|
||||
Assert.Contains("path=/signin-oidc", correlation);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RedirectToIdentityProvider_CorrelationIdCookieOptions_CanBeOverriden()
|
||||
{
|
||||
var setting = new TestSettings(opt =>
|
||||
{
|
||||
opt.ClientId = "Test Id";
|
||||
opt.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||
opt.Configuration = new OpenIdConnectConfiguration
|
||||
{
|
||||
AuthorizationEndpoint = "https://example.com/provider/login"
|
||||
};
|
||||
opt.ConfigureCorrelationIdCookie = (ctx, options) => options.Path = "/";
|
||||
});
|
||||
|
||||
var server = setting.CreateTestServer();
|
||||
|
||||
var transaction = await server.SendAsync(DefaultHost + TestServerBuilder.Challenge);
|
||||
var res = transaction.Response;
|
||||
|
||||
Assert.Equal(HttpStatusCode.Redirect, res.StatusCode);
|
||||
Assert.NotNull(res.Headers.Location);
|
||||
var setCookie = Assert.Single(res.Headers, h => h.Key == "Set-Cookie");
|
||||
var correlation = Assert.Single(setCookie.Value, v => v.StartsWith(".AspNetCore.Correlation."));
|
||||
Assert.Contains("path=/", correlation);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EndSessionRequestDoesNotIncludeTelemetryParametersWhenDisabled()
|
||||
{
|
||||
|
|
@ -173,7 +275,8 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
|
|||
[Fact]
|
||||
public async Task SignOut_WithMissingConfig_Throws()
|
||||
{
|
||||
var setting = new TestSettings(opt => {
|
||||
var setting = new TestSettings(opt =>
|
||||
{
|
||||
opt.ClientId = "Test Id";
|
||||
opt.Configuration = new OpenIdConnectConfiguration();
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue