onAuthRequired triggering only once in Chrome version > 72 - selenium

In our project we are using Selenium to test our Angular Web Application using a docker image and chrome with version 70.0.3538.77 (Official Build) (64-bit). Since the application uses basic authentication on our servers we created our own extension to listen to "onAuthRequired" and whenever this happens we return the username and password (Original Source).
A simple test for our application would be to navigate to the main page (basic auth login required with the popup window) and then to another subpage that is hosted inside of this page in an iframe where the basic authentication is required again.
We added some logging to the extension just to check whether the onAuthRequired gets triggered so the extension currently looks like this:
​if (!chrome.webRequest.onAuthRequired.hasListener(retrieveCredentials)) {
chrome.webRequest.onAuthRequired.addListener(
retrieveCredentials,
{urls: ["<all_urls>"]},
['blocking']
);
}
function retrieveCredentials(details) {
console.log("onAuthRequired");
return {
authCredentials: {
username: username,
password: password
}
};
}
What we found out is that with the Version 72.0.3626 this was still working. The extension would fill out both of the login prompts that occur and we would see two messages with "onAuthRequired" in the console.
However with the Version 73.0.3683 and newer, this seems to not work anymore. Only one time the onAuthRequired (retrieveCredentials-Method) is called and the second popup gets ignored by the extension. This lets the Selenium tests fail with a timeout exception since the navigation doesn't work anymore.
Does anyone know how to handle this in the newer Versions of Chrome or sees a mistake that we made in our extension?
Any help appreciated

Related

What is the RIGHT way to access Google websites from within an electron app? [duplicate]

A user of my app reported an issue today about authorizing the user with Google (using OAuth 2.0). So far the application was opening a new BrowserWindow (node integration disabled, session is separated from the main application). You can see the implementation here since the library is OSS. I am using this to authorize the user to access application data on Google Drive.
Today after logging in I see the following message:
This browser or app may not be secure.
Try using a different browser. If you’re already using a supported browser, you can refresh your screen and try again to sign in.
The learn more link has a section for developers. This section has 2 links. One is how to upgrade the application to PWA. Because the application is an API testing tool it won't be possible to run it in a web browser.
The second link points to a document describing how to migrate to authorization for native application. However described flow requires authorization_code grant. This means I need to include OAuth secret into my application. Electron application, however, is still web application and there's no notion of compiling sources. I would expose client secret to the public which is not secured. Potentially I could build a server application to support it but the app is OSS project. It does not have funding to run a server for authorization.
My question is now how should I implement OAuth 2 for Electron application then. I can't use PWA's and server authorization flow (code grant) is far from ideal in this case.
As Paweł explained, changing the user agent will do the trick. However, you can easily set the user agent by passing an object when loading the URL
win = new BrowserWindow({width: 800, height: 600});
win.loadURL(authUrl, {userAgent: 'Chrome'})
I have tested it and it worked like a charm
Warning: This answer relies on changing the browser's user-agent. As of Jan. 2021, Google disapproves of this and warns not to do this (see EDIT4). Use at your own risk!
The other answers didn't work for me (in Electron 9.0.5), but I eventually found this, which worked:
app.on("ready", ()=> {
session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
details.requestHeaders["User-Agent"] = "Chrome";
callback({ cancel: false, requestHeaders: details.requestHeaders });
});
CreateMainWindow(); // your regular code to create root browser window
});
EDIT: Two other approaches, which I haven't tested, but which may also work:
app.on("ready", ()=>{
session.defaultSession.setUserAgent("Chrome");
...
}
app.userAgentFallback = "Chrome";
EDIT2: Trying again sometime later, approach #2 did not work, but #1 still did. Haven't tried #3 yet.
EDIT3: Trying again later still, it seems that none of these workarounds are needed anymore! Google appears to accept sign-in popups from Electron apps again, without modifications to the user-agent. (odd that they'd revert this; perhaps I just did something wrong in my re-attempt)
EDIT4: While approach #1 still works atm, I recently found this blog post: https://developers.googleblog.com/2020/08/guidance-for-our-effort-to-block-less-secure-browser-and-apps.html Apparently Google is restricting usage of Google sign-in in non-standard browsers (which presumably includes Electron) starting in Jan. 2021, and warns developers not to modify their browser's user-agent (which all three of the possibilities I mention do). Use at your own risk! (they don't make clear what outcome will result, but for my own use, I'm opting to use the alternative shown below from now on)
As an alternative to using a Google sign-in popup in your app (which some might be wary of, since Electron apps could in principle insert code into the popup to read the raw password -- not that it matters that much, since Electron apps could just install keyloggers or the like anyway), you could instead open a tab in the user's regular external browser, pointed to a special page that triggers a sign-in popup there, and then just sends the credentials to your Electron app afterward.
Instructions can be seen here (approach 3): https://stackoverflow.com/a/64328193/2441655
After taking a wild guess I decided to alter the user agent string and to remove application name from it as well as Electron/ with version. After this alteration it started working again.
Example implementation:
const win = new BrowserWindow(params);
let ua = win.webContents.userAgent;
ua = ua.replace(/APPLICATION NAME HERE\/[0-9\.-]*/,'');
ua = ua.replace(/Electron\/*/,'');
win.webContents.userAgent = ua;
This assumes the application is using symver and no pre-release tags. Otherwise you would have to tweak the regexp a bit.

Laravel 8 Dusk: testing login using Jetstream and Inertia JS is not working

I am working on a Laravel 8 project. I have noticed that a couple of things have changed in the Laravel 8 including authentication. I am using Jetstream and Intertia JS for authentication and admin panel. I am now having trouble writing Browser test using Dusk for login. It seems to be that the login is not working in the test even though it is actually working.
This is my test
function test userCanLogin()
{
$this->browse(function (Browser $browser) {
$admin = User::factory()->create();//I make sure that the password is Test.1234 and I can login on the browser.
$browser->visit('/login')->type('email', $admin->email)
->type('password', 'Test.1234')
->press('Login')
->assertAuthenticatedAs($admin);
});
}
When I run the test, I am getting the following error.
The currently authenticated user is not who was expected.
Failed asserting that two arrays are identical.
--- Expected
+++ Actual
## ##
-Array &0 (
- 'id' => 1
- 'className' => 'App\Models\User'
-)
+Array &0 ()
What is wrong with my code and how can I fix it?
I'm not 100% sure but I think by default Dusk waits for a page refresh before doing asserts. Since Inertia is loading via JS you need to either explicitly pause() or waitFor() some element that would indicate that the page has been loaded. (See Dusk docs on waiting)
There's also this library that adds events to Inertia.js that you can listen for in Dusk

Anti forgery token and web testing

I am trying to do a web test in VS2012 for an MVC site.
One of the scenarios is to login and go through a list of products, select the one you want and follow through to the purchase page.
Problem is that when the web test is run, I get an error about the anti forgery token and that it does not match.
How on earth is it possible to do the testing with the anti forgery token? The user must login - there will be thousands of users for the load test (eventually) but need to make it work for 1 user first.
the login view/action does do an AntiForgeryToken in the view and validation on the controller.
any advice and tips are appreciated.
Once you run your script and it fails, go to the call proceeding the one that fails.
Go to the response tab
In the body, find the __RequestVerificationToken name which is in an input tag and extract everything in between the value attribute.
Select the value and right click > add extraction rule and press OK.
You will find an Extraction rules folder and underneath it, the Extraction rule we just created. Feel free to rename the Context Parameter Name.
Go to the next page , which should be the one that failed, and find the Form Post Parameter named "__RequestVerificationToken". View it's properties
Bind it to the Context Parameter Name created previously. To do so, view the properties of this post parameter and set the "Value" to be:
{{Name Of Context Parameter}}
(Include the 2x curly braces)
Press enter to confirm/save
Next time you run the script - all works
This is how it worked for me...
I was seeing a similar problem. After recording a web test script, the script would fail at the point of log-in on with the following message:
The provided anti-forgery token was meant for user "Domain\UserName", but the current user is "".
The solution was to set the PreAuthenticate property to false in the test properties. By default the web tests will pass an authentication header to the server which was being used in the generation the token.
I am not familiar with "web testing in VS2012" but as I know "Anti-Forgery Token" requires sending the token from browser to the server back.
I had an experience with Selenium-Webdriver and suggest you use it because it provides an API to interact with supported browsers as real user does.
You can easily start using Selenium WebDriver if you add Selenium WebDriver 2.37.0 NuGet package to you test project.
Selenium-WebDriver makes direct calls to the browser using each
browser’s native support for automation. How these direct calls are
made, and the features they support depends on the browser you are
using.
Selenium-webdriver currently supports the following drivers:
Chrome
Internet Explorer
Firefox
Opera
HtmlUnit
Android

Login to Facebook Connect using JavaScript SDK freezes in Opera?

I have a Facebook Connect site under development. The login works great on Firefox, IE, Safari and Chrome. But when I test it using Opera (using the latest version = 11) I run into problems.
I get to the point where I get the login form pop-up, fill it in with my email and password and click the login button. After that the login window becomes empty and the window title says "XD Proxy".
I am using the Facebook Connect JavaScript SDK and there are some iFrames (displayed in the jQuery lightbox-style plugin "ColorBox") in my solution.
Do you know if there are problems with Opera and Facebook Connect, or if there's something in my code that causes this? As I said, the solution has been tested in all the other major browsers and work fine there.
Thanks in advance!
/Thomas
PS. On the development server I'm running the site on port number 8585. I've seen some posts on the net saying this can cause problems.
Facebook connect currently fails because Facebook's scripts "detect" Internet Explorer by checking if the attachEvent method is supported. Unfortunately, Opera supports this method (historically for compatibility with other sites that needed it). Now, if they only did this: https://github.com/facebook/connect-js/pull/240 ..
This broken pseudo-browser-detection causes the problem because Facebook happens to use some IE code that Opera does not support to embed Flash for cross-document messaging in the Flash-based part of the script, while the alternate window.postMessage() part of the script has other problems as described here: http://my.opera.com/hallvors/blog/2010/07/20/postmessage-s-targetorigin-and-security
I have found a solution that seems to work! If I add a channelUrl option to FB.init, I can log in. I got a warning from Opera saying the following:
Warning
http://www.yourdomain.com/channel.html?fb_xd_fragment
A page on the public Internet requests
data from your private intranet. For
security reasons, automatic access is
blocked, but you may choose to
continue.
LINK: Continue
LINK: Always continue when data is
requested from this server on my
private intranet
I clicked the link "Continue" and after that it worked (during this session). The original FB.init line looked like this (appId and some other stuff has been replaced in this example):
// Facebook init
FB.init({ appId: '123456789', status: true, cookie: true, xfbml: true });
After I had added the channelUrl it looked like this:
// Facebook init
FB.init({ appId: '123456789', status: true, cookie: true, xfbml: true, channelUrl: "http://www.yourdomain.com/channel.html" });
The file "channel.html" is a simple file that contains just this line of code:
<script src="http://connect.facebook.net/en_EN/all.js"></script>
Create it and put it on your server at the location you have specified in the channelUrl. The server root is a good place.
The next step is figuring out how to get rid of the Opera warning, if it's possible at all?
Good luck!
/Thomas Kahn
To fix it in Opera, just add the following after FB.init():
if($.browser.opera ) // it uses jQuery library here!
{
FB.XD._transport="postmessage";
FB.XD.PostMessage.init();
}
This channel.html dont work for me
<script src="http://connect.facebook.net/en_EN/all.js"></script>
but when I change it to:
<script src="http://connect.facebook.net/en_GB/all.js"></script>
it works. In this url is not valid content http://connect.facebook.net/en_EN/all.js there is something wr go to this instead http://connect.facebook.net/en_GB/all.js

Over-ride Browser Authentication Dialog

Is there a way using Java to over-ride the browser authentication dialog box when a 401 message is received from the web server? I want to know when this dialog is being displayed, and instead of it being given to the user, I fill in the credentials for them.
Overview of application:
i wrote the web server, so essentially i want to stop someone from opening an external browser and putting in the localhost and port to gain access to the data being displayed. my app has an embedded web browser linked to my written server. the browser displays decrypted content, so if i force the auth (even for my embedded browser), an external browser would need credentials. if my embedded browser is trying to access the files, i supply the credentials for the user and display the content
If you don't care about the password showing you can construct the URL so it passes the credentials ex. http://username:password#www.example.com This will by pass the authentication box but will show the user the credentials so also might not be what you are looking for.
SWT 3.5M6 has a new listener within it call AuthenticationListener. It simply listens for authentication event passed from the server and is fired. The code below is what performs the behavior I wanted. It waits for the auth, and if the host is my application, it passes back the credentials. Of course fill in the USER_NAME, PASSWORD and HOST_NAME with appropriate variables. Otherwise it lets the browser auth dialog pop up and makes the user enter the credentials. This code can also be found in the Eclipse SWT snippets page:
webBrowser.addAuthenticationListener(new AuthenticationListener()
{
public void authenticate(AuthenticationEvent event) {
try {
URL url = new URL(event.location);
if (url.getHost().equals(HOST_NAME))
{
event.user = USER_NAME;
event.password = PASSWORD;
}
else
{
/* do nothing, let default prompter run */
}
} catch (MalformedURLException e) {
/* should not happen, let default prompter run */
}
}
});
your question is a bit unclear. The whole basic authentication is based on HTTP Headers.
If the browser gets an authorization header than it displays the dialog. The content from the dialog is then send back to the server. There is nothing special about it. It iser username:password in base64 encoded. Have a look at
wikipedia
The problem is how you want to interfere. You would have to capture the authorization header and then for the next request you have to alter the HTTP header to include the credentials.
hope that helps
I think this is mostly browser-dependent behavior and what the server reports to the browser.
For example, Internet Explorer, being a Microsoft product, directly supports automatic sending of Windows credentials (you can modify this behavior in your Internet Settings) after an anonymous request fails in a 401.
Firefox, for example, does not and will always prompt the user even if it was set to remember the id and password via the password manager. IE will also prompt if auto-login fails (such as your Windows credentials still result in a 401 because you're id isn't allowed).
I don't think, as a web developer, you have much control over this besides setting up your server and app to work in the most expected and harmonious way... if you could, this might get into black hat territory.
If you want to control what is displayed to the user for authentication, you can change the auth-method in the login-config section of the web.xml from BASIC to FORM.
Then you can specify what page should be displayed when the user is authenticating, and, I suppose, pre-fill the credentials for them...but doesn't this defeat the whole purpose of security?
Setting up Authentication for Web Applications
Edit after further details:
My only suggestion would be to change the auth-method to CLIENT-CERT and require two-way SSL, where the client is also required to present a certificate to the server. If you install the certificate into your embedded browser (and make sure external browsers can't get the certificate) then you should be OK. And actually this should stop any authentication dialog from being displayed.