e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
241 lines
10 KiB
C#
241 lines
10 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="FormsAuthenticationTicket.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
/*
|
|
* FormsAuthenticationTicket class
|
|
*
|
|
* Copyright (c) 1999 Microsoft Corporation
|
|
*/
|
|
|
|
namespace System.Web.Security {
|
|
using System.Security.Principal;
|
|
using System.Security.Permissions;
|
|
using System.Web.Configuration;
|
|
using System.Runtime.Serialization;
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>This class encapsulates the information represented in
|
|
/// an authentication cookie as used by FormsAuthenticationModule.</para>
|
|
/// </devdoc>
|
|
[Serializable]
|
|
public sealed class FormsAuthenticationTicket {
|
|
|
|
/// <devdoc>
|
|
/// <para>A one byte version number for future
|
|
/// use.</para>
|
|
/// </devdoc>
|
|
public int Version { get { return _Version;}}
|
|
|
|
/// <devdoc>
|
|
/// The user name associated with the
|
|
/// authentication cookie. Note that, at most, 32 bytes are stored in the
|
|
/// cookie.
|
|
/// </devdoc>
|
|
public String Name { get { return _Name;}}
|
|
|
|
/// <devdoc>
|
|
/// The date/time at which the cookie
|
|
/// expires.
|
|
/// </devdoc>
|
|
public DateTime Expiration { get { return _Expiration;}}
|
|
|
|
/// <devdoc>
|
|
/// The time at which the cookie was originally
|
|
/// issued. This can be used for custom expiration schemes.
|
|
/// </devdoc>
|
|
public DateTime IssueDate { get { return _IssueDate;}}
|
|
|
|
/// <devdoc>
|
|
/// True if a durable cookie was issued.
|
|
/// Otherwise, the authentication cookie is scoped to the browser lifetime.
|
|
/// </devdoc>
|
|
public bool IsPersistent { get { return _IsPersistent;}}
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
public bool Expired {
|
|
get {
|
|
/*
|
|
* Two DateTime instances can only be compared if they are of the same DateTimeKind.
|
|
* Therefore we normalize everything to UTC to do the comparison. See comments on
|
|
* the ExpirationUtc property for more information
|
|
*/
|
|
return (ExpirationUtc < DateTime.UtcNow);
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
public String UserData { get { return _UserData;}}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
public String CookiePath { get { return _CookiePath;}}
|
|
|
|
/*
|
|
* We always prefer UTC expiration dates to work around issues like a daylight
|
|
* saving time changes between the time the ticket was issued and the time the
|
|
* ticket was checked. If we have a firm UTC expiration date, just use it
|
|
* directly.
|
|
*
|
|
* If we don't have a firm UTC expiration date, try converting the developer-
|
|
* provided date to UTC before doing the comparison. There are three types of
|
|
* DateTime, and the .NET Framework converts as so:
|
|
*
|
|
* - The DateTime is already UTC, in which case it is returned unmodified.
|
|
* - The DateTime is local, in which case the .NET Framework converts it to
|
|
* UTC. There is also a hidden bit in the DateTime struct which essentially
|
|
* states whether daylight saving time was active when this DateTime was
|
|
* generated, i.e. whether this was 2:02 AM PDT or 2:02 AM PST. The .NET
|
|
* framework handles round-tripping Local <-> UTC correctly, but comparisons
|
|
* can still fail as described in detail below.
|
|
* - The DateTime is of an undefined type, in which case it is implicitly
|
|
* treated as local in a manner consistent with .NET 1.1.
|
|
*
|
|
* However, this alone is insufficient to work around DST issues when comparing
|
|
* local dates. For example, assume that a ticket is issued on Nov 6, 2011 at
|
|
* 1:30 AM PDT (UTC -0700) with a timeout period of 20 minutes. The expiration
|
|
* date is thus calculated to be Nov 6, 2011 at 1:50 AM PDT (UTC -0700). Now
|
|
* say a request comes in 25 minutes after expiration; the time is currently
|
|
* 1:15 AM PST (UTC -0800). [A DST boundary has been crossed.] Since this
|
|
* request came in *after* the ticket expiration date, the ticket should be
|
|
* rejected. And if we were doing all of our comparisons in UTC, this would
|
|
* indeed be the case. However, since the DateTime struct doesn't have UTC
|
|
* offset information embedded in it, comparisons of their dates are taken at
|
|
* face value as simple wall time comparisons. Thus the current time is
|
|
* interpreted just as "1:15 AM" and the expiration time is intepreted just as
|
|
* "1:50 AM", and from this simple comparison the token is considered unexpired
|
|
* and is accepted by the system.
|
|
*
|
|
* To see this incorrect behavior in action, run the following on a machine
|
|
* in the Pacific Time Zone. Contrast the behavior of the DateTimeOffset type
|
|
* (which is designed to handle UTC offsets correctly) with the DateTime type, in
|
|
* which the FromFileTime method implicitly does a local time conversion.
|
|
*
|
|
* long ft1 = 129650430000000000; // Nov 6, 2011 1:50 AM PDT (UTC -0700)
|
|
* long ft2 = 129650445000000000; // Nov 6, 2011 1:15 AM PST (UTC -0800)
|
|
* DateTimeOffset.FromFileTime(ft1) < DateTimeOffset.FromFileTime(ft2) = true
|
|
* DateTime.FromFileTime(ft1) < DateTime.FromFileTime(ft2) = false (INCORRECT!)
|
|
*
|
|
* To be absolutely safe, we must perform comparisons *only* on DateTime instances
|
|
* we know to have correct UTC information, or we must use an offset-aware type
|
|
* like DateTimeOffset which just does the right thing automatically.
|
|
*
|
|
* More info: http://msdn.microsoft.com/en-us/library/bb546099.aspx
|
|
*/
|
|
internal DateTime ExpirationUtc {
|
|
get { return (_ExpirationUtcHasValue) ? _ExpirationUtc : Expiration.ToUniversalTime(); }
|
|
}
|
|
|
|
internal DateTime IssueDateUtc {
|
|
get { return (_IssueDateUtcHasValue) ? _IssueDateUtc : IssueDate.ToUniversalTime(); }
|
|
}
|
|
|
|
private int _Version;
|
|
private String _Name;
|
|
private DateTime _Expiration;
|
|
private DateTime _IssueDate;
|
|
private bool _IsPersistent;
|
|
private String _UserData;
|
|
private String _CookiePath;
|
|
|
|
#pragma warning disable 0169 // unused field
|
|
// These fields were added in .NET 4 but weren't actually used anywhere.
|
|
// We can't remove them since they're part of the serialization contract.
|
|
[OptionalField(VersionAdded = 2)]
|
|
private int _InternalVersion;
|
|
[OptionalField(VersionAdded = 2)]
|
|
private Byte[] _InternalData;
|
|
#pragma warning restore 0169
|
|
|
|
// Issue and expiration times as UTC.
|
|
// We can't use nullable types since they didn't exist in v1.1, and this assists backporting fixes downlevel.
|
|
[NonSerialized]
|
|
private bool _ExpirationUtcHasValue;
|
|
[NonSerialized]
|
|
private DateTime _ExpirationUtc;
|
|
[NonSerialized]
|
|
private bool _IssueDateUtcHasValue;
|
|
[NonSerialized]
|
|
private DateTime _IssueDateUtc;
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>This constructor creates a
|
|
/// FormsAuthenticationTicket instance with explicit values.</para>
|
|
/// </devdoc>
|
|
public FormsAuthenticationTicket(int version,
|
|
String name,
|
|
DateTime issueDate,
|
|
DateTime expiration,
|
|
bool isPersistent,
|
|
String userData) {
|
|
_Version = version;
|
|
_Name = name;
|
|
_Expiration = expiration;
|
|
_IssueDate = issueDate;
|
|
_IsPersistent = isPersistent;
|
|
_UserData = userData;
|
|
_CookiePath = FormsAuthentication.FormsCookiePath;
|
|
}
|
|
|
|
|
|
public FormsAuthenticationTicket(int version,
|
|
String name,
|
|
DateTime issueDate,
|
|
DateTime expiration,
|
|
bool isPersistent,
|
|
String userData,
|
|
String cookiePath) {
|
|
_Version = version;
|
|
_Name = name;
|
|
_Expiration = expiration;
|
|
_IssueDate = issueDate;
|
|
_IsPersistent = isPersistent;
|
|
_UserData = userData;
|
|
_CookiePath = cookiePath;
|
|
}
|
|
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para> This constructor creates
|
|
/// a FormsAuthenticationTicket instance with the specified name and cookie durability,
|
|
/// and default values for the other settings.</para>
|
|
/// </devdoc>
|
|
public FormsAuthenticationTicket(String name, bool isPersistent, Int32 timeout) {
|
|
_Version = 2;
|
|
_Name = name;
|
|
_IssueDateUtcHasValue = true;
|
|
_IssueDateUtc = DateTime.UtcNow;
|
|
_IssueDate = DateTime.Now;
|
|
_IsPersistent = isPersistent;
|
|
_UserData = "";
|
|
_ExpirationUtcHasValue = true;
|
|
_ExpirationUtc = _IssueDateUtc.AddMinutes(timeout);
|
|
_Expiration = _IssueDate.AddMinutes(timeout);
|
|
_CookiePath = FormsAuthentication.FormsCookiePath;
|
|
}
|
|
|
|
internal static FormsAuthenticationTicket FromUtc(int version, String name, DateTime issueDateUtc, DateTime expirationUtc, bool isPersistent, String userData, String cookiePath) {
|
|
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(version, name, issueDateUtc.ToLocalTime(), expirationUtc.ToLocalTime(), isPersistent, userData, cookiePath);
|
|
|
|
ticket._IssueDateUtcHasValue = true;
|
|
ticket._IssueDateUtc = issueDateUtc;
|
|
ticket._ExpirationUtcHasValue = true;
|
|
ticket._ExpirationUtc = expirationUtc;
|
|
|
|
return ticket;
|
|
}
|
|
|
|
}
|
|
}
|