I've got web application with browser authentication before webpage is loaded so in automated test i am log in via http://user:password#domain but when i am entering wrong credentials, pop up would not disappear it would wait for correct credentials. But i want to test if there is a access to webpage with wrong credentials, every browser is closing without problem, but IE is throwing
modal dialog present
i was trying to use
driver.SwitchTo().Alert().Dismiss();
but it doesn't work.
any idea how to close that pop up authentication?
Authentication popup is NOT generated by Javascript / it is not a javascript alert. So It can not be handled by WebDriver.
You did not mention the programming language you use. Your sample code seems to be in C#. In Java - we have a java.awt.Robot to simulate keyboard events. You might have to find C# equivalent to press the ESC key.
Robot robot = new Robot();
//Press ESC key
robot.keyPress(InputEvent.VK_ESCAPE);
robot.keyRelease(InputEvent.VK_ESCAPE);
In project I currently work in I decided to take completely another approach. I had similar situation of NTLM authentication but I'm pretty sure that for basic authentication it will work as well. I wrote simple chrome extension which utilizes listener on chrome.webRequest.onAuthRequired. Additionally, by putting additional methods in content script to communicate with background script I've managed a way to change credentials on the fly without caring about annoying windows.
background.js:
var CurrentCredentials = {
user: undefined,
password: undefined
}
chrome.runtime.onMessage.addListener(function(request) {
if(request.type === 'SET_CREDENTIALS') {
CurrentCredentials.user = request.user;
CurrentCredentials.password = request.password;
}
});
chrome.webRequest.onAuthRequired.addListener(function(details, callback) {
if(CurrentCredentials.user !== undefined && CurrentCredentials.password !== undefined) {
return {authCredentials:
{
username: CurrentCredentials.user,
password: CurrentCredentials.password
}
};
}
}, {urls: ["http://my-server/*"]}, ["blocking"]);
and content-script.js:
var port = chrome.runtime.connect();
window.addEventListener("message", function(event) {
if (event.source !== window)
return;
if (event.data.type && (event.data.type === 'SET_CREDENTIALS')) {
chrome.runtime.sendMessage({
type: 'SET_CREDENTIALS',
user: event.data.user,
password: event.data.password
});
}
}, false);
Extension must be packed as crx and added to ChromeOptions prior to driver initialization. Additionally, it is required to set credentials BEFORE actual call to site that needs authentication, so I browse simple html file on the disk and post a chrome message while being on the page: window.postMessage({type: 'SET_CREDENTIALS', user: arguments[0], password: arguments[1]}, '*') by using IJavascriptExecutor.ExecuteScript method. After it is done, no authentication window shows up and user is authentication as expected.
Related
I'm using Cypress to test a login flow that uses Magic.link auth on a mobile Web device, which is encountering the ResizeObserver loop limit exceeded error, as it tries to navigate the Google Auth forms. I've looked at numerous posts, and played around with my test, but it seems the handler is not working.
The recommended Google Authentication from the Cypress docs is insufficient, because with Magic, the flow is initiated by a call to magic.oauth.loginWithRedirect, hence I was hoping to drive the process via the UI directly.
You'll see I added a test to ensure the password input is visible. Now the exception is being thrown at that part of the test. If I remove that check the error occurs on the next step where I try to type the password.
describe('my auth flow', () => {
it('can auth with google', () => {
// click login button from my site
cy.get('button')
.contains('sign-in')
.click();
cy.origin('https://accounts.google.com', () => {
// enter email address
cy.get('input[type=email]')
.type('myuser#mydomain.com');
cy.get('button')
.find('span')
.contains('Next')
.click();
// wait for password page to show
cy.get('#password')
.should('exist')
.and('be.visible'); // error here...
// enter password
// error here if above visibility check removed
cy.get('#password input[type=password]')
.type('mypassword');
cy.get('button')
.find('span')
.contains('Next')
.click();
});
});
});
In support/commands.js, I've added the global error handler, which should handle all uncaught exceptions according to the documentation.
Cypress.on(
'uncaught:exception',
(err) => false
);
Magic does have a test mode, however I really don't want to bypass the login flow. Ideally I could exercise the login flow without hacks for testing.
The cy.origin() command is an isolated sandbox with different document and window to the primary domain.
Try adding the exception handler inside the origin command (presuming the error is happening while on the google domain).
cy.origin('https://accounts.google.com', () => {
Cypress.on('uncaught:exception', (err) => false)
I'm working on e2e test with cypress on my application.
In my case the login are manage by a external service.
When I want to enter in my application's home page (https://myApplication/home), the system redirects me in different superdomains to login.
At first cypress seems to be able to change the superdomain, but once arrived in external service page for the authentication, the system go in login error (as if we have already logged in, but incorrect).
This type of behavior does not happen outside the cypress .
Are there alternative solutions to manage external access in a cypress test or is it possible to manage it directly from cypress?
I added in my cypress.json the chromeWebSecurity:false and when I call the link for login, I added the failOnStatusCode: false,
but it still doesn't work.
Assuming this is caused by SameSite cookie blocking , then I've just been fighting the same issue. I resolved it by intercepting all requests, checking if they had a set-cookie header(s) and rewriting the SameSite attribute. There's probably a neater way to do it, as this does clutter up the cypress dashboard a little.
Sadly Zachary Costa's answer no longer works as Chrome 94 removed the SameSiteByDefaultCookies flag.
You can add this as a command for easy reuse:
In your commands file:
declare namespace Cypress {
interface Chainable<Subject> {
disableSameSiteCookieRestrictions(): void;
}
}
Cypress.Commands.add('disableSameSiteCookieRestrictions', () => {
cy.intercept('*', (req) => {
req.on('response', (res) => {
if (!res.headers['set-cookie']) {
return;
}
const disableSameSite = (headerContent: string): string => {
return headerContent.replace(/samesite=(lax|strict)/ig, 'samesite=none');
}
if (Array.isArray(res.headers['set-cookie'])) {
res.headers['set-cookie'] = res.headers['set-cookie'].map(disableSameSite);
} else {
res.headers['set-cookie'] = disableSameSite(res.headers['set-cookie']);
}
})
});
});
Usage:
it('should login using third party idp', () => {
cy.disableSameSiteCookieRestrictions();
//add test body here
});
or alteratively, run it before each test:
beforeEach(() => cy.disableSameSiteCookieRestrictions());
We were encountering a similar issue, where Cypress was redirecting us to the default "You are not logged in" page after getting through the login process. I'm not certain if that's EXACTLY the issue you were experiencing, but just in case, here's our solution. In our case, the issue was caused by Chrome's "Same Site Cookies" feature interacting poorly with Cypress, so we needed to disable it. In your plugins/index.js file, you would add the following code:
module.exports = (on, config) => {
on('before:browser:launch', (browser, launchOptions) => {
if (browser.name === 'chrome') {
launchOptions.args.push('--disable-features=SameSiteByDefaultCookies');
}
return launchOptions;
});
};
Note that if you already have launchOptions being set, you can just add this code onto it so it doesn't clash at all.
Hopefully, this works for you as well!
In the current version of cypress you can't go to another domain in the same test. This is due to the fact that cypress injects its test into the browser (they are working on this issue).
So one solution today is that you need to utilize cy.request to perform the login programmatically and inject the auth secret (jwt, cookie, localstorage, token or what you have) into the browser context yourself (for cookie this would be cy.setcookie).
Always make sure to checkout the plugins if there is already an abstraction for your login. Often this is openId or ntlm.
I am starting with Polymer and Firebase and have implemented the Google OAuth authentication.
I have notice the page loads before authentication and if you click back you can get to the page without authorization, albeit that you are not able to use the firebase api and therefore the page is not usable.
My issue is that I do not want my javascript loaded until authenticated.
How could this be done.
Many thanks
It depends if your using firebase or their polymer wrapper, polymerfire.
Create a document for all the imports that you want to be conditionally loaded
// user-scripts-lazy.html
<link rel="import" href="user-script-one.html">
<script src="script.js"></script>
// etc
Using Polymerfire
In the element that hosts <firebase-auth> create a observer and you'll expose some variables from firebase-auth.
<firebase-auth
user="{{user}}"
status-known="{{statusKnown}}"></firebase-auth>
In the observer, watch the user element and the status known
statusKnown: When true, login status can be determined by checking user property
user: The currently-authenticated user with user-related metadata. See the firebase.User documentation for the spec.
observers:[
'_userStateKnown(user, statusKnown)'
]
_userStateKnown: function(user, status) {
if(status && user) {
// The status is known and the user has logged in
// so load the files here - using the lazy load method
var resolvedPageUrl = this.resolveUrl('user-scripts-lazy.html.html');
this.importHref(resolvedPageUrl, null, this.onError, true);
}
}
To get the state without using polymerfire you can use onAuthStateChange
properties: {
user: {
type: Object,
value: null // important to initialise to null
}
}
..
ready: function() {
firebase.auth().onAuthStateChagned(function(user) {
if(user)
this.set('user', user); // when a user is logged in set their firebase user variable to ser
else
this.set('user', false); // when no user is logged in set user to false
}.bind(this)); // bind the Polymer scope to the onAuthStateChanged function
}
// set an observer in the element
observers: [
'_userChanged(user)'
],
_userChanged: function(user) {
if(user === null) {
// authStatus is false, the authStateChagned function hasn't returned yet. Do nothing
return;
}
if(user) {
// user has been signed in
// lazy load the same as before
} else {
// no user is signed in
}
}
I haven't tested the code while writing it here, but i've implemented the same thing various times.
There are a couple of options.
Put content you don't want loaded behind a dom-if template with "[[user]]" as its driver. This could include your firebase element, so the database isn't even considered until after log on.
Put a modal dialog box up if the user is not logged on. I do this with a custom session element . Whilst the overlay is showing then the rest of the page is unresponsive to anything.
If it is simply an aesthetic issue of removing the non-logged-in page from view, could you either hide the page (or display some kind of overlay) while the user isn't authenticated?
I currently have this in an current project for some elements: hidden$="{{!user}}"
I have identified the solution for my purpose ...
Add storage role based authorization (see is there a way to authenticate user role in firebase storage rules?)
This does have a limitation currently of hard coded uid's
In the page, request storage resource and if successful include it in the dom (i.e. add script element with src pointing to storage url)
Call javascript as normal
I'm trying to connect to SoundCloud using CasperJS. What is interesting is once you signed in and rerun the login feature later, the previous login is still active. Before going any further, here is the code:
casper.thenOpen('https://soundcloud.com/', function() {
casper.click('.header__login');
popup = /soundcloud\.com\/connect/;
casper.waitForPopup(popup, function() {
casper.withPopup(popup, function() {
selectors = {
'#username': username,
'#password': password
};
casper.fillSelectors('form.log-in', selectors, false);
casper.click('#authorize');
});
});
});
If you run this code at least twice, you should see the following error appears:
CasperError: Cannot dispatch mousedown event on nonexistent selector: .header__login
If you analyse the logs you will see that the second time, you were redirected to https://soundcloud.com/stream meaning that you were already logged in.
I did some research to clear environments between each test but it seems that the following lines don't solve the problem.
phantom.clearCookies()
casper.clear()
localStorage.clear()
sessionStorage.clear()
Technically, I'm really interested about understanding what is happening here. Maybe SoundCloud built a system to also store some variables server-side. In this case, I would have to log out before login. But my question is how can I perfectly isolate and clear everything between each test? Does someone know how to make the environment unsigned between each test?
To clear server-side session cache, calling: phantom.clearCookies(); did the trick for me. This cleared my session between test files.
Example here:
casper.test.begin("Test", {
test: function(test) {
casper.start(
"http://example.com",
function() {
... //Some testing here
}
);
casper.run(function() {
test.done();
});
},
tearDown: function(test) {
phantom.clearCookies();
}
});
If you're still having issues, check the way you are executing your tests.
Where did you call casper.clear() ?
I think you have to call it immediately after you have opened a page like:
casper.start('http://www.google.fr/', function() {
this.clear(); // javascript execution in this page has been stopped
//rest of code
});
From the doc: Clears the current page execution environment context. Useful to avoid having previously loaded DOM contents being still active.
I am trying to write an FirefoxOS app for my portal which uses Mozilla Persona for authentication. How I should proceed if I want to achieve:
Allow users of my app to signup to my portal using Persona
Allow users of my app to login to my portal within the FirefoxOS app and perform some actions with the API
Depends if users is logged or not - giving access to different actions.
I have found this post with info that its integrated already: http://identity.mozilla.com/post/47114516102/persona-on-firefox-os-phones but I can't find any real examples.
What type of application I need to create? webapp or privileged?
I am trying to implement it using regular tutorial: https://developer.mozilla.org/en/Persona/Quick_Setup
But with this code:
signinLink.onclick = function() { navigator.id.request(); };
I am getting only following error:
[17:25:18.089] Error: Permission denied to access object
One thing is to make sure you're calling watch() to setup callbacks before you call request().
For example, something like this in your page:
<script src="https://login.persona.org/include.js"></script>
<script>
window.addEventListener("DOMContentLoaded", function() {
navigator.id.watch({
// Provide a hint to Persona: who do you think is logged in?
loggedInUser: null,
// Called when persona provides you an identity assertion
// after a successful request(). You *must* post the assertion
// to your server for verification. Never verify assertions
// in client code. See Step 3 in this document:
// https://developer.mozilla.org/en/Persona/Quick_Setup
onlogin: function(assertion) {
// do something with assertion ...
// Note that Persona will also call this function automatically
// if a previously-signed-in user visits your page again.
},
onlogout: function() {
// handle logout ...
},
onready: function() {
// Your signal that Persona's state- and callback-management
// business is complete. Enable signin buttons etc.
}
});
// Set up click handlers for your buttons
document.getElementById("signin").addEventListener(
'click', function() {
navigator.id.request({
// optional callback to request so you can respond to
// a user canceling the sign-in flow
oncancel: function() { /* do something */ }
});
}
});
});
</script>
Here's an example that shows this in action:
https://people.mozilla.org/~jparsons/persona_example.html
However, on FirefoxOS, you should be aware that installed apps (not packaged or certified, but generic installed apps) are given a unique origin of the form app://{uuid}, with a different uuid for each device. This is unfortunately useless for sign-in purposes because there's no way for your server to know whether an app requesting sign-in is friend or foe. The way around this problem is to run your persona code in an invisible iframe hosted on your server. Thus the iframe will have the correct origin and your server will know it's your app. The iframe and the app can communicate via postMessage.
In the case of a packaged app (sometimes called a privileged app), your origin will be the origin declared in your webapp manifest. E.g., app://yourapp.yoursite.org. This gives you better assurance that the app is really yours, but the truly paranoid may still wish to deploy the iframe trick.
Hope this helps!
j