IsInRole Getting New Security Token - c#

I'm using WindowsPrincipal's IsInRole method to check group memberships in WPF and Winforms apps. I'm generating an identity token which can be for any AD user (not necessarily the user who's actually logged into the computer--depending on what I'm doing I don't necessarily authenticate, I just use the basic informational level token (I think the proper name for it is "identity token").
The first time this code is run on a particular computer the operating system generates the identity token for the user specified. That token is then used by the IsInRole function to validate group memberships. It's fast so I really like it. However, subsequent calls to create the WindowsIdentity/WindowsPrincipal reference the existing token instead of creating a new one. The only way I know how to update the token is to log out of the computer or reboot (which clears the token cache). Does anyone know a better way to reset cached identity tokens?
Example Code C#:
Using System.Security.Principal;
WindowsIdentity impersonationLevelIdentity = new WindowsIdentity("Some_UserID_That_Isn't_Me", null);
WindowsPrincipal identityWindowsPrincipal = new WindowsPrincipal(impersonationLevelIdentity);
If (identityWindowsPrincipal.IsInRole("AN_AD_GROUP")) { ...
VB:
Imports System.Security.Principal
Dim impersonationLevelIdentity = New WindowsIdentity("Some_UserID_That_Isn't_Me", Nothing)
Dim identityWindowsPrincipal = New WindowsPrincipal(impersonationLevelIdentity)
if identityWindowsPrincipal.IsInRole("AN_AD_GROUP") then...

Not sure if this may resolve your issue, try calling the dispose method of WindowsIdentity class either directly or indirectly.
using (WindowsIdentity impersonationLevelIdentity = new WindowsIdentity("Some_UserID_That_Isn't_Me", null))
{
// your code
}

Turns out I was wrong. It is caching, but it appears to be on the AD side. Eventually after I create a new identityWindowsPrincipal it gets updated to the correct group memberships.

Related

How is PrincipalContext.ValidateCredentials call registered or marked on DC?

I am using PrincipalContext.ValidateCredentials method from System.DirectoryServices.AccountManagement namespace to validate user credentials against Active
Directory LDAP server. Sample of code:
private bool CheckIfCredentialsAreValidInDomain(string pLogin, string pPassword)
{
bool areCredentialsValidInDomain = true;
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
areCredentialsValidInDomain = context.ValidateCredentials(login, password);
}
return areCredentialsValidInDomain;
}
There is one domain and several (6 or more) DC in customer's environment. I don't pass DC name into PrincipalContext constructor - assuming DC Locator Service is doing its job - it is not important for me which particular DC is used from list of available DCs . Everything works great but I have have case of user who doesn't directly log on the domain (before starting application where this validanting is used) but his computer is physically connected to the customer's network.
This user's domain account has been recently disabled. Reason: he didn't log in to the domain for the last X months. But until then he was using app on daily basis so ValidateCredentials method was being called and returning true. But for unclear reason this action was "transaparent" for DC and this validation was not marked.
So how does ValidateCredentials work? Does it set LastLogon and lastLogonTimestamp user's attribute or just tells us if credentials are valid or not? Does it register any Event log entry on DC?
The source code for PrincipalContext is available now. ValidateCredentials() calls CredentialValidator.Validate() (an internal class).
That eventually calls lockedLdapBind(), which calls LdapConnection.Bind() with the credentials.
It does actually test the credentials against a server. So either that part of your code is not actually being run, or the account being tested isn't really disabled.
Something I noticed in your code is that you're passing the variables login and password to ValidateCredentials. However, the parameters for your method are called pLogin and pPassword. Is that just a typo in your question, or is that really how it is in your code? If that is accurate, then you're not actually testing the credentials passed to your method.

C# Ask for Domain Admin credential and use them to perform some task

I need some help with examples how to use Credential of a current user running application.
So in windows 7 you can run application using user loged in by simply running application or you can use "Run as a different User" option and run it as another user.
In my Active Directory I have 2 account Domain User and one with Domain Admin rights. I'm login Windows as a Domain User and when I need I'm using "Run as a different User" to launch some task as a Domain Admin.
So the task is to get my Credential and use it to perform some task, lets say rename active directory user name.
Best way to do this as I can see is to ask user running application to enter Domain Admin credential on then start application and use them for various task. Of course I can easily run application with "Run as a different User" but I still need to get this credential and use them.
I've searched through the web and I can't find this, all i could find is using credential for a web auth.
If you can show me some examples how to:
1) Ask user for a Admin user credential ( i can leave without this )
2) Get and use credentials of a user running application
I don't want to know password I know I can't. Don't really want to add to a WPF form password box I prefer to use windows API to handle this i've already entered user name and password using "Run as a different User".
PS: I sorry if this topic exists :( I guess I'm bad at creating correct search requests.
ADDED: to be more clear what I need. In powershell it will look like this:
# This Asks user to enter credentials
$cred = Get-Credential;
# this checks if I have rights to use them.
Get-ADDomain “DOMAIN” –Server “Domain.com” –Credential $cred;
Of course it's simplified as hell though the point is that I can use credentials user entered when ever it's needed.
The equivalent C# to your Get-ADDomain is quite simple, it is just
public void PerformSomeActionAsAdmin(string adminUsername, string adminPassword)
{
//Null causes the constructor to connect to the current domain the machine is on.
// |
// V
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, null, adminUsername, adminPassword))
{
//do something here with ctx, the operations will be performed as whoever's username and password you passed in.
}
}
if you don't want to connect to the current domain and instead want to connect to Domain.com then replace the null with the appropriate string.
EDIT: if you want to use secure strings you can't use System.DirectoryServices.AccountManagement.PrincipalContext, you will need to go with the lower level calls in System.DirectoryServices.Protocols. Doing this process is quite complex, here is a link to the MSDN article "Introduction to System.DirectoryServices.Protocols (S.DS.P)" explaining how to use it. It is a big complex read and honestly I don't think it is worth it to be able to use encrypted strings.
public void PerformSomeActionAsAdmin(NetworkCredential adminCredential)
{
using(LdapConnection connection = new LdapConnection("fabrikam.com", adminCredential))
{
// MAGIC
}
}
Do you want to check if the current user is a doman admin? start by looking at his code, it should help you get started identifying what AD groups the current user is in. This will give you a list of strings that are each group's name the current user belongs to. Then you can check that list against whatever AD group you are trying to check for. Replace YourDomain with your domain name:
WindowsIdentity wi = WindowIdentity.GetCurrent();
List<string> result = new List<string>();
foreach (IdentityReference group in wi.Groups)
{
result.Add(group.Translate(typeof(NTAccount)).ToString().Replace("YourDomain\\", String.Empty));
}
Since i'm not quite sure what you're trying to do, this also might be helpful. You'd have to get the user name and password from a textobx, password box etc. This could be used for an "override" to use, for example, a manager's credentials etc. to do something the current user wasn't allowed to do because of AD group membership etc.
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "YourDomain"))
{
if (UserName.Contains("YourDomain\\"))
{
UserName = UserName.Replace("YourDomain\\", String.Empty);
}
//validate the credentials
bool IsValid = pc.ValidateCredentials(UserName, Password);
}

Authenticating to AD using sAMAccountName format using PrincipleContext

I am implementing AD authentication for an offline application.
My original code did the following:
var validAuth = false;
using (var context = new System.DirectoryServices.AccountManagement.PrincipalContext(System.DirectoryServices.AccountManagement.ContextType.Domain))
{
validAuth = context.ValidateCredentials(_viewModel.Username, txtPassword.Password);
}
However, during testing it was noticed that this caused account lockouts in half the number of attempts of the AD Group Policy - so say the policy was set to 4 attempts before lockout, the user would be locked out in 2.
I googled and found this article on MSDN and the TL;DR is this:
It sounds that bad password count increase by 2 if you use UPN format (Domain#sAMAccountName), however, the count increase 1 every time if you use sAMAccountName format (Domain\sAMAccountName).
In light of this I changed my code to this:
var validAuth = false;
using (var context = new System.DirectoryServices.AccountManagement.PrincipalContext(System.DirectoryServices.AccountManagement.ContextType.Domain))
{
var usernameToAuth = string.Format("{0}\\{1}", Environment.UserDomainName, _viewModel.Username);
validAuth = context.ValidateCredentials(usernameToAuth, txtPassword.Password);
}
But this now fails to authenticate regardless of input. If I change it to use the old style UPN format of user#domain it authenticates fine - but obviously that is using up two authentication requests.
The MSDN post says to use the sAMAccountName format as a work around but I am struggling to work out how to do this. My original code also didn't explicitly use the old UPN format - I just passed the User Name directly to the ValidateCredentials method (no # symbol anywhere to be seen) so does this method use the old UPN method first?
Any advice please - I don't particularly want to half the bad log on attempts our users can have.
I used the domain specification in the PrincipalContext constructor, specified in this post, like that:
public static bool IsAuthenticated(string username_, string password_)
{
using (var pc = new PrincipalContext(ContextType.Domain, DomainManager.DomainName))
return pc.ValidateCredentials(username_, password_);
}
In my case, I use the System.DirectoryServices.ActiveDirectory.Domain and System.DirectoryServices.ActiveDirectory.DomainController to get this DomainManager.DomainName values.

WSFederation & Manual ClaimsIdentity with AppPool recycles

I have an application that needs to support both Federation (IdP-Initiated) as well as manual authentication (standard username/password form). As such I am using .NET v4.5 System.Identity to make the application claims aware.
The issue we are seeing in dev is that anytime an AppPool recycle happens (like a recompile) and we reload the page or take any other action we get an error trying to access any of our custom claims. It's as if the user is still authenticated, but our custom claims are totally gone. In order to keep working, we need to close all instances of the browser and login again. This can obviously happen in the wild and is something we cannot have happen (horrible end user experience).
Is there something we are doing wrong or a way that we can trap/detect this condition and force the user to log back in again?
Background
In the case of a manual login, we build a CustomClaimsIdentity instance that gets passed into a new ClaimsPrincipal which is then used to create a new SessionSecuirytToken instance and then written out as follows:
var claims = CustomClaimsAuthenticationManager.BuildClaimsList( user );
var identity = new UniversalIdentity( claims, AuthenticationTypes.Password );
var principal = new ClaimsPrincipal( identity );
var token = new SessionSecurityToken( principal, TimeSpan.FromMinutes( user.Customer.SessionExp ?? 120 ) );
var sam = FederatedAuthentication.SessionAuthenticationModule;
sam.WriteSessionTokenToCookie( token );
In the case of Idp-Initiated logins, we handle the FederatedAuthentication.WSFederationAuthenticationModule.SignedIn event and perform checks to validate the supplied claims as well as build up the custom claims our app adds to the identity the same way we do for manual authentication.
You can safely cast an IPrincipal to ClaimsPrincipal:
ClaimsPrincipal cp = (ClaimsPrincipal)Thread.CurrentPrincipal;

Windows identity foundation - sign out or update claims

I am using Windows Identity foundation to manage login to our site.
When a user logs in i am using some information in his request to put into the claims.
It is all working fine, but now I need to manage this scenario:
user is already logged in, athenticated and has a valid token.
But user decides to browses in again (via a redirect from another site)
So his information in his request is different.
I want to either
Sign him out - so that he naturally creates a new token with his new information
OR update his existing token.
So my question is:
How do i Sign out of Windows Identity foundation?
Or How do I update the existing claims?
I have tried this code:
public void ExpireClaims(HttpContextBase httpContextBase)
{
var module =
httpContextBase.ApplicationInstance.Modules["WSFederationAuthenticationModule"] as
WSFederationAuthenticationModule;
if (module == null)
{
return;
}
module.SignOut(true);
}
But module is alway null.
and i tried this:
public void FederatedSignOut(string replyUrl)
{
WSFederationAuthenticationModule.FederatedSignOut(null, new Uri(replyUrl));
}
But i get a null reference execption when i do this.
Thanks very much.
Essentially sign-out is just deleting the cookie so:
FormsAuthentication.SignOut
or
FederatedAuthentication.SessionAuthenticationModule.SignOut
or
FederatedAuthentication.SessionAuthenticationModule.DeleteSessionTokenCookie
will work.
Or use the FederatedPassiveSignInStatus (should be in your Toolbox). Set the property SignOutAction to FederatedSignOut and the control will clear out your STS session as well.

Categories

Resources