AuthenticateResult->AuthenticationTicket->AuthenticationProperties->ClaimsPrincipal
using System;
using System.Diagnostics.CodeAnalysis;
using System.Security.Claims;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// Contains the result of an Authenticate call
/// </summary>
public class AuthenticateResult
{
/// <summary>
/// Creates a new <see cref="AuthenticateResult"/> instance.
/// </summary>
protected AuthenticateResult() { }
/// <summary>
/// If a ticket was produced, authenticate was successful.
/// </summary>
[MemberNotNullWhen(true, nameof(Ticket), nameof(Principal), nameof(Properties))]
public bool Succeeded => Ticket != null;
/// <summary>
/// The authentication ticket.
/// </summary>
public AuthenticationTicket? Ticket { get; protected set; }
/// <summary>
/// Gets the claims-principal with authenticated user identities.
/// </summary>
public ClaimsPrincipal? Principal => Ticket?.Principal;
/// <summary>
/// Additional state values for the authentication session.
/// </summary>
public AuthenticationProperties? Properties { get; protected set; }
/// <summary>
/// Holds failure information from the authentication.
/// </summary>
public Exception? Failure { get; protected set; }
/// <summary>
/// Indicates that there was no information returned for this authentication scheme.
/// </summary>
public bool None { get; protected set; }
/// <summary>
/// Create a new deep copy of the result
/// </summary>
/// <returns>A copy of the result</returns>
public AuthenticateResult Clone()
{
if (None)
{
return NoResult();
}
if (Failure != null)
{
return Fail(Failure, Properties?.Clone());
}
if (Succeeded)
{
return Success(Ticket!.Clone());
}
// This shouldn't happen
throw new NotImplementedException();
}
/// <summary>
/// Indicates that authentication was successful.
/// </summary>
/// <param name="ticket">The ticket representing the authentication result.</param>
/// <returns>The result.</returns>
public static AuthenticateResult Success(AuthenticationTicket ticket)
{
if (ticket == null)
{
throw new ArgumentNullException(nameof(ticket));
}
return new AuthenticateResult() { Ticket = ticket, Properties = ticket.Properties };
}
/// <summary>
/// Indicates that there was no information returned for this authentication scheme.
/// </summary>
/// <returns>The result.</returns>
public static AuthenticateResult NoResult()
{
return new AuthenticateResult() { None = true };
}
/// <summary>
/// Indicates that there was a failure during authentication.
/// </summary>
/// <param name="failure">The failure exception.</param>
/// <returns>The result.</returns>
public static AuthenticateResult Fail(Exception failure)
{
return new AuthenticateResult() { Failure = failure };
}
/// <summary>
/// Indicates that there was a failure during authentication.
/// </summary>
/// <param name="failure">The failure exception.</param>
/// <param name="properties">Additional state values for the authentication session.</param>
/// <returns>The result.</returns>
public static AuthenticateResult Fail(Exception failure, AuthenticationProperties? properties)
{
return new AuthenticateResult() { Failure = failure, Properties = properties };
}
/// <summary>
/// Indicates that there was a failure during authentication.
/// </summary>
/// <param name="failureMessage">The failure message.</param>
/// <returns>The result.</returns>
public static AuthenticateResult Fail(string failureMessage)
=> Fail(new Exception(failureMessage));
/// <summary>
/// Indicates that there was a failure during authentication.
/// </summary>
/// <param name="failureMessage">The failure message.</param>
/// <param name="properties">Additional state values for the authentication session.</param>
/// <returns>The result.</returns>
public static AuthenticateResult Fail(string failureMessage, AuthenticationProperties? properties)
=> Fail(new Exception(failureMessage), properties);
}
}
using System;
using System.Security.Claims;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// Contains user identity information as well as additional authentication state.
/// </summary>
public class AuthenticationTicket
{
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationTicket"/> class
/// </summary>
/// <param name="principal">the <see cref="ClaimsPrincipal"/> that represents the authenticated user.</param>
/// <param name="properties">additional properties that can be consumed by the user or runtime.</param>
/// <param name="authenticationScheme">the authentication scheme that was responsible for this ticket.</param>
public AuthenticationTicket(ClaimsPrincipal principal, AuthenticationProperties? properties, string authenticationScheme)
{
if (principal == null)
{
throw new ArgumentNullException(nameof(principal));
}
AuthenticationScheme = authenticationScheme;
Principal = principal;
Properties = properties ?? new AuthenticationProperties();
}
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationTicket"/> class
/// </summary>
/// <param name="principal">the <see cref="ClaimsPrincipal"/> that represents the authenticated user.</param>
/// <param name="authenticationScheme">the authentication scheme that was responsible for this ticket.</param>
public AuthenticationTicket(ClaimsPrincipal principal, string authenticationScheme)
: this(principal, properties: null, authenticationScheme: authenticationScheme)
{ }
/// <summary>
/// Gets the authentication scheme that was responsible for this ticket.
/// </summary>
public string AuthenticationScheme { get; }
/// <summary>
/// Gets the claims-principal with authenticated user identities.
/// </summary>
public ClaimsPrincipal Principal { get; }
/// <summary>
/// Additional state values for the authentication session.
/// </summary>
public AuthenticationProperties Properties { get; }
/// <summary>
/// Returns a copy of the ticket.
/// </summary>
/// <remarks>
/// The method clones the <see cref="Principal"/> by calling <see cref="ClaimsIdentity.Clone"/> on each of the <see cref="ClaimsPrincipal.Identities"/>.
/// </remarks>
/// <returns>A copy of the ticket</returns>
public AuthenticationTicket Clone()
{
var principal = new ClaimsPrincipal();
foreach (var identity in Principal.Identities)
{
principal.AddIdentity(identity.Clone());
}
return new AuthenticationTicket(principal, Properties.Clone(), AuthenticationScheme);
}
}
}
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.Json.Serialization;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// Dictionary used to store state values about the authentication session.
/// </summary>
public class AuthenticationProperties
{
internal const string IssuedUtcKey = ".issued";
internal const string ExpiresUtcKey = ".expires";
internal const string IsPersistentKey = ".persistent";
internal const string RedirectUriKey = ".redirect";
internal const string RefreshKey = ".refresh";
internal const string UtcDateTimeFormat = "r";
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationProperties"/> class.
/// </summary>
public AuthenticationProperties()
: this(items: null, parameters: null)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationProperties"/> class.
/// </summary>
/// <param name="items">State values dictionary to use.</param>
[JsonConstructor]
public AuthenticationProperties(IDictionary<string, string?> items)
: this(items, parameters: null)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationProperties"/> class.
/// </summary>
/// <param name="items">State values dictionary to use.</param>
/// <param name="parameters">Parameters dictionary to use.</param>
public AuthenticationProperties(IDictionary<string, string?>? items, IDictionary<string, object?>? parameters)
{
Items = items ?? new Dictionary<string, string?>(StringComparer.Ordinal);
Parameters = parameters ?? new Dictionary<string, object?>(StringComparer.Ordinal);
}
/// <summary>
/// Return a copy.
/// </summary>
/// <returns>A copy.</returns>
public AuthenticationProperties Clone()
=> new AuthenticationProperties(
new Dictionary<string, string?>(Items, StringComparer.Ordinal),
new Dictionary<string, object?>(Parameters, StringComparer.Ordinal));
/// <summary>
/// State values about the authentication session.
/// </summary>
public IDictionary<string, string?> Items { get; }
/// <summary>
/// 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.
/// </summary>
[JsonIgnore]
public IDictionary<string, object?> Parameters { get; }
/// <summary>
/// Gets or sets whether the authentication session is persisted across multiple requests.
/// </summary>
[JsonIgnore]
public bool IsPersistent
{
get => GetString(IsPersistentKey) != null;
set => SetString(IsPersistentKey, value ? string.Empty : null);
}
/// <summary>
/// Gets or sets the full path or absolute URI to be used as an http redirect response value.
/// </summary>
[JsonIgnore]
public string? RedirectUri
{
get => GetString(RedirectUriKey);
set => SetString(RedirectUriKey, value);
}
/// <summary>
/// Gets or sets the time at which the authentication ticket was issued.
/// </summary>
[JsonIgnore]
public DateTimeOffset? IssuedUtc
{
get => GetDateTimeOffset(IssuedUtcKey);
set => SetDateTimeOffset(IssuedUtcKey, value);
}
/// <summary>
/// Gets or sets the time at which the authentication ticket expires.
/// </summary>
[JsonIgnore]
public DateTimeOffset? ExpiresUtc
{
get => GetDateTimeOffset(ExpiresUtcKey);
set => SetDateTimeOffset(ExpiresUtcKey, value);
}
/// <summary>
/// Gets or sets if refreshing the authentication session should be allowed.
/// </summary>
[JsonIgnore]
public bool? AllowRefresh
{
get => GetBool(RefreshKey);
set => SetBool(RefreshKey, value);
}
/// <summary>
/// Get a string value from the <see cref="Items"/> collection.
/// </summary>
/// <param name="key">Property key.</param>
/// <returns>Retrieved value or <c>null</c> if the property is not set.</returns>
public string? GetString(string key)
{
return Items.TryGetValue(key, out var value) ? value : null;
}
/// <summary>
/// Set or remove a string value from the <see cref="Items"/> collection.
/// </summary>
/// <param name="key">Property key.</param>
/// <param name="value">Value to set or <see langword="null" /> to remove the property.</param>
public void SetString(string key, string? value)
{
if (value != null)
{
Items[key] = value;
}
else
{
Items.Remove(key);
}
}
/// <summary>
/// Get a parameter from the <see cref="Parameters"/> collection.
/// </summary>
/// <typeparam name="T">Parameter type.</typeparam>
/// <param name="key">Parameter key.</param>
/// <returns>Retrieved value or the default value if the property is not set.</returns>
public T? GetParameter<T>(string key)
=> Parameters.TryGetValue(key, out var obj) && obj is T value ? value : default;
/// <summary>
/// Set a parameter value in the <see cref="Parameters"/> collection.
/// </summary>
/// <typeparam name="T">Parameter type.</typeparam>
/// <param name="key">Parameter key.</param>
/// <param name="value">Value to set.</param>
public void SetParameter<T>(string key, T value)
=> Parameters[key] = value;
/// <summary>
/// Get a nullable <see cref="bool"/> from the <see cref="Items"/> collection.
/// </summary>
/// <param name="key">Property key.</param>
/// <returns>Retrieved value or <see langword="null" /> if the property is not set.</returns>
protected bool? GetBool(string key)
{
if (Items.TryGetValue(key, out var value) && bool.TryParse(value, out var boolValue))
{
return boolValue;
}
return null;
}
/// <summary>
/// Set or remove a <see cref="bool"/> value in the <see cref="Items"/> collection.
/// </summary>
/// <param name="key">Property key.</param>
/// <param name="value">Value to set or <see langword="null" /> to remove the property.</param>
protected void SetBool(string key, bool? value)
{
if (value.HasValue)
{
Items[key] = value.GetValueOrDefault().ToString();
}
else
{
Items.Remove(key);
}
}
/// <summary>
/// Get a nullable <see cref="DateTimeOffset"/> value from the <see cref="Items"/> collection.
/// </summary>
/// <param name="key">Property key.</param>
/// <returns>Retrieved value or <see langword="null" /> if the property is not set.</returns>
protected DateTimeOffset? GetDateTimeOffset(string key)
{
if (Items.TryGetValue(key, out var value)
&& DateTimeOffset.TryParseExact(value, UtcDateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dateTimeOffset))
{
return dateTimeOffset;
}
return null;
}
/// <summary>
/// Sets or removes a <see cref="DateTimeOffset" /> value in the <see cref="Items"/> collection.
/// </summary>
/// <param name="key">Property key.</param>
/// <param name="value">Value to set or <see langword="null" /> to remove the property.</param>
protected void SetDateTimeOffset(string key, DateTimeOffset? value)
{
if (value.HasValue)
{
Items[key] = value.GetValueOrDefault().ToString(UtcDateTimeFormat, CultureInfo.InvariantCulture);
}
else
{
Items.Remove(key);
}
}
}
}