I'm having a really frustrating error trying to secure an ASP.NET application using the WindowsTokenRoleProvider. For a particular user I'm seeing the following ProviderException thrown:
API failed due to error 'Catastrophic failure
As I said, this only seems to happen for a particuar user, I seem to be able to access the site fine and so have several colleagues. The only difference between us and the failing user is that they are not an administrator on the box where the site is being hosted.
From the call to GetRolesForUser. From the MSDN documentation it states that this can happen because of the following:
The currently executing user does
not have an authenticated
WindowsIdentity attached to
Page.User. For non-HTTP
scenarios, the currently executing
user does not have an authenticated
WindowsIdentity attached to
Thread.CurrentPrincipal.
username does not match the Name of
the current WindowsIdentity.
A failure occurred while retrieving
the user's Windows group
information.
I'm suspecting the issue may be related to point 3 as I've managed to use the .NET Framework debugging ability in Visual Studio 2008 to debug the code and it seems to be failing calling:
UnsafeNativeMethods.GetGroupsForUser
What I can't understand is why! And if the call is failing in a Framework library then I'm not entirely sure what I can do to resolve the issue.
Any help or suggestions on this would be grately received as I'm at a loss as to where I go from here, I'm seriously considering scrapping using the role provider in place of some other less elegant method.
Okay, after some very helpful input from a Microsoft ADC I've managed to resolve the issue.
The call to UnsafeNativeMethods.GetGroupsForUser should return a list of all AD groups which a particular user has access to (this is recursive, so will also include parent groups etc). It appears that sometimes when migrating AD profiles between domains a user can end up with an erroneous SID pointing to a group with no name associated with it. Because the call tries to grab all the SID names for a particular profile it will fail when it gets to the null entry resulting in the (rather unhelpful) "catastrophic failure" error above.
Just as a reference, the group was showing up in a dump from the whoami command line tool as follows (other groups, SIDs and domain name masked):
DOMAIN\ Deleted account S-1-2-34-123456789-123456789-123456789-12345 Mandatory group, Enabled by default, Enabled group
As you can see, the domain name is present, but the group is not. The resolution was to get a domain administrator to remove the entry from the user's profile.
I truly hope this one helps others to resolve this issue because it had me completely flummoxed!
Related
We had a working application, we went through the go-live process, and everything was running live for several days. Then we started getting SSL errors, and we saw that the nuget package for the DocuSign package had an update (I believe this was all for the 11/13/2019 2019 certificates auto-update), so we updated our code, but now every request returns the USER_LACKS_MEMBERSHIP error for every token-authenticated request.
Things I can confirm are not the issue:
We have authenticated the app via account.docusign.com and the oauth signature impersonation scope, and the testing and live paths are in the API approved Redirect URIs.
We have the correct base path in the configuration (https://na3.docusign.net, as shown on our Apps and Keys page)
The base path did not change after we get the token back (The BaseUri on the Account object matches what we started with)
We are using the correct user for the configuration (The value labeled "API Username" in the Apps and Keys page is entered as "IMPERSONATED_USER_GUID" in appsettings.json and successfully used in creating the token as parameter UserID, which also matches our user account's ID shown in the backend, so we are not confusing it with TARGET_ACCOUNT_ID or CLIENT_ID, and shuffling those around causes errors much earlier at the token generation step).
We only have one user: the administrator of the DocuSign account. Their ID appears in the API configuration labeled as "API Username". The DocuSign administration backend doesn't display a membership tab anywhere for us to correct any possible issues with a user lacking membership. As far as I can tell, Membership is a higher tier account option than what we're paying for, so I'm confused how we could be having problems with a feature we haven't bought.
We get this error for checking envelope status. We get this error for trying to create new envelopes. We get this error for trying to get Account information. The only thing we can do is get an authentication token, but then that token can't be used to make any further authenticated requests.
Is there anything I'm missing that could be causing this other than some database error on DocuSign that I can't correct through the tools available to me? The package update changed the order of which class constructor accepts the ApiClient object, and there's a new AccessToken field on the Configuration class (which I filled out, but doesn't seem to have any effect, since we're still adding the Authorization/Bearer header manually). I'm out of ideas on what to try next.
Are you using the production environment or the demo environment?
I suspect that what's happening is that you are getting them mixed. As the baseUrl should not be demo.docusign.net etc. if you're using production (as indicated by your na3.docusign.net address) but you must ensure that the same account/user from production is also used.
So, the 4 things to check:
userId
accountId.
baseURI
authURI (account-d.docusign.com vs. account.docusign.com)
All of these should match and be for the same account in the same env.
I have built an MFA extension for ADFS using this guide: https://blogs.msdn.microsoft.com/jenfieldmsft/2014/03/24/build-your-own-external-authentication-provider-for-ad-fs-in-windows-server-2012-r2-walk-through-part-1/
I am trying get the incoming claim in the IAuthenticationAdapter.BeginAuthentication(Claim claim, ...) to have the e-mail of the user that is authenticating. Based on the guide, i should be able to specify in my metadata the IdentityClaims to return "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" and then i should get the e-mail.
However, my code is never hit.
Instead, i get the following error in the Event Viewer logs:
System.IO.InvalidDataException: The identity information provided does not contain a Windows account name.
at Microsoft.IdentityServer.Web.Authentication.External.ExternalAuthenticationHandler.ProcessContext(ProtocolContext context, IAuthenticationContext authContext, IAccountStoreUserData userData)
at Microsoft.IdentityServer.Web.Authentication.External.ExternalAuthenticationHandler.Process(ProtocolContext context)
at Microsoft.IdentityServer.Web.Authentication.AuthenticationOptionsHandler.Process(ProtocolContext context)
at Microsoft.IdentityServer.Web.PassiveProtocolListener.OnGetContext(WrappedHttpListenerContext context)
I tried specifying the relying party trusts to pass on the LDAP parameters but i am unable to access those in my code.
Any suggestions?
Background
We ran into this question when trying to debug this exact issue, ourselves. We were experimenting with IAuthenticationAdapterMetadata.IdentityClaims. When our adapter requested emailaddress as our input claim, ADFS threw the above exception immediately before calling BeginAuthentication.
The main issue was that our Claims Provider (Active Directory, in this case) was not configured to provide the user's email address. We discovered this with help from Microsoft's (free) Claims X-Ray service, which I highly recommend for those debugging ADFS claims issues.
Secondarily, ADFS was giving a misleading error. ADFS does not actually seem to validate the Claim value passed to BeginAuthentication. Once we resolved the main issue, there were no restrictions on the user's email address, so long as it was defined.
Solution
Note: if you have multiple Claims Provider Trusts, you may need to do this with each of them.
Navigate to the AD FS Management app on the relevant machine.
Select Claims Provider Trusts.
Right click the provider you want to "fix" and select Edit Claim rules...
Select Add Rule... and then select Send LDAP Attributes as Claims.
I'm assuming you should select the provider you're working on as the Attribute store for this rule. You may need to experiment if you can't.
Map LDAP attribute E-Mail-Addresses to claim type E-Mail Address.
Save the new rule.
Additional note: Only the first value in the E-Mail-Addresses array will be sent to BeginAuthentication. This seems to be a quirk of the interface, not a quirk of this solution.
Currently we (myself and my company) have an asp.net mvc4 page. We wish to utilize a logon page which authenticates via AD. One requirement being with an unsuccessful attempt we give back some information to the user.
The information we would like to have would be something like:
Invalid user/pw
Account is locked
Password expired
This is unfamiliar territory so I'm not sure what .NET libraries may be available. So far I've only come across the System.DirectoryServices but it doesn't seem I will get results beyond a bool.
Is this possible? Any references, suggestions, or examples would be greatly appreciated!
You can use PrincipalContext.ValidateCredentials to validate your credentials first. If false is returned, use the static UserPrincipal.FindByIdentity to find your user then, if found, look to see if the account is locked out using IsAccountLockedOut().
You might need to extend UserPrincipal yourself to see if the password is expired, I'm not seeing a direct property/method. You can extend it to access the userAccountControl attribute directly and check to see if bit 0x800000 is set, which is PasswordExpired. Here is more information on the userAccountControl values.
Suppose you have code like this
try
{
SearchResult result = searcher.FindOne();
}
catch(Exception e)
{
// now what?
}
Now in Exception you can deal with LDAP exception type, Here is the List of all LDAP error's.
http://msdn.microsoft.com/en-us/library/aa746530(v=vs.85).aspx
You can identify on the basis of ADSI Error Value which type of error you are getting.
But according to me you should give user a single common error like invalid credentials because LDAP error are much hard to deal with.
Cheers.!!
I am trying to create a web application to reset the password based on question/answer using System.Web.Security API.
I get an exception:
DirectoryServicesCOMException (0x8007202f): A constraint violation
occurred" if user provide one bad answer to the question.
If I reset value of attributeMapFailedPasswordAnswerCount to not set the account becomes active again.
Account Lockout threshold in AD is set to 20 logon attempts.
I am novice on AD knowledge and will be grateful if someone can guide me how to solve this problem.
Thank you.
I'm guessing you're using ASP.NET? I don't really have any experience with it, nor do I have much experience with .NET in general (I'm still learning myself), but this was a really useful link providing examples of various Active Directory API's (link). Including resetting a user password. Here is a link to the DirectoryEntry class, if you aren't sure how to set it up (link). Plus, just browsing through the namespace documentation is very, very helpful (link). Probably the only thing I like about Microsoft is their good documentation.
I usually do something like this (in IronPython, so it will not translate directly to code you can use):
ou = System.DirectoryServices.DirectoryEntry("LDAP://ou=Users,dc=whatever,dc=something,dc=localetc")
search = System.DirectoryServices.DirectorySearcher(ou, "(samAccountName="+acc"+")", Array[str](["distinguishedName"]]))
result = search.FindAll() # note 1
if result.Count != 1:
raise BadError
else:
ent = System.DirectoryServices.DirectoryEntry(result[0].Properties["distinguishedName"][0])
ent.Username = admin # note 2
ent.Password = pwd
ent.Invoke("SetPassword", Array[object](["newpassword!"]))
ent.Properties["LockOutTime"].Value = 0
ent.CommitChanges()
Notes:
If this ever returns more than one result, you have issues.
this and the password are only necessary if the account running this does not have permission to change the user. I run these on an unprivelaged account so I have to include my admin credentials in the script (don't worry, they aren't hardcoded)
Oh and you're account lockout threshold is quite high. I would suggest 3-5, depending on the aptitude of your users.
I'm creating a service on a machine in C# via a win32 API call to CreateService. The MSDN page for that function says about the lpServiceStartName param:
The name of the account under which the service should run. If the
service type is SERVICE_WIN32_OWN_PROCESS, use an account name in the
form DomainName\UserName. The service process will be logged on as
this user. If the account belongs to the built-in domain, you can
specify .\UserName.
On the remote machine, the user myuser belongs to the default local domain WORKGROUP. If I pass ".\myuser", everything works fine. If I pass "WORKGROUP\myuser", I get ERROR_INVALID_SERVICE_ACCOUNT.
I'm curious about this behavior, as the above docs seem to say I can specify .\myuser, not that I must. Can anyone shed any light on this?
First of all, I'm not an expert in windows account management, and I may get a few downvotes here, but here is what I noticed when I was testing windows services log on identities.
As documentation states, you can specify ".\UserName" if the account belongs to the built-in domain. Built-in domain includes groups and users created when OS is installed (link).
So instead of ".\UserName", you can specify "BUILTIN\UserName" since BUILTIN is the name of the built-in domain.
In practice, it worked for "BUILTIN\Administrators" and "BUILTIN\Guests", but it didn't work for user "BUILTIN\Guest".
If you try to create a new local group and local user and set it as a log on identity, you will still see it in a form ".\UserName". This may be inaccuracy in documentation, but in any case, for local user accounts that are not built-in accounts you can replace the dot with machine name: "MACHINE\UserName".