Hi,
I'd love to use Cypress for testing sites that use Bearer token authorization - every .visit()
should automatically have a header added. I couldn't find a place where these additional headers could be added, except maybe in socket.coffee - would that be the right place?
Thanks for any pointers!
I'm confused. How does your app do this normally? Doesn't it automatically add the headers to the requests? Or are we talking about basic auth here?
Do you use an extension or modify the browser to use your site?
Probably different use case from the OP, but having the ability to set custom headers (via cy.visit(url, options.headers)
would be cool for auth mocking -- currently I have to set custom cookie before issuing cy.visit
to handle this.
The tests need to run as a service account. As myself, I'd login through our separate login server, but that requires dual authentication and is not usable for service accounts - so I need to send a header with every request. Does it make sense?
And yes, for certain purposes we do use a wrapped browser :)
We could easily add headers to the visit
but my question is - why would that work? Sure that single request may get authorized by your server but then all subsequent requests from the browser wouldn't have that header - so likely they would then not go through since web servers are completely stateless.
It seems we'd likely have to modify all requests to have this header. If that's the case it opens a case of questions like - should the headers be modified for only a single host? What about multiple hosts?
What I need is a concrete example / reproducible repo. It's possible we can add / modify headers at the network layer for all requests but I have to understand what API's are necessary in order to do this.
The way I see it, there could be a config dictionary. It's keys would be hostnames (probably with wildcards or regexps), and the values would be functions returning a dictionary with header names and values.
And before making a request, Cypress would look if currently requested hostname has an entry, and would get the headers and append them to the request.
Sure - but that's an implementation detail and it doesn't really answer my question.
Why is it necessary to add these headers at the lower level network layer. If we're bypassing the way browsers work normally, how could this possibly be a useful solution in the real world. It wouldn't work in any situation.
I need to understand the use case for what Cypress would be replicating. For instance if you said something like - my application works like X, so we do Y, which enables us to work with Z. I need Cypress to replicate X, Y, and Z, then we could come up with an implementation.
Without a clear use case and understanding of the mechanics of why this is necessary this feature will never be built.
I have the same issue, what we actually ask for is a headers
option in visit
as it exists in request
.
Even on "normal" browser tasks this makes sense, for example when the page is restricted for specific client or behaves different, when browser adds DoNotTrack-
or Accept-Language
header.
@gregor-tb Your comment does not make sense to me.
You say: "When the page is restricted for a specific client". Give me an example of this. How could you restrict a page for a specific client? Are we talking about basic auth? You can already test that in Cypress.
When we implement this feature, we are going to provide you functionality that does not exist in a normal browse - because there is no way to set a request header for a normal non-XHR HTTP request.
My question is this: in what scenario would a real application ever need this functionality?
For instance, let's step back a moment. Imagine you're not inside of Cypress. Imagine you're in your web browser. You type in a URL. In no situation are there are additional headers attached to the request without you doing something special. Therefore, how in a real world application could this ever be helpful? This is what we're trying to understand better.
What are you doing to achieve this right now without Cypress? How are you getting additional request headers on your non XHR requests? Are you using an extension? Are you using a special profile? Are you using a special version of Chromium? Are you modifying something on your operating system?
If someone could fully answer this question then we can understand what it is we need to build. As it stands there has not yet been enough information provided.
Even when you say: "you want headers option like you have in cy.request
. cy.request
is not something the browser even gives you - its something that only Cypress adds which bypasses the normal security mechanisms of the browser. It's not an actual example because it's not actually something a browser can do by itself.
I can setup my browser to send different headers, as I mentioned DoNotTrack
is a browser preference or Accept-Language
can be different on your locale.
Of cause this does not open the door to legit _full_ header control in a real world application, but when we're looking for a reason to implement it, these are examples that can benefit from it.
With "client" I mean user_agent
, sorry for misunderstanding. Example: I visit with a old browser and test, if my deprecate warning pops up.
At the end this tool is designed for developers, setting _custom_ headers is not something you do by accident. ;)
Setting the userAgent
was something we just merged and is about to be released.
Setting custom request headers makes sense based on what you said.
Both options will not be dynamic - they will persist for the entire browsing session, so you'll have to partition your specs to the ones that specifically need those.
Is there an update on this?
This would be nice to have as we use headers to have Nginx redirect you to our mocked site or our real one.
Hi !
We have the same need as we use Distill to protect our sites and it detects cypress requests as robot requests. There's a workaround adding a specific "X-Distil" header to the browser request.
So having this option in cy.visit
would be perfect!
@davidbarna Would you also need those headers on all subsequent requests? Not just the visit?
Every subsequent request would be nice, or at least the option.
The ability to set arbitrary headers on an individual visit would be extremely useful to us as well, for a different use case.
One of our applications can be accessed via the web or from a webview within an iOS app. The webview attaches certain headers when the page first loads that allow us to authenticate the user if they've previously authenticated.
After that point, the two ways of accessing the app are more or less identical, so we just need to test that the web app picks up the header and uses that for auth. If we could specify the headers in the cy.visit
call, that would be perfect. (As it is, we can use cy.request, but that only lets us test that the client page renders appropriately, not that the Elm app behaves right after that.)
Thanks for the awesome testing framework! It's made a huge difference for us.
@arsduo I guess what I don't understand about this is that your app should already be doing that correctly - and that can be tested in Cypress.
When you attach new headers via a cy.visit
then you're going outside of the way the browser would normally work.
For instance, likely the token attached to the headers is stored in local storage. With local storage cleared, your web app won't be authenticated and should react accordingly - which you would test. Once it authenticates, it'll automatically apply the header, and then react accordingly to test - which you would also test.
If that's the case - then you can already use localStorage as the valve by which to control how you want your app to behave.
This talk I gave at AssertJS tests exactly this functionality described: https://www.youtube.com/watch?v=5XQOK0v_YRE
I'm not seeing what you would be able to gain from setting headers with cy.visit
that you can't already do now.
@brian-mann the header is attached by the iOS app containing the web view as the web view makes the request for the page. The first time the app loads, the header is present and localStorage is empty, so the app needs to react to the presence of the header, verify the token, and then store it for subsequent use.
The tests I would have would be:
In this case, the ability to add an arbitrary header to visit simulates the iOS app's addition of an arbitrary header to the page load.
Hopefully that makes sense -- happy to explain more.
So wait - you're wanting to test and simulate the way the iOS would work on a phone - but instead do it in Chrome outside of the phone? And the way to do that is to test the underlying mechanism.
Do I have that correct?
There's nothing specific to iOS in what we want to test; it's all related to web requests and headers. The native app happens to be the mechanism that adds the header to the request, but plays no other role; we'd expect the same behavior if we added the request header in some other way.
We want to test a very specific behavior (a header is present on the initial page load) and how the app then behaves.
I guess this is where I'm confused again...
We want to test a very specific behavior (a header is present on the initial page load)
Without manually setting this in Cypress land (which is not how the browser normally behaves) how would this header be present naturally?
What I mean is... if I visit your app: https://app.foo.com is this header present on the first request? No, its not possible to be, because the browser makes a regular GET request for text/html.
On subsequent requests via fetch or XHR, it may attach additional headers which is logic your app controls.
What I'm saying is - that your app is going to do that correctly inside or outside of Cypress, based on other stateful conditions in the browser such as localstorage. If that's the case - you can already test all of this behavior without manipulating the browser in a way it's not possible to do otherwise.
When you provided the iOS use case - I thought this is a great use case because you're trying to simulate the way iOS would work (not a normal Chrome browser). But now you're saying that's not the case, and I'm confused again.
The native app happens to be the mechanism that adds the header to the request.
Yes! That's what I thought you're saying - that something outside the norm is doing this - and you can simulate that behavior by attaching the request. If that's the case, then this goes back to what I just said - that providing additional headers to cy.visit
is exactly what you want.
I think we are on the same page - it's just the way you're phrasing it makes me think otherwise. Because iOS attaches a header (something that a normal browser would not do), you want to use Cypress to simulate this exact scenario which gives you coverage that your app behaves correctly under the iOS environment.
If this is true we don't need to discuss further since this is clear.
@brian-mann yep, you've got it. I know it's a relatively unusual use case, so I appreciate the consideration. Thanks!
I also like to have the option to add an (optional) header in cy.request().
For example, in the 'request-promise' node module I used in the past before I found cypress, I could specify headers with attributes such as Content-Type, Authorization, Cache-Control, Connection, etc & etc. I wrap it up with a function, and can keep calling the same function with ease. Here is an example I have with headers' use:
'use strict';
const rp = require('request-promise');
module.exports = function (method, uri, body) {
let options = {
method: method,
url: uri,
headers: {
'Content-Type': 'application/hal+json',
'Authorization': 'Basic YWRtaW46MTIz',
'Cache-Control': 'no-cache',
'Connection' : 'keep-alive'
},
body: body,
json: true,
resolveWithFullResponse: true,
simple: false // get a rejection only if request failed for technical reasons
};
return rp(options)
.then(function (res) {
return res;
})
.catch(function (err) {
console.log('\trequest-promise call failed: ' + err);
console.log('\tstatusCode: ' + err.statusCode);
console.log('\tmethod: ' + method);
console.log('\turi: ' + uri + '\n');
return err;
});
};
@jianentrinsik you can already add custom headers to cy.request
. It's documented in our docs here: https://docs.cypress.io/api/commands/request.html#Arguments
I have another use case.
We have a site in Azure with SSO enabled, so that as soon as you go to the url, it redirects you to a sign-on page. Even if you've already auth'd, it redirects and then redirects back so you can view the site.
I've made a command to login via oauth2 to get the access_token
, and setting that in the header of a cy.request
allows me to directly hit the api without the redirect. But when I do cy.visit
, it's doing the redirect and messing up cypress. It looks like if I add a Authorization Bearer
header to a GET
request to site root via postman, it doesn't do that redirect.
I tried the SSO recipe and setting the access token in localSession
, but visit doesn't seem to read it out:
Tried both id_token
, bearer
, token
, and access_token
as the storage key.
However, it's still redirecting to login.microsoftonline.com
expecting you to login and cypress doesn't know what to do with that. So I think the only option I have is to set an Authorization
header in visit as well, unless I'm doing this incorrectly.
Here's what it ends up as because it's not reading the localStorage
item properly. It goes from localhost:port
to login.microsoftonline.com
In my case I test app that's integrated into mobile app web view. Mobile app passes Bearer token to authorize the app, this is required only one time to load the page initially, then for all other requests my app adds token header by itself.
Right now I stumbled upon not having the way to add a custom header just for a single page load cy.visit() and can't really continue with writing tests, is there any workaround I can use to add a header to initial page load?
Maybe it's weird, but in our case we want to omit login page. App is configured to automatically login test user when backend finds custom header and that speeds up test a little bit.
For now - we use basic auth header, but it's a standard header and it would be great to use custom one in the future.
I would also love to have this feature built in where I can pass a custom header to all the requests going from the browser to the server.
Hi there. I think I've got a similar use-case to https://github.com/cypress-io/cypress/issues/908#issuecomment-380889611.
We have a Javascript webapp behind an authorization proxy. The first time a user visits the app the proxy kicks them to a corporate sign-in page we don't control. This corporate sign-in page requires 2FA, which is obviously not possible with Cypress. After the user authenticates against the corp sign-in page, they're kicked back to the authorization proxy to make a SAML POST request. After that succeeds, the browser has a few cookies that it sends on subsequent requests. The authorization proxy sees these cookies and lets the requests through.
This process can be bypassed if you provide a Authorization header in every request with a bearer token. In order to test our webapp, I believe we'd need the ability to include a bearer token in every single cy.visit()
call. @brian-mann is this still something that might be put on the roadmap?
Just stumbled across this issue looking for the same answers.
We're currently using RoboHydra to mock a bunch of headers before we run our e2es, but RoboHydra doesn't work with node v9+. Bummer. So i was hoping i could just send the required headers as part of the cy.visit(url, {headers: {}})
method.
Slightly disappointed i've hit a dead end, but reallly hoping this is something you can put on your roadmap!
Another use case is when kerberos authentication is used. Cypress does currently not work with kerberos (https://github.com/cypress-io/cypress/issues/1255), but if we could add custom headers, we could authenticate using a plugin, then add the token to the headers of each request, including visit() requests.
Ironically, you say adding headers is bypassing how browsers normally work, @brian-mann, but in my case, cypress is actually changing the way the browser works, preventing kerberos authentication from working.
Is there any progress on this so far? I also have a use case to where I'd like to set custom headers on cy.visit()
Is there any progress on this so far? I also have a use case to where I'd like to set custom headers on
cy.visit()
There is pending PR here:
https://github.com/cypress-io/cypress/pull/1544
Just chiming in to say I also have a use case for this issue and am looking forward to the PR.
Any update on this? I would find it extremely useful. In the meantime, are there any workarounds?
The code for this is done in cypress-io/cypress#3489, but has yet to be released.
We'll update this issue and reference the changelog when it's released.
Are there any work around solutions for this in the mean time? I have a request rate limiter on the backend and would like to override the limiter by using a custom header during my tests.
Released in 3.2.0
.
Thanks very much for adding this change, it has helped my team hit our admin pages (guarded by SSO), which looks at our headers to determine if a user's kerberos token is valid before allowing access. One thing I have noticed though is that if our servers issue a redirect to Cypress, the headers provided in the "visit" command are not included in the subsequent redirect request. Is this expected behavior? Can there be an option for these headers to be preserved or is there another workaround I can use?
@austinfox see the discussion in #3759 regarding adding an option for headers to be preserved on redirect
@flotwig Awesome, glad to see I'm not the only one with this feature request. Thanks!
@jblaketc are you trying to login to microsoft online ? did you resolve this ? I would like to Test UI with cypress, but had the same issue, not able to visit the app directly.
@Brian
Could we add Bearer token in GET request in Newtwork call. I did it in this way and i gets 400 error message every time. Please help me out in this regards. Code is below added;
var aoth = 'bearer ${' + this.value + '}';
//var aoth=+this.value;
const options = {
method: 'GET',
url: `https://odms.baitussalam.org:8445/api/v1/qurbani-representative`,
authorization{
authorization: aoth
}
// 'authorization':authHeader,
// 'accept': 'application/json',
}};
cy.request(options)
.then((response) => {
expect(response.status).to.equal(200);
});
});
@shahmed-nisum-com The cy.request()
accepts an auth
option (not authorization
). See the docs. https://on.cypress.io/request#Arguments
Also, check out our community chat, it can be helpful for debugging or answering questions on how to use Cypress.
Most helpful comment
I have another use case.
We have a site in Azure with SSO enabled, so that as soon as you go to the url, it redirects you to a sign-on page. Even if you've already auth'd, it redirects and then redirects back so you can view the site.
I've made a command to login via oauth2 to get the
access_token
, and setting that in the header of acy.request
allows me to directly hit the api without the redirect. But when I docy.visit
, it's doing the redirect and messing up cypress. It looks like if I add aAuthorization Bearer
header to aGET
request to site root via postman, it doesn't do that redirect.I tried the SSO recipe and setting the access token in
localSession
, but visit doesn't seem to read it out:Tried both
id_token
,bearer
,token
, andaccess_token
as the storage key.However, it's still redirecting to
login.microsoftonline.com
expecting you to login and cypress doesn't know what to do with that. So I think the only option I have is to set anAuthorization
header in visit as well, unless I'm doing this incorrectly.Here's what it ends up as because it's not reading the
localStorage
item properly. It goes fromlocalhost:port
tologin.microsoftonline.com