Stop Auto Logon after failure - Custom Credential Provider Windows - windows

I've taken up Widows Samples on Credential Providers and have built one using them as a reference. I'm able to log in seamlessly, wither by giving username and password manually. I've set
CustomCredential::SetSelected (__out BOOL* pbAutoLogon) {
*pbAutoLogon = TRUE; // FALSE;
return S_OK;
}
Now, AutoLogon, when the Tile is selected, is happening seamlessly.
As a test case, I changed the password, and as expected the Login fails. After failure, an error message appears and when I click OK ( this is the only option ), the credentials are resubmitted for a retry. How do can we stop this behavior? Which method gets called after the authentication failure?
I've handled ReportResult() but that did not help.
Thanks in advance.

It's been a while - but I believe GetSerialization() is called to return serialized credentials to LogonUI. You need to implement this as well. The credential provider samples I think have working code for KERB_INTERACTIVE_UNLOCK_LOGON.
You can also change *pbAutoLogon conditionally in SetSelected() - I do this in my credential provider depending on certain results.
ReportResult() would be called after GetSerialization returns its result to LogonUI. Inside ReportResult() you could do things like clear the password box (which is done in the samples code.)
If you're not returning serialized credentials in GetSerialization then I think you might get the kind of error you listed in your original post. In the credential samples KerbInteractiveUnlockLogonPack() is called in GetSerialization() and this is what 'logs' the user in effectively.
The error seems specific - 'the user has not been granted the requested logon type' so maybe it has something to do with the rights of the user you're testing with.
If you are using remote desktop, make sure your users are members of the right groups to be able to login (https://support.jumpdesktop.com/hc/en-us/articles/216424183-General-RDP-You-must-be-granted-the-Allow-log-on-through-the-Terminal-or-Remote-Desktop-Services-Right-) or if it is a regular user that it is allowed interactive login.
Also - SetSelected() Gets called when your credential provider is clicked on - I'm not sure if it gets called after every logon attempt or not (my guess is that it doesn't.) In my credential provider I am using a custom logon dialog that I show using SetSelected().

Related

Access violation on Server 2012R2 during UAC with custom credential provider

I have a custom credential provider and the credential implements IConnectableCredentialProviderCredential. It is written using Visual C++ 2019 and ATL. It works on Server 2016/2019 and Windows 10/11, if I use it on Server 2012R2, then the UI crashes during UAC--trying to elevate a non-admin user to use an Administrator PowerShell session for example.
I attached a remote debugger and the debugger intercepts the exception consistently as: Exception thrown at 0x00007FFE9E627EC3 (authui.dll) in consent.exe: 0xC0000005: Access violation reading location 0x0000000000000008.
I had some logging/debugging code and I could see that the system never calls my Connect() or GetSerialization() method on the credential when it crashes. My logging showed the username and password getting set, but then after keying the Enter key or clicking on OK, the process crashes without ever asking the credential to serialize its credentials. The logging showed that the last call into my credential provider was to ICredentialProviderCredential::SetStringValue() which was corresponding to entering the password before keying the Enter key.
Since it was throwing an access violation, I assumed that I must be passing bad data back to the system when it was calling the ICredentialProvder::GetFieldDescriptorAt() or possibly the ICredentialProviderCredential::GetStringValue() or some other CredentialProvider::Get...() function. I've gone over the methods with fine tooth comb as well as all other methods and have found nothing. For the GetStringValue() implementation, I am calling SHStrDupW() like in the SDK examples, and for GetFieldDescriptorAt() I am calling the same functions as the SDK examples. In fact, I copied the SDK code.
I decided to distill my credential provider down to a minimal reproducible example. That minimal example is at https://github.com/willcoxson/CredDemo
If anyone could see where I might be passing bad data back in one of the methods, I'd sure be grateful.
IConnectableCredentialProviderCredential have sense only for CPUS_LOGONcase. so for other scenarion your QueryInterface must return E_NOINTERFACE when system ask for IConnectableCredentialProviderCredential
in your concrete case we have CPUS_CREDUI scenario. inside function
void CGetSerializationJob::Do(CREDENTIAL_PROVIDER_THREAD_JOB_CONTEXT const &) system query you for IConnectableCredentialProviderCredential ("9387928b-ac75-4bf9-8ab2-2b93c4a55290" )
and if you return this interface - run next code
system try access some object by pointer - CPLAPCallback::_pSingleton
but pointer is 0. crash exactly at yellow line ( 0xC0000005: Access violation reading location 0x0000000000000008. )

Validate whether Google API Client's OAuth2 access token is still valid before using it

Starting from the point where an user has given permissions to the app, and the access token is stored in session. Following Google's web server app example, I'm just checking whether an access token exist.
However, the token might expire, or the user might remove it manually on his account page. How do I check that the token is still valid, before executing a request?
Or maybe that approach is wrong, and the correct design includes that I should handle the error after executing the action, and if it's an authorization error then show the user a way to authorize it once again?
The latter is the recommended approach. By assuming failure and dealing with it routinely, your app is much more robust. The only downside is that an access attempt takes a bit longer because of the need to fetch a new Access Token and retry. If that's a problem (it shouldn't be normally), then you can always note the expiration time of the new Access Token and set up a background process to renew it with say 5 minutes to spare.

Auth0 and Google API, access token expired, howto write files to Google Drive

I've written an online converter and integrated Auth0 to my website. What I'm trying to achieve is to auto-upload the converted file to the Google Drive of a logged in user. I set up Google oauth in Auth0 and everything seemed to work fine.
The problem is, Google's access_token expires after 60min and I don't have a refresh_token. Therefore, the user needs to log in via the Google Login-page again. That is not, what I want, because the user is in fact logged in way longer than just 60min on my site, but Google refuses API-calls (because the Google token expired).
I know I can request a refresh_token by setting access_type=offline but this will add the permission Have offline access. I don't want that, I just want to upload data to the user's Drive, if he clicked the convert button on my page. I don't want to ask the users for permissions I don't need. If I (as a user) would link my Google account on a similar page and the tool asks for offline access I wouldn't approve, to be honest - the permission sounds like the tool creator can do whatever he wants with your account whenever he wants... There are many tools out there that have write access to a user's Drive without asking for offline access and with one single login until the user revokes the permission. How is that done?
Is there a way to make Google API calls without asking for offline access and without forcing the user to approve the app (that is already approved by him) again and again every 60min?
Thanks in advance,
phlo
Is there a way to make Google API calls without asking for offline access and without forcing the user to approve the app (that is already approved by him) again and again every 60min?
Yes there are ways, but it depends on the specifics of your use case. For example, is your code in Java/php/etc running on a server, or is it JavaScript running in the browser?
Running your auth in the browser is probably the simplest solution as the Google library (https://developers.google.com/api-client-library/javascript/features/authentication) does all the work for you.
By asking for offline access you are requesting a refresh token. Google is going to tell the user that you are requesting offline access. You can request something without telling the user what they are authorizing.
No there is no way to request a refresh token without displaying that message. Nor is there a way for you to change the message it's a standard Google thing.
I found the solution!
Prerequirements
Enable Use Auth0 instead of the IdP to do Single Sign On in your client's Dashboard
Create a new Angular-route to handle the silent login callback (e.g. /sso)
Add
$rootScope.$on("$locationChangeStart", function() {
if ($location.path().indexOf("sso") == -1) {
authService.relogin(); //this is your own service
}
});
to your run-function and set the callbackURL in angularAuth0Provider.init() to your new Angular-route (<YOUR_DOMAIN>/sso). Add this URL to your accepted callbacks in the Auth0 dashboard - this won't end in an infinite loop, because the locationChangeStart-event won't call authService.relogin() for this route
Add $window.close(); to the controller of the Angular-route (/sso) to auto-close the popup
Authenticate the user via Auth0 and save the timestamp and the Auth0-access_token somewhere
On reload:
Check, if the Auth0-token is still valid in authService.relogin(). If not, the user has to login again either way. If the token is valid and the Google token is about to expire (check this with the saved timestamp to prevent unnecessary API calls) check for SSO-data and login silently, if present
/* ... */
if (validToken && googleExpired) {
angularAuth0.getSSOData(function (err, data) {
var lastUsedConnection = data.lastUsedConnection;
var connectionName = (_.isUndefined(lastUsedConnection) ? undefined : lastUsedConnection.name);
var isGoogle = (_.isUndefined(connectionName) ? false : connectionName == "google-oauth2");
if (!err && data.sso && isGoogle) {
authManager.authenticate();
localStorage.setItem("last-relogin", new Date().getTime());
angularAuth0.signin({
popup: true,
connection: data.lastUsedConnection.name
});
}
});
}
Now you will find a fresh Google access_token for this user (without asking for offline access)
Resources:
https://auth0.com/docs/quickstart/spa/angularjs/03-session-handling
https://auth0.com/docs/quickstart/spa/angularjs/11-sso

In ASP.NET is there an event fired on a Windows Authentication log in failure? (Logging the details of a Windows Authentication failure)

I am building a .NET 4.0, ASP.NET MVC 3 intranet application that runs on IIS 7.5 in integrated mode. Windows Authentication is used to govern access to the website. The Windows Authentication module is enabled and all other auth modules are disabled.
Currently when a user provides improper credentials, the Windows Authentication module correctly rejects the credentials and re-displays a login prompt. It does so 3 times, after which a standard .NET 401 Unauthorized Access page is shown. This is expected and desirable.
My goal: I would like to be able to log the details of the failed authentication attempt to my own custom event log. Particularly, to capture the user name that was used in the log in attempt. (I'll accept that capturing the password is not likely to be possible for security reasons.)
Is my goal possible?
I have already built a working an IHttpModule module and added it as an event handler to the WindowsAuthenticationModule, like this:
myWindowsAuthenticationModule.Authenticate += WindowsAuthentication_Authenticate;
But my code does not get called in the case of a failed log in attempt, presumably because WindowsAuthenticationModule has already decided that the log in is failed and so there is no point calling my module. My module does get called after a successful log in attempt, and so I am certain that my event handler is properly set up.
To the best of my knowledge, the WindowsAuthenticationModule does not expose an event that is fired when authentication fails, so that option is out.
Any ideas? Or am I barking up a tree that has no solution?
I was looking at the same issue, and looks like there is no events for windows authentication, even that Authenticate event is common for forms and windows.
But I found a solution to this!
http://www.codeproject.com/Articles/11202/Redirecting-to-custom-401-page-when-quot-Access-de
UPDATE
From original article
protected void Application_EndRequest(Object sender, EventArgs e)
{
HttpContext context = HttpContext.Current;
if (context.Response.Status.Substring(0,3).Equals("401"))
{
if(User.Identity.IsAuthenticated)
{
// this means user is authenticated, but 401 still returned
// which means no access to page or whatever you are trying to access?
}
}
}
UPDATE2
I also found out that this solution doesn't work in all cases. I was testing in different environments, so was working for me, but not for others.
By default IIS will not even run this piece of code and just return it's own error page, what you want to do is tell IIS to let app handle errors.
<httpErrors existingResponse="PassThrough">
</httpErrors>
Now, with that said, IIS won't return any more of custom errors and you will need to handle them in application, ie. not only 401, but 403, 405, etc

Why doesn't LogonUser(...) work for domain accounts?

I've been trying to use LogonUser(...) to get an access token for a user account, as in this MSDN sample.
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(userName, domainName, Console.ReadLine(),
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
out safeTokenHandle);
When I run the sample (with Administrator privileges) it works fine when given a domain of . and a local user account name and password, but no matter what I do I get error code 1326 (Logon failure: unknown user name or bad password) if I try to use a domain account. I get the same result if I enter garbage for the domain, which makes me wonder if it's actually contacting the DC at all.
What could be stopping this from working?
In my case the issue, similar to the question asker, was that the account I was trying to authenticate to was in a domain that my current machine did not belong to. Unlike the original poster, my machine should not and could not be part of this other domain. I wanted the login to perform action on a resource on this domain though.
The answer was the following
bool success = LogonUser(
userName,
domain,
password,
(int)LOGON32_LOGON_NEW_CREDENTIALS, //9
(int)LOGON32_PROVIDER_DEFAULT, //0
out userToken);
with the following constants defined:
public const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
public const int LOGON32_PROVIDER_DEFAULT = 0;
Hopefully this will help others who are lost in a similar situation.
Edit: As mentioned in the comments below, this logon type allows the caller to clone its current token and specify new credentials for outbound connections. The new logon session has the same local identifier but uses different credentials for other network connections. As a result of that fact, "success" will return true even if the password is bad. You will need an additional check beyond "success" to confirm that the credentials are actually good.
This was not a concern in my initial use case as we used the current network user's credential in another function to pull the plaintext password from secure storage. So it would have never been wrong unless there was an inconsistency between that system and active directory in which case we had bigger problems.
In my case it was the fact that, although I was logged in to my computer as a domain user, my computer was not itself part of the domain. Once added to the domain the sample started to work.
Use DOMAIN\LOGIN with an empty domainname for that case...

Resources