Skip to content

Instantly share code, notes, and snippets.

@realslacker
Created March 1, 2024 09:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save realslacker/bf257d26e0234e6f0e98aa9dc6990ed3 to your computer and use it in GitHub Desktop.
Save realslacker/bf257d26e0234e6f0e98aa9dc6990ed3 to your computer and use it in GitHub Desktop.
PowerShell Universal Authentication Example Supporting Multiple Domains
Set-PSUAuthenticationMethod -Type "Form" -ScriptBlock {
param(
[PSCredential]$Credential
)
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
# is this a UPN?
if ( $Credential.UserName.IndexOf('@') -gt -1 ) {
# juggle back and forth from SID to get NTAccount format
$NTAccountName = ([System.Security.Principal.NTAccount]$Credential.UserName).Translate([System.Security.Principal.SecurityIdentifier]).Translate([System.Security.Principal.NTAccount]).Value
} elseif ( $Credential.UserName.IndexOf('\') -gt -1 ) {
# already NTAccount format
$NTAccountName = $Credential.UserName
} else {
# someone didn't enter their domain...
$NTAccountName = "NETBIOSDOMAIN\" + $Credential.GetNetworkCredential().UserName
}
# split domain and username
$DomainName, $UserName = $NTAccountName.Split('\',2)
# perform auth with AD
$PrincipalContext = New-Object System.DirectoryServices.AccountManagement.PrincipalContext( 'Domain', $DomainName )
$Authenticated = $PrincipalContext.ValidateCredentials( $UserName, $Credential.GetNetworkCredential().Password, 'Negotiate, Sealing' )
if ( $Authenticated ) {
# discover the user principal, needed for the user DN
$UserPrincipal = [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($PrincipalContext, [System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName, $NTAccountName )
# get the user's domain
$UserDomainContext = [System.DirectoryServices.ActiveDirectory.DirectoryContext]::new( 'Domain', $DomainName, $Credential.UserName, $Credential.GetNetworkCredential().Password )
$UserDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain( $UserDomainContext )
# get the computer's domain
$ComputerDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetComputerDomain()
# hold all the user groups
[System.Collections.Generic.List[hashtable]]$Groups = @()
# get groups from user's domain
[adsisearcher]::new( $UserDomain.GetDirectoryEntry(), "(&(objectCategory=group)(objectClass=group)(member:1.2.840.113556.1.4.1941:=$($UserPrincipal.DistinguishedName)))", @('name') ).FindAll().ForEach({
$Groups.Add(@{
Type = 'Group'
Value = $_.Properties['name'][0]
Issuer = $UserDomain.Name
})
})
# get groups from the computer's domain (if different)
if ( $UserDomain.Name -ne $ComputerDomain.Name ) {
# lookup the user's foreign security principal in the computer's domain
$ForeignSecurityPrincipal = [adsisearcher]::new( $ComputerDomain.GetDirectoryEntry(), "(&(objectCategory=foreignSecurityPrincipal)(objectClass=foreignSecurityPrincipal)(name=$($UserPrincipal.Sid)))", @('distinguishedName') ).FindOne().Properties['distinguishedName'][0]
# find all the group memberships
[adsisearcher]::new( $ComputerDomain.GetDirectoryEntry(), "(&(objectCategory=group)(objectClass=group)(member:1.2.840.113556.1.4.1941:=$ForeignSecurityPrincipal))", @('name') ).FindAll().ForEach({
$Groups.Add(@{
Type = 'Group'
Value = $_.Properties['name'][0]
Issuer = $ComputerDomain.Name
})
})
}
New-PSUAuthenticationResult -Success -UserName $UserPrincipal.UserPrincipalName -Claims {
$Groups | ForEach-Object { New-PSUAuthorizationClaim @_ }
}
} else {
New-PSUAuthenticationResult -ErrorMessage 'Bad username or password'
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment