Replace configure method on Twitter, RemoteAuthentication, and OpenIdConnect options with CookieBuilder

This commit is contained in:
Nate McMaster 2017-06-29 16:23:31 -07:00
parent 66b939725e
commit 968237d751
9 changed files with 203 additions and 102 deletions

View File

@ -275,8 +275,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// <returns>A task executing the callback procedure</returns> /// <returns>A task executing the callback procedure</returns>
protected virtual Task<bool> HandleSignOutCallbackAsync() protected virtual Task<bool> HandleSignOutCallbackAsync()
{ {
StringValues protectedState; if (Request.Query.TryGetValue(OpenIdConnectParameterNames.State, out StringValues protectedState))
if (Request.Query.TryGetValue(OpenIdConnectParameterNames.State, out protectedState))
{ {
var properties = Options.StateDataFormat.Unprotect(protectedState); var properties = Options.StateDataFormat.Unprotect(protectedState);
if (!string.IsNullOrEmpty(properties?.RedirectUri)) if (!string.IsNullOrEmpty(properties?.RedirectUri))
@ -505,8 +504,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
return HandleRequestResult.Fail(Resources.MessageStateIsInvalid); return HandleRequestResult.Fail(Resources.MessageStateIsInvalid);
} }
string userstate = null; properties.Items.TryGetValue(OpenIdConnectDefaults.UserstatePropertiesKey, out string userstate);
properties.Items.TryGetValue(OpenIdConnectDefaults.UserstatePropertiesKey, out userstate);
authorizationResponse.State = userstate; authorizationResponse.State = userstate;
if (!ValidateCorrelationId(properties)) if (!ValidateCorrelationId(properties))
@ -859,8 +857,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
if (!string.IsNullOrEmpty(message.ExpiresIn)) if (!string.IsNullOrEmpty(message.ExpiresIn))
{ {
int value; if (int.TryParse(message.ExpiresIn, NumberStyles.Integer, CultureInfo.InvariantCulture, out int value))
if (int.TryParse(message.ExpiresIn, NumberStyles.Integer, CultureInfo.InvariantCulture, out value))
{ {
var expiresAt = Clock.UtcNow + TimeSpan.FromSeconds(value); var expiresAt = Clock.UtcNow + TimeSpan.FromSeconds(value);
// https://www.w3.org/TR/xmlschema-2/#dateTime // https://www.w3.org/TR/xmlschema-2/#dateTime
@ -885,21 +882,12 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
throw new ArgumentNullException(nameof(nonce)); throw new ArgumentNullException(nameof(nonce));
} }
var options = new CookieOptions var cookieOptions = Options.NonceCookie.Build(Context, Clock.UtcNow);
{
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( Response.Cookies.Append(
OpenIdConnectDefaults.CookieNoncePrefix + Options.StringDataFormat.Protect(nonce), Options.NonceCookie.Name + Options.StringDataFormat.Protect(nonce),
NonceProperty, NonceProperty,
options); cookieOptions);
} }
/// <summary> /// <summary>
@ -918,23 +906,14 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
foreach (var nonceKey in Request.Cookies.Keys) foreach (var nonceKey in Request.Cookies.Keys)
{ {
if (nonceKey.StartsWith(OpenIdConnectDefaults.CookieNoncePrefix)) if (nonceKey.StartsWith(Options.NonceCookie.Name))
{ {
try try
{ {
var nonceDecodedValue = Options.StringDataFormat.Unprotect(nonceKey.Substring(OpenIdConnectDefaults.CookieNoncePrefix.Length, nonceKey.Length - OpenIdConnectDefaults.CookieNoncePrefix.Length)); var nonceDecodedValue = Options.StringDataFormat.Unprotect(nonceKey.Substring(Options.NonceCookie.Name.Length, nonceKey.Length - Options.NonceCookie.Name.Length));
if (nonceDecodedValue == nonce) if (nonceDecodedValue == nonce)
{ {
var cookieOptions = new CookieOptions var cookieOptions = Options.NonceCookie.Build(Context, Clock.UtcNow);
{
HttpOnly = true,
Path = OriginalPathBase + Options.CallbackPath,
SameSite = Http.SameSiteMode.None,
Secure = Request.IsHttps
};
Options.ConfigureNonceCookie?.Invoke(Context, cookieOptions);
Response.Cookies.Delete(nonceKey, cookieOptions); Response.Cookies.Delete(nonceKey, cookieOptions);
return nonce; return nonce;
} }
@ -1170,8 +1149,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
validationParameters.IssuerSigningKeys = validationParameters.IssuerSigningKeys?.Concat(_configuration.SigningKeys) ?? _configuration.SigningKeys; validationParameters.IssuerSigningKeys = validationParameters.IssuerSigningKeys?.Concat(_configuration.SigningKeys) ?? _configuration.SigningKeys;
} }
SecurityToken validatedToken = null; var principal = Options.SecurityTokenValidator.ValidateToken(idToken, validationParameters, out SecurityToken validatedToken);
var principal = Options.SecurityTokenValidator.ValidateToken(idToken, validationParameters, out validatedToken);
jwt = validatedToken as JwtSecurityToken; jwt = validatedToken as JwtSecurityToken;
if (jwt == null) if (jwt == null)
{ {

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using Microsoft.AspNetCore.Authentication.Internal;
using Microsoft.AspNetCore.Authentication.OAuth.Claims; using Microsoft.AspNetCore.Authentication.OAuth.Claims;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Protocols;
@ -17,6 +18,8 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// </summary> /// </summary>
public class OpenIdConnectOptions : RemoteAuthenticationOptions public class OpenIdConnectOptions : RemoteAuthenticationOptions
{ {
private CookieBuilder _nonceCookieBuilder;
/// <summary> /// <summary>
/// Initializes a new <see cref="OpenIdConnectOptions"/> /// Initializes a new <see cref="OpenIdConnectOptions"/>
/// </summary> /// </summary>
@ -65,6 +68,14 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
ClaimActions.MapUniqueJsonKey("family_name", "family_name"); ClaimActions.MapUniqueJsonKey("family_name", "family_name");
ClaimActions.MapUniqueJsonKey("profile", "profile"); ClaimActions.MapUniqueJsonKey("profile", "profile");
ClaimActions.MapUniqueJsonKey("email", "email"); ClaimActions.MapUniqueJsonKey("email", "email");
_nonceCookieBuilder = new OpenIdConnectNonceCookieBuilder(this)
{
Name = OpenIdConnectDefaults.CookieNoncePrefix,
HttpOnly = true,
SameSite = SameSiteMode.None,
SecurePolicy = CookieSecurePolicy.SameAsRequest,
};
} }
/// <summary> /// <summary>
@ -145,8 +156,8 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// </summary> /// </summary>
public new OpenIdConnectEvents Events public new OpenIdConnectEvents Events
{ {
get { return (OpenIdConnectEvents)base.Events; } get => (OpenIdConnectEvents)base.Events;
set { base.Events = value; } set => base.Events = value;
} }
/// <summary> /// <summary>
@ -259,9 +270,40 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
public bool DisableTelemetry { get; set; } public bool DisableTelemetry { get; set; }
/// <summary> /// <summary>
/// Gets or sets an action that can override the nonce cookie options before the /// Determines the settings used to create the nonce cookie before the
/// cookie gets added to the response. /// cookie gets added to the response.
/// </summary> /// </summary>
public Action<HttpContext, CookieOptions> ConfigureNonceCookie { get; set; } /// <remarks>
/// The value of <see cref="CookieBuilder.Name"/> is treated as the prefix to the cookie name, and defaults to <seealso cref="OpenIdConnectDefaults.CookieNoncePrefix"/>.
/// </remarks>
public CookieBuilder NonceCookie
{
get => _nonceCookieBuilder;
set => _nonceCookieBuilder = value ?? throw new ArgumentNullException(nameof(value));
}
private class OpenIdConnectNonceCookieBuilder : RequestPathBaseCookieBuilder
{
private readonly OpenIdConnectOptions _options;
public OpenIdConnectNonceCookieBuilder(OpenIdConnectOptions oidcOptions)
{
_options = oidcOptions;
}
protected override string AdditionalPath => _options.CallbackPath;
public override CookieOptions Build(HttpContext context, DateTimeOffset expiresFrom)
{
var cookieOptions = base.Build(context, expiresFrom);
if (!Expiration.HasValue || !cookieOptions.Expires.HasValue)
{
cookieOptions.Expires = expiresFrom.Add(_options.ProtocolValidator.NonceLifetime);
}
return cookieOptions;
}
}
} }
} }

View File

@ -10,7 +10,6 @@ using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities; using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -23,7 +22,6 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
internal class TwitterHandler : RemoteAuthenticationHandler<TwitterOptions> internal class TwitterHandler : RemoteAuthenticationHandler<TwitterOptions>
{ {
private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private const string StateCookie = "__TwitterState";
private const string RequestTokenEndpoint = "https://api.twitter.com/oauth/request_token"; private const string RequestTokenEndpoint = "https://api.twitter.com/oauth/request_token";
private const string AuthenticationEndpoint = "https://api.twitter.com/oauth/authenticate?oauth_token="; private const string AuthenticationEndpoint = "https://api.twitter.com/oauth/authenticate?oauth_token=";
private const string AccessTokenEndpoint = "https://api.twitter.com/oauth/access_token"; private const string AccessTokenEndpoint = "https://api.twitter.com/oauth/access_token";
@ -50,7 +48,7 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
{ {
AuthenticationProperties properties = null; AuthenticationProperties properties = null;
var query = Request.Query; var query = Request.Query;
var protectedRequestToken = Request.Cookies[StateCookie]; var protectedRequestToken = Request.Cookies[Options.StateCookie.Name];
var requestToken = Options.StateDataFormat.Unprotect(protectedRequestToken); var requestToken = Options.StateDataFormat.Unprotect(protectedRequestToken);
@ -80,16 +78,9 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
return HandleRequestResult.Fail("Missing or blank oauth_verifier"); return HandleRequestResult.Fail("Missing or blank oauth_verifier");
} }
var cookieOptions = new CookieOptions var cookieOptions = Options.StateCookie.Build(Context, Clock.UtcNow);
{
HttpOnly = true,
SameSite = SameSiteMode.Lax,
Secure = Request.IsHttps
};
Options.ConfigureStateCookie?.Invoke(Context, cookieOptions); Response.Cookies.Delete(Options.StateCookie.Name, cookieOptions);
Response.Cookies.Delete(StateCookie, cookieOptions);
var accessToken = await ObtainAccessTokenAsync(requestToken, oauthVerifier); var accessToken = await ObtainAccessTokenAsync(requestToken, oauthVerifier);
@ -144,17 +135,9 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
var requestToken = await ObtainRequestTokenAsync(BuildRedirectUri(Options.CallbackPath), properties); var requestToken = await ObtainRequestTokenAsync(BuildRedirectUri(Options.CallbackPath), properties);
var twitterAuthenticationEndpoint = AuthenticationEndpoint + requestToken.Token; var twitterAuthenticationEndpoint = AuthenticationEndpoint + requestToken.Token;
var cookieOptions = new CookieOptions var cookieOptions = Options.StateCookie.Build(Context, Clock.UtcNow);
{
HttpOnly = true,
SameSite = SameSiteMode.Lax,
Secure = Request.IsHttps,
Expires = Clock.UtcNow.Add(Options.RemoteAuthenticationTimeout),
};
Options.ConfigureStateCookie?.Invoke(Context, cookieOptions); Response.Cookies.Append(Options.StateCookie.Name, Options.StateDataFormat.Protect(requestToken), cookieOptions);
Response.Cookies.Append(StateCookie, Options.StateDataFormat.Protect(requestToken), cookieOptions);
var redirectContext = new RedirectContext<TwitterOptions>(Context, Scheme, Options, properties, twitterAuthenticationEndpoint); var redirectContext = new RedirectContext<TwitterOptions>(Context, Scheme, Options, properties, twitterAuthenticationEndpoint);
await Events.RedirectToAuthorizationEndpoint(redirectContext); await Events.RedirectToAuthorizationEndpoint(redirectContext);

View File

@ -3,10 +3,7 @@
using System; using System;
using System.Security.Claims; using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OAuth.Claims; using Microsoft.AspNetCore.Authentication.OAuth.Claims;
using Microsoft.AspNetCore.Authentication.Twitter;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication.Twitter namespace Microsoft.AspNetCore.Authentication.Twitter
@ -16,6 +13,10 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
/// </summary> /// </summary>
public class TwitterOptions : RemoteAuthenticationOptions public class TwitterOptions : RemoteAuthenticationOptions
{ {
private const string DefaultStateCookieName = "__TwitterState";
private CookieBuilder _stateCookieBuilder;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="TwitterOptions"/> class. /// Initializes a new instance of the <see cref="TwitterOptions"/> class.
/// </summary> /// </summary>
@ -26,6 +27,14 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
Events = new TwitterEvents(); Events = new TwitterEvents();
ClaimActions.MapJsonKey(ClaimTypes.Email, "email", ClaimValueTypes.Email); ClaimActions.MapJsonKey(ClaimTypes.Email, "email", ClaimValueTypes.Email);
_stateCookieBuilder = new TwitterCookieBuilder(this)
{
Name = DefaultStateCookieName,
SecurePolicy = CookieSecurePolicy.SameAsRequest,
HttpOnly = true,
SameSite = SameSiteMode.Lax,
};
} }
/// <summary> /// <summary>
@ -58,19 +67,43 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
/// </summary> /// </summary>
public ISecureDataFormat<RequestToken> StateDataFormat { get; set; } 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> /// <summary>
/// Gets or sets the <see cref="TwitterEvents"/> used to handle authentication events. /// Gets or sets the <see cref="TwitterEvents"/> used to handle authentication events.
/// </summary> /// </summary>
public new TwitterEvents Events public new TwitterEvents Events
{ {
get { return (TwitterEvents)base.Events; } get => (TwitterEvents)base.Events;
set { base.Events = value; } set => base.Events = value;
}
/// <summary>
/// Determines the settings used to create the state cookie before the
/// cookie gets added to the response.
/// </summary>
public CookieBuilder StateCookie
{
get => _stateCookieBuilder;
set => _stateCookieBuilder = value ?? throw new ArgumentNullException(nameof(value));
}
private class TwitterCookieBuilder : CookieBuilder
{
private readonly TwitterOptions _twitterOptions;
public TwitterCookieBuilder(TwitterOptions twitterOptions)
{
_twitterOptions = twitterOptions;
}
public override CookieOptions Build(HttpContext context, DateTimeOffset expiresFrom)
{
var options = base.Build(context, expiresFrom);
if (!Expiration.HasValue)
{
options.Expires = expiresFrom.Add(_twitterOptions.RemoteAuthenticationTimeout);
}
return options;
}
} }
} }
} }

View File

@ -0,0 +1,38 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication.Internal
{
/// <summary>
/// A cookie builder that sets <see cref="CookieOptions.Path"/> to the request path base.
/// </summary>
public class RequestPathBaseCookieBuilder : CookieBuilder
{
/// <summary>
/// Gets an optional value that is appended to the request path base.
/// </summary>
protected virtual string AdditionalPath { get; }
public override CookieOptions Build(HttpContext context, DateTimeOffset expiresFrom)
{
// check if the user has overridden the default value of path. If so, use that instead of our default value.
var path = Path;
if (path == null)
{
var originalPathBase = context.Features.Get<IAuthenticationFeature>()?.OriginalPathBase ?? context.Request.PathBase;
path = originalPathBase + AdditionalPath;
}
var options = base.Build(context, expiresFrom);
options.Path = !string.IsNullOrEmpty(path)
? path
: "/";
return options;
}
}
}

View File

@ -5,7 +5,6 @@ using System;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -14,7 +13,6 @@ namespace Microsoft.AspNetCore.Authentication
public abstract class RemoteAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions>, IAuthenticationRequestHandler public abstract class RemoteAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions>, IAuthenticationRequestHandler
where TOptions : RemoteAuthenticationOptions, new() where TOptions : RemoteAuthenticationOptions, new()
{ {
private const string CorrelationPrefix = ".AspNetCore.Correlation.";
private const string CorrelationProperty = ".xsrf"; private const string CorrelationProperty = ".xsrf";
private const string CorrelationMarker = "N"; private const string CorrelationMarker = "N";
private const string AuthSchemeKey = ".AuthScheme"; private const string AuthSchemeKey = ".AuthScheme";
@ -187,20 +185,11 @@ namespace Microsoft.AspNetCore.Authentication
CryptoRandom.GetBytes(bytes); CryptoRandom.GetBytes(bytes);
var correlationId = Base64UrlTextEncoder.Encode(bytes); var correlationId = Base64UrlTextEncoder.Encode(bytes);
var cookieOptions = new CookieOptions var cookieOptions = Options.CorrelationCookie.Build(Context, Clock.UtcNow);
{
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; properties.Items[CorrelationProperty] = correlationId;
var cookieName = CorrelationPrefix + Scheme.Name + "." + correlationId; var cookieName = Options.CorrelationCookie.Name + Scheme.Name + "." + correlationId;
Response.Cookies.Append(cookieName, CorrelationMarker, cookieOptions); Response.Cookies.Append(cookieName, CorrelationMarker, cookieOptions);
} }
@ -212,16 +201,15 @@ namespace Microsoft.AspNetCore.Authentication
throw new ArgumentNullException(nameof(properties)); throw new ArgumentNullException(nameof(properties));
} }
string correlationId; if (!properties.Items.TryGetValue(CorrelationProperty, out string correlationId))
if (!properties.Items.TryGetValue(CorrelationProperty, out correlationId))
{ {
Logger.CorrelationPropertyNotFound(CorrelationPrefix); Logger.CorrelationPropertyNotFound(Options.CorrelationCookie.Name);
return false; return false;
} }
properties.Items.Remove(CorrelationProperty); properties.Items.Remove(CorrelationProperty);
var cookieName = CorrelationPrefix + Scheme.Name + "." + correlationId; var cookieName = Options.CorrelationCookie.Name + Scheme.Name + "." + correlationId;
var correlationCookie = Request.Cookies[cookieName]; var correlationCookie = Request.Cookies[cookieName];
if (string.IsNullOrEmpty(correlationCookie)) if (string.IsNullOrEmpty(correlationCookie))
@ -230,15 +218,7 @@ namespace Microsoft.AspNetCore.Authentication
return false; return false;
} }
var cookieOptions = new CookieOptions var cookieOptions = Options.CorrelationCookie.Build(Context, Clock.UtcNow);
{
HttpOnly = true,
Path = OriginalPathBase + Options.CallbackPath,
SameSite = SameSiteMode.None,
Secure = Request.IsHttps
};
Options.ConfigureCorrelationIdCookie?.Invoke(Context, cookieOptions);
Response.Cookies.Delete(cookieName, cookieOptions); Response.Cookies.Delete(cookieName, cookieOptions);

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Authentication.Internal;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -13,6 +14,24 @@ namespace Microsoft.AspNetCore.Authentication
/// </summary> /// </summary>
public class RemoteAuthenticationOptions : AuthenticationSchemeOptions public class RemoteAuthenticationOptions : AuthenticationSchemeOptions
{ {
private const string CorrelationPrefix = ".AspNetCore.Correlation.";
private CookieBuilder _correlationCookieBuilder;
/// <summary>
/// Initializes a new <see cref="RemoteAuthenticationOptions"/>.
/// </summary>
public RemoteAuthenticationOptions()
{
_correlationCookieBuilder = new CorrelationCookieBuilder(this)
{
Name = CorrelationPrefix,
HttpOnly = true,
SameSite = SameSiteMode.None,
SecurePolicy = CookieSecurePolicy.SameAsRequest,
};
}
/// <summary> /// <summary>
/// Check that the options are valid. Should throw an exception if things are not ok. /// Check that the options are valid. Should throw an exception if things are not ok.
/// </summary> /// </summary>
@ -71,8 +90,8 @@ namespace Microsoft.AspNetCore.Authentication
public new RemoteAuthenticationEvents Events public new RemoteAuthenticationEvents Events
{ {
get { return (RemoteAuthenticationEvents)base.Events; } get => (RemoteAuthenticationEvents)base.Events;
set { base.Events = value; } set => base.Events = value;
} }
/// <summary> /// <summary>
@ -84,9 +103,37 @@ namespace Microsoft.AspNetCore.Authentication
public bool SaveTokens { get; set; } public bool SaveTokens { get; set; }
/// <summary> /// <summary>
/// Gets or sets an action that can override the correlation id cookie options before the /// Determines the settings used to create the correlation cookie before the
/// cookie gets added to the response. /// cookie gets added to the response.
/// </summary> /// </summary>
public Action<HttpContext, CookieOptions> ConfigureCorrelationIdCookie { get; set; } public CookieBuilder CorrelationCookie
{
get => _correlationCookieBuilder;
set => _correlationCookieBuilder = value ?? throw new ArgumentNullException(nameof(value));
}
private class CorrelationCookieBuilder : RequestPathBaseCookieBuilder
{
private readonly RemoteAuthenticationOptions _options;
public CorrelationCookieBuilder(RemoteAuthenticationOptions remoteAuthenticationOptions)
{
_options = remoteAuthenticationOptions;
}
protected override string AdditionalPath => _options.CallbackPath;
public override CookieOptions Build(HttpContext context, DateTimeOffset expiresFrom)
{
var cookieOptions = base.Build(context, expiresFrom);
if (!Expiration.HasValue || !cookieOptions.Expires.HasValue)
{
cookieOptions.Expires = expiresFrom.Add(_options.RemoteAuthenticationTimeout);
}
return cookieOptions;
}
}
} }
} }

View File

@ -190,7 +190,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
opt.AuthorizationEndpoint = "https://example.com/provider/login"; opt.AuthorizationEndpoint = "https://example.com/provider/login";
opt.TokenEndpoint = "https://example.com/provider/token"; opt.TokenEndpoint = "https://example.com/provider/token";
opt.CallbackPath = "/oauth-callback"; opt.CallbackPath = "/oauth-callback";
opt.ConfigureCorrelationIdCookie = (ctx, options) => options.Path = "/"; opt.CorrelationCookie.Path = "/";
}), }),
ctx => ctx =>
{ {

View File

@ -92,7 +92,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
{ {
AuthorizationEndpoint = "https://example.com/provider/login" AuthorizationEndpoint = "https://example.com/provider/login"
}; };
opt.ConfigureNonceCookie = (ctx, options) => options.Path = "/"; opt.NonceCookie.Path = "/";
}); });
var server = setting.CreateTestServer(); var server = setting.CreateTestServer();
@ -143,7 +143,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
{ {
AuthorizationEndpoint = "https://example.com/provider/login" AuthorizationEndpoint = "https://example.com/provider/login"
}; };
opt.ConfigureCorrelationIdCookie = (ctx, options) => options.Path = "/"; opt.CorrelationCookie.Path = "/";
}); });
var server = setting.CreateTestServer(); var server = setting.CreateTestServer();