diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/CookieBuilder.cs b/src/Microsoft.AspNetCore.Http.Abstractions/CookieBuilder.cs
index f0cdc3ad..203a7a04 100644
--- a/src/Microsoft.AspNetCore.Http.Abstractions/CookieBuilder.cs
+++ b/src/Microsoft.AspNetCore.Http.Abstractions/CookieBuilder.cs
@@ -67,6 +67,11 @@ public virtual string Name
///
public virtual TimeSpan? Expiration { get; set; }
+ ///
+ /// Gets or sets the max-age for the cookie.
+ ///
+ public virtual TimeSpan? MaxAge { get; set; }
+
///
/// Creates the cookie options from the given .
///
@@ -92,6 +97,7 @@ public virtual CookieOptions Build(HttpContext context, DateTimeOffset expiresFr
Path = Path ?? "/",
SameSite = SameSite,
HttpOnly = HttpOnly,
+ MaxAge = MaxAge,
Domain = Domain,
Secure = SecurePolicy == CookieSecurePolicy.Always || (SecurePolicy == CookieSecurePolicy.SameAsRequest && context.Request.IsHttps),
Expires = Expiration.HasValue ? expiresFrom.Add(Expiration.Value) : default(DateTimeOffset?)
diff --git a/src/Microsoft.AspNetCore.Http.Features/CookieOptions.cs b/src/Microsoft.AspNetCore.Http.Features/CookieOptions.cs
index 017b6520..e01a759c 100644
--- a/src/Microsoft.AspNetCore.Http.Features/CookieOptions.cs
+++ b/src/Microsoft.AspNetCore.Http.Features/CookieOptions.cs
@@ -53,5 +53,11 @@ public CookieOptions()
///
/// true if a cookie must not be accessible by client-side script; otherwise, false.
public bool HttpOnly { get; set; }
+
+ ///
+ /// Gets or sets the max-age for the cookie.
+ ///
+ /// The max-age date and time for the cookie.
+ public TimeSpan? MaxAge { get; set; }
}
}
diff --git a/src/Microsoft.AspNetCore.Http/Internal/ResponseCookies.cs b/src/Microsoft.AspNetCore.Http/Internal/ResponseCookies.cs
index 85e006dc..7c6e3e03 100644
--- a/src/Microsoft.AspNetCore.Http/Internal/ResponseCookies.cs
+++ b/src/Microsoft.AspNetCore.Http/Internal/ResponseCookies.cs
@@ -61,6 +61,7 @@ public void Append(string key, string value, CookieOptions options)
Domain = options.Domain,
Path = options.Path,
Expires = options.Expires,
+ MaxAge = options.MaxAge,
Secure = options.Secure,
SameSite = (Net.Http.Headers.SameSiteMode)options.SameSite,
HttpOnly = options.HttpOnly
diff --git a/test/Microsoft.AspNetCore.Http.Abstractions.Tests/CookieBuilderTests.cs b/test/Microsoft.AspNetCore.Http.Abstractions.Tests/CookieBuilderTests.cs
index 386374b2..dd540ccc 100644
--- a/test/Microsoft.AspNetCore.Http.Abstractions.Tests/CookieBuilderTests.cs
+++ b/test/Microsoft.AspNetCore.Http.Abstractions.Tests/CookieBuilderTests.cs
@@ -38,6 +38,16 @@ public void ComputesExpiration()
Assert.Equal(now.AddHours(1), options.Expires);
}
+ [Fact]
+ public void ComputesMaxAge()
+ {
+ Assert.Null(new CookieBuilder().Build(new DefaultHttpContext()).MaxAge);
+
+ var now = TimeSpan.FromHours(1);
+ var options = new CookieBuilder { MaxAge = now }.Build(new DefaultHttpContext());
+ Assert.Equal(now, options.MaxAge);
+ }
+
[Fact]
public void CookieBuilderPreservesDefaultPath()
{
diff --git a/test/Microsoft.AspNetCore.Http.Tests/ResponseCookiesTest.cs b/test/Microsoft.AspNetCore.Http.Tests/ResponseCookiesTest.cs
index 3693a428..8ab9dd1c 100644
--- a/test/Microsoft.AspNetCore.Http.Tests/ResponseCookiesTest.cs
+++ b/test/Microsoft.AspNetCore.Http.Tests/ResponseCookiesTest.cs
@@ -2,9 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using System.Text;
using Microsoft.AspNetCore.Http.Internal;
-using Microsoft.Extensions.ObjectPool;
using Microsoft.Net.Http.Headers;
using Xunit;
@@ -74,6 +72,23 @@ public void NoParamsDeleteRemovesCookieCreatedByAdd()
Assert.Contains("expires=Thu, 01 Jan 1970 00:00:00 GMT", cookieHeaderValues[0]);
}
+ [Fact]
+ public void ProvidesMaxAgeWithCookieOptionsArgumentExpectMaxAgeToBeSet()
+ {
+ var headers = new HeaderDictionary();
+ var cookies = new ResponseCookies(headers, null);
+ var cookieOptions = new CookieOptions();
+ var maxAgeTime = TimeSpan.FromHours(1);
+ cookieOptions.MaxAge = TimeSpan.FromHours(1);
+ var testcookie = "TestCookie";
+
+ cookies.Append(testcookie, testcookie, cookieOptions);
+
+ var cookieHeaderValues = headers[HeaderNames.SetCookie];
+ Assert.Equal(1, cookieHeaderValues.Count);
+ Assert.Contains($"max-age={maxAgeTime.TotalSeconds.ToString()}", cookieHeaderValues[0]);
+ }
+
public static TheoryData EscapesKeyValuesBeforeSettingCookieData
{
get