diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationProperties.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationProperties.cs
index 9d1e670ea8..271329209a 100644
--- a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationProperties.cs
+++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationProperties.cs
@@ -20,20 +20,29 @@ namespace Microsoft.AspNetCore.Authentication
internal const string UtcDateTimeFormat = "r";
///
- /// Initializes a new instance of the class
+ /// Initializes a new instance of the class.
///
public AuthenticationProperties()
- : this(items: null)
- {
- }
+ : this(items: null, parameters: null)
+ { }
///
- /// Initializes a new instance of the class
+ /// Initializes a new instance of the class.
///
- ///
+ /// State values dictionary to use.
public AuthenticationProperties(IDictionary items)
+ : this(items, parameters: null)
+ { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// State values dictionary to use.
+ /// Parameters dictionary to use.
+ public AuthenticationProperties(IDictionary items, IDictionary parameters)
{
Items = items ?? new Dictionary(StringComparer.Ordinal);
+ Parameters = parameters ?? new Dictionary(StringComparer.Ordinal);
}
///
@@ -41,6 +50,12 @@ namespace Microsoft.AspNetCore.Authentication
///
public IDictionary Items { get; }
+ ///
+ /// Collection of parameters that are passed to the authentication handler. These are not intended for
+ /// serialization or persistence, only for flowing data between call sites.
+ ///
+ public IDictionary Parameters { get; }
+
///
/// Gets or sets whether the authentication session is persisted across multiple requests.
///
@@ -86,12 +101,22 @@ namespace Microsoft.AspNetCore.Authentication
set => SetBool(RefreshKey, value);
}
- private string GetString(string key)
+ ///
+ /// Get a string value from the collection.
+ ///
+ /// Property key.
+ /// Retrieved value or null if the property is not set.
+ public string GetString(string key)
{
return Items.TryGetValue(key, out string value) ? value : null;
}
- private void SetString(string key, string value)
+ ///
+ /// Set a string value in the collection.
+ ///
+ /// Property key.
+ /// Value to set or null to remove the property.
+ public void SetString(string key, string value)
{
if (value != null)
{
@@ -103,16 +128,44 @@ namespace Microsoft.AspNetCore.Authentication
}
}
- private bool? GetBool(string key)
+ ///
+ /// Get a parameter from the collection.
+ ///
+ /// Parameter type.
+ /// Parameter key.
+ /// Retrieved value or the default value if the property is not set.
+ public T GetParameter(string key)
+ => Parameters.TryGetValue(key, out var obj) && obj is T value ? value : default;
+
+ ///
+ /// Set a parameter value in the collection.
+ ///
+ /// Parameter type.
+ /// Parameter key.
+ /// Value to set.
+ public void SetParameter(string key, T value)
+ => Parameters[key] = value;
+
+ ///
+ /// Get a bool value from the collection.
+ ///
+ /// Property key.
+ /// Retrieved value or null if the property is not set.
+ protected bool? GetBool(string key)
{
- if (Items.TryGetValue(key, out string value) && bool.TryParse(value, out bool refresh))
+ if (Items.TryGetValue(key, out string value) && bool.TryParse(value, out bool boolValue))
{
- return refresh;
+ return boolValue;
}
return null;
}
- private void SetBool(string key, bool? value)
+ ///
+ /// Set a bool value in the collection.
+ ///
+ /// Property key.
+ /// Value to set or null to remove the property.
+ protected void SetBool(string key, bool? value)
{
if (value.HasValue)
{
@@ -124,7 +177,12 @@ namespace Microsoft.AspNetCore.Authentication
}
}
- private DateTimeOffset? GetDateTimeOffset(string key)
+ ///
+ /// Get a DateTimeOffset value from the collection.
+ ///
+ /// Property key.
+ /// Retrieved value or null if the property is not set.
+ protected DateTimeOffset? GetDateTimeOffset(string key)
{
if (Items.TryGetValue(key, out string value)
&& DateTimeOffset.TryParseExact(value, UtcDateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out DateTimeOffset dateTimeOffset))
@@ -134,7 +192,12 @@ namespace Microsoft.AspNetCore.Authentication
return null;
}
- private void SetDateTimeOffset(string key, DateTimeOffset? value)
+ ///
+ /// Set a DateTimeOffset value in the collection.
+ ///
+ /// Property key.
+ /// Value to set or null to remove the property.
+ protected void SetDateTimeOffset(string key, DateTimeOffset? value)
{
if (value.HasValue)
{
diff --git a/test/Microsoft.AspNetCore.Authentication.Core.Test/AuthenticationPropertiesTests.cs b/test/Microsoft.AspNetCore.Authentication.Core.Test/AuthenticationPropertiesTests.cs
new file mode 100644
index 0000000000..639c9b558e
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Authentication.Core.Test/AuthenticationPropertiesTests.cs
@@ -0,0 +1,207 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Authentication.Core.Test
+{
+ public class AuthenticationPropertiesTests
+ {
+ [Fact]
+ public void DefaultConstructor_EmptyCollections()
+ {
+ var props = new AuthenticationProperties();
+ Assert.Empty(props.Items);
+ Assert.Empty(props.Parameters);
+ }
+
+ [Fact]
+ public void ItemsConstructor_ReusesItemsDictionary()
+ {
+ var items = new Dictionary
+ {
+ ["foo"] = "bar",
+ };
+ var props = new AuthenticationProperties(items);
+ Assert.Same(items, props.Items);
+ Assert.Empty(props.Parameters);
+ }
+
+ [Fact]
+ public void FullConstructor_ReusesDictionaries()
+ {
+ var items = new Dictionary
+ {
+ ["foo"] = "bar",
+ };
+ var parameters = new Dictionary
+ {
+ ["number"] = 1234,
+ ["list"] = new List { "a", "b", "c" },
+ };
+ var props = new AuthenticationProperties(items, parameters);
+ Assert.Same(items, props.Items);
+ Assert.Same(parameters, props.Parameters);
+ }
+
+ [Fact]
+ public void GetSetString()
+ {
+ var props = new AuthenticationProperties();
+ Assert.Null(props.GetString("foo"));
+ Assert.Equal(0, props.Items.Count);
+
+ props.SetString("foo", "foo bar");
+ Assert.Equal("foo bar", props.GetString("foo"));
+ Assert.Equal("foo bar", props.Items["foo"]);
+ Assert.Equal(1, props.Items.Count);
+
+ props.SetString("foo", "foo baz");
+ Assert.Equal("foo baz", props.GetString("foo"));
+ Assert.Equal("foo baz", props.Items["foo"]);
+ Assert.Equal(1, props.Items.Count);
+
+ props.SetString("bar", "xy");
+ Assert.Equal("xy", props.GetString("bar"));
+ Assert.Equal("xy", props.Items["bar"]);
+ Assert.Equal(2, props.Items.Count);
+
+ props.SetString("bar", string.Empty);
+ Assert.Equal(string.Empty, props.GetString("bar"));
+ Assert.Equal(string.Empty, props.Items["bar"]);
+
+ props.SetString("foo", null);
+ Assert.Null(props.GetString("foo"));
+ Assert.Equal(1, props.Items.Count);
+ }
+
+ [Fact]
+ public void GetSetParameter_String()
+ {
+ var props = new AuthenticationProperties();
+ Assert.Null(props.GetParameter("foo"));
+ Assert.Equal(0, props.Parameters.Count);
+
+ props.SetParameter("foo", "foo bar");
+ Assert.Equal("foo bar", props.GetParameter("foo"));
+ Assert.Equal("foo bar", props.Parameters["foo"]);
+ Assert.Equal(1, props.Parameters.Count);
+
+ props.SetParameter("foo", null);
+ Assert.Null(props.GetParameter("foo"));
+ Assert.Null(props.Parameters["foo"]);
+ Assert.Equal(1, props.Parameters.Count);
+ }
+
+ [Fact]
+ public void GetSetParameter_Int()
+ {
+ var props = new AuthenticationProperties();
+ Assert.Null(props.GetParameter("foo"));
+ Assert.Equal(0, props.Parameters.Count);
+
+ props.SetParameter("foo", 123);
+ Assert.Equal(123, props.GetParameter("foo"));
+ Assert.Equal(123, props.Parameters["foo"]);
+ Assert.Equal(1, props.Parameters.Count);
+
+ props.SetParameter("foo", null);
+ Assert.Null(props.GetParameter("foo"));
+ Assert.Null(props.Parameters["foo"]);
+ Assert.Equal(1, props.Parameters.Count);
+ }
+
+ [Fact]
+ public void GetSetParameter_Collection()
+ {
+ var props = new AuthenticationProperties();
+ Assert.Null(props.GetParameter("foo"));
+ Assert.Equal(0, props.Parameters.Count);
+
+ var list = new string[] { "a", "b", "c" };
+ props.SetParameter>("foo", list);
+ Assert.Equal(new string[] { "a", "b", "c" }, props.GetParameter>("foo"));
+ Assert.Same(list, props.Parameters["foo"]);
+ Assert.Equal(1, props.Parameters.Count);
+
+ props.SetParameter>("foo", null);
+ Assert.Null(props.GetParameter>("foo"));
+ Assert.Null(props.Parameters["foo"]);
+ Assert.Equal(1, props.Parameters.Count);
+ }
+
+ [Fact]
+ public void IsPersistent_Test()
+ {
+ var props = new AuthenticationProperties();
+ Assert.False(props.IsPersistent);
+
+ props.IsPersistent = true;
+ Assert.True(props.IsPersistent);
+ Assert.Equal(string.Empty, props.Items.First().Value);
+
+ props.Items.Clear();
+ Assert.False(props.IsPersistent);
+ }
+
+ [Fact]
+ public void RedirectUri_Test()
+ {
+ var props = new AuthenticationProperties();
+ Assert.Null(props.RedirectUri);
+
+ props.RedirectUri = "http://example.com";
+ Assert.Equal("http://example.com", props.RedirectUri);
+ Assert.Equal("http://example.com", props.Items.First().Value);
+
+ props.Items.Clear();
+ Assert.Null(props.RedirectUri);
+ }
+
+ [Fact]
+ public void IssuedUtc_Test()
+ {
+ var props = new AuthenticationProperties();
+ Assert.Null(props.IssuedUtc);
+
+ props.IssuedUtc = new DateTimeOffset(new DateTime(2018, 03, 21, 0, 0, 0, DateTimeKind.Utc));
+ Assert.Equal(new DateTimeOffset(new DateTime(2018, 03, 21, 0, 0, 0, DateTimeKind.Utc)), props.IssuedUtc);
+ Assert.Equal("Wed, 21 Mar 2018 00:00:00 GMT", props.Items.First().Value);
+
+ props.Items.Clear();
+ Assert.Null(props.IssuedUtc);
+ }
+
+ [Fact]
+ public void ExpiresUtc_Test()
+ {
+ var props = new AuthenticationProperties();
+ Assert.Null(props.ExpiresUtc);
+
+ props.ExpiresUtc = new DateTimeOffset(new DateTime(2018, 03, 19, 12, 34, 56, DateTimeKind.Utc));
+ Assert.Equal(new DateTimeOffset(new DateTime(2018, 03, 19, 12, 34, 56, DateTimeKind.Utc)), props.ExpiresUtc);
+ Assert.Equal("Mon, 19 Mar 2018 12:34:56 GMT", props.Items.First().Value);
+
+ props.Items.Clear();
+ Assert.Null(props.ExpiresUtc);
+ }
+
+ [Fact]
+ public void AllowRefresh_Test()
+ {
+ var props = new AuthenticationProperties();
+ Assert.Null(props.AllowRefresh);
+
+ props.AllowRefresh = true;
+ Assert.True(props.AllowRefresh);
+ Assert.Equal("True", props.Items.First().Value);
+
+ props.AllowRefresh = false;
+ Assert.False(props.AllowRefresh);
+ Assert.Equal("False", props.Items.First().Value);
+
+ props.Items.Clear();
+ Assert.Null(props.AllowRefresh);
+ }
+ }
+}