Active Directory Authentication and access

Groups, SID history, and token size

Separate Kerberos ticket size from Windows access-token limits and inspect the memberships behind an access decision.

There are two different size problems

Kerberos authentication data travels in tickets and application authentication messages. Large group memberships, SID history, and claims can enlarge it. HTTP also has header limits and Base64 overhead.

A Windows access token is the local authorization object created for a logon. It contains the user and group SIDs, privileges, and other security information. It exists whether that logon authenticated with Kerberos or NTLM.

Changing Kerberos MaxTokenSize does not remove a Windows access token’s group-SID limit. Conversely, a user can hit an HTTP authentication-header limit before approaching the access-token limit.

Inspect the existing logon token

Run in the affected user’s session:

whoami /user
whoami /groups
whoami /priv

The output describes that session’s token. A newly changed group membership may require a new logon and fresh tickets before it is reflected in the relevant application. Do not confuse an AD membership query made now with the token created hours earlier.

The resource server can add resource-domain local memberships to its authorization context. You’ll miss those SIDs if you only inspect the user’s workstation.

For Kerberos, the resource-domain KDC can add domain-local memberships to the service ticket’s PAC during TGS processing. The accepting computer can also add local memberships when building its token. These are different from the account-domain groups collected when the TGT was issued. The KILE domain-local membership rule explains the TGS step.

Read whoami /groups attributes as well as names. A deny-only SID does not grant access through an allow ACE. UAC can give an administrator a filtered desktop token and a different elevated token, so evidence from the elevated console may not reproduce the application’s context. Windows token attributes define enabled and deny-only behavior.

Read memberships at the right level

# Immediate members, including nested groups as groups
Get-ADGroupMember -Identity 'Application Readers'

# Recursive leaf members; this loses the nesting structure
Get-ADGroupMember -Identity 'Application Readers' -Recursive

# Groups containing this principal, subject to GC availability and scope
Get-ADPrincipalGroupMembership -Identity alice |
    Select-Object Name, GroupScope, GroupCategory

Keep the nesting structure when explaining an unexpected privilege. A flattened list can show that a user is a member, but hides which group’s owner controls that path. Keep lookup failures too, especially foreign security principals; dropping them silently makes the list look more complete than it is.

Large groups need an explicit completeness check. Microsoft documents a known Get-ADGroupMember limitation above 5,000 results in its Exchange administration guidance. The cmdlet’s AD Web Services retrieval path, LDAP search-result paging, and LDAP range retrieval of a multivalued member attribute have different limits. Increasing ResultPageSize on another AD cmdlet does not repair this failure. Use a collection method that handles the relevant limit and preserve errors; see LDAP range retrieval.

For an authorization-oriented directory calculation, use the dedicated command:

Get-ADAccountAuthorizationGroup -Identity alice -Server dc1.contoso.com |
    Select-Object Name, SID, GroupScope, GroupCategory

This requires a GC and returns a directory-derived security-group view. It does not reach into a running process and read that process’s existing token. Get-ADAccountAuthorizationGroup documents its GC requirement and returned objects.

When looking for domain-local groups in a separate resource domain, make the context explicit:

$user = Get-ADUser -Identity alice -Server dc1.accounts.example.com
Get-ADPrincipalGroupMembership -Identity $user `
    -Server dc1.accounts.example.com `
    -ResourceContextServer dc1.resource.contoso.com |
    Select-Object Name, SID, GroupScope

Use the correct domains for the investigation and retain lookup failures. The resource-context parameter exists specifically because a group search in the account’s own context cannot describe all resource-domain memberships.

Global groups take members from their own domain. Universal groups and domain local groups support different cross-domain arrangements. Plan migrations with those constraints and the resource permissions together. Converting every global group to universal merely to avoid a migration constraint can have unwanted replication and ticket-size effects.

A transitive LDAP match answers a narrower question

For this illustrative literal DN, the following asks which groups in the selected search scope contain Alice through the member chain:

Get-ADGroup -Server dc1.contoso.com -SearchBase 'DC=contoso,DC=com' `
    -LDAPFilter '(member:1.2.840.113556.1.4.1941:=CN=Alice,OU=People,DC=contoso,DC=com)' |
    Select-Object Name, SID, GroupScope, GroupCategory

Use LDAP filter escaping when substituting arbitrary DNs. The matching rule traverses linked DNs; it does not perform every step of Windows token construction, add primary-group membership, or reconstruct a foreign security principal’s original account graph. Its cost also depends on graph size and search scope. Microsoft’s filter documentation describes both the rule and its potentially expensive expansion.

Universal group membership caching is DC state

UGMC lets an authenticating DC use cached universal-group information instead of contacting a GC for every qualifying logon. It reduces one logon dependency; it does not make the DC a GC search endpoint. Initial population and later refresh still need a suitable GC. A roaming user’s first logon at a location can therefore behave differently from that of a regular user with established cache state. Microsoft’s GC placement guidance separates caching from application requirements for a GC.

The membership cache and its timestamp are local, nonreplicated attributes maintained by the system. A membership edit can be healthy on another DC while the authenticating DC still uses an older cached result. See the msDS-Cached-Membership and cache timestamp definitions. Before blaming replication, check which DC authenticated the user, whether the site enables caching, GC reachability and when the membership changed.

Keep four states separate: the workstation’s cached interactive sign-in information; UGMC on the DC; password secrets cached by an RODC; and authorization information already carried by issued tickets or access tokens. Refreshing one does not automatically refresh the others. An existing token can remain stale after the DC’s group view has become current, and a fresh authentication can still depend on the DC’s cached membership evidence.

UGMC doesn’t supply forest-wide application searches or solve every GC-dependent name lookup. Test a first-time/roaming user and a recent group change during a branch outage, alongside established users. One previously signed-in user continuing to work isn’t enough to check this.

A group expansion and an access context are different products

An application can compute authorization for a SID even when that user has not signed in to it. AuthzInitializeContextFromSid can use S4U and directory group data, including tokenGroupsGlobalAndUniversal (TGGAU). This requires suitable read access on the target account. Access denied while constructing that context can therefore precede the actual resource ACL check. The Authz API contract also explains why a context derived from an existing token is more complete than one reconstructed from a SID.

Inspect the application’s service identity, target-account ACL, S4U result and any LDAP fallback separately. Windows Authorization Access is a supported way to grant the relevant TGGAU read permission, but its membership is a directory information-access grant, not a delegation flag. Do not add the service to broad compatibility or administrator groups as a trial. Microsoft documents the TGGAU permission dependency, including why an application may depend on the attribute indirectly through an API.

MaxTokenSize is not a universal repair

The default documented for Windows Server 2012 and later, and Windows 8 and later, is 48,000 bytes, rather than a general 65 KB Windows 10 default. Inspect an explicit local override with:

Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters' `
    -Name MaxTokenSize -ErrorAction SilentlyContinue

Absence of the value means the system default applies. Microsoft’s Kerberos size guidance explains both the estimate 1200 + 40d + 8s and its limitations. The result is an estimate, not a measurement of a particular ticket. Claims, SID compression, delegation, and protocol limits affect the real case.

For a suspected HTTP header problem, collect the failing request and actual server limits before changing registry values throughout the domain. Microsoft’s AD maximum limits retains 48,000 bytes as the recommended maximum because HTTP encoding can impose a lower practical limit.

The access-token group limit is separate

Windows limits the number of SIDs that can be represented in this context. Microsoft documents logon failures above approximately 1,010 group memberships once the additional system SIDs are considered. Direct memberships alone do not capture nested groups and other SIDs.

Use the actual token, effective memberships, and failure evidence. Raising MaxTokenSize does not fix this limit. Microsoft’s logon group-limit explanation describes the distinction.

The old 32-bit paged-pool exhaustion stories are useful background to why token size became such a frequent incident topic. Their historical pool ceilings are not sizing rules for current 64-bit servers.

SID history is both access and size

Get-ADUser -LDAPFilter '(sIDHistory=*)' -Properties sIDHistory |
    Select-Object SamAccountName, SID, sIDHistory
Get-ADGroup -LDAPFilter '(sIDHistory=*)' -Properties sIDHistory |
    Select-Object Name, SID, sIDHistory

SID history can keep migrated identities working against old ACLs. It also adds authorization data and can carry powerful access. Confirm which resources still require each historical SID before removing it. See trusts and SID history.

Empty groups can still have permissions

Get-ADGroup -LDAPFilter '(!(member=*))' |
    Select-Object Name, GroupScope, GroupCategory

This finds groups with no values in member. It doesn’t detect users referring to the group through primaryGroupID. Check the group’s ACL permissions, nesting elsewhere and intended future use before deciding to delete it.

← All Active Directory topics