Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Authorization header to cy.visit() #908

Closed
kerray opened this Issue Nov 14, 2017 · 37 comments

Comments

@kerray
Copy link

kerray commented Nov 14, 2017

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!

@brian-mann

This comment has been minimized.

Copy link
Member

brian-mann commented Nov 14, 2017

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?

@dwelle

This comment has been minimized.

Copy link

dwelle commented Nov 15, 2017

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.

@kerray

This comment has been minimized.

Copy link
Author

kerray commented Nov 15, 2017

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 :)

@brian-mann

This comment has been minimized.

Copy link
Member

brian-mann commented Nov 15, 2017

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.

@kerray

This comment has been minimized.

Copy link
Author

kerray commented Nov 16, 2017

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.

@brian-mann

This comment has been minimized.

Copy link
Member

brian-mann commented Nov 16, 2017

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.

@gregor-tb

This comment has been minimized.

Copy link

gregor-tb commented Dec 13, 2017

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.

@brian-mann

This comment has been minimized.

Copy link
Member

brian-mann commented Dec 13, 2017

@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.

@gregor-tb

This comment has been minimized.

Copy link

gregor-tb commented Dec 13, 2017

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. ;)

@brian-mann

This comment has been minimized.

Copy link
Member

brian-mann commented Dec 13, 2017

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.

@JPHamlett1993

This comment has been minimized.

Copy link

JPHamlett1993 commented Jan 3, 2018

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.

@davidbarna

This comment has been minimized.

Copy link
Contributor

davidbarna commented Mar 6, 2018

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!

@brian-mann

This comment has been minimized.

Copy link
Member

brian-mann commented Mar 6, 2018

@davidbarna Would you also need those headers on all subsequent requests? Not just the visit?

@JPHamlett1993

This comment has been minimized.

Copy link

JPHamlett1993 commented Mar 6, 2018

Every subsequent request would be nice, or at least the option.

@arsduo

This comment has been minimized.

Copy link

arsduo commented Mar 8, 2018

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.

@brian-mann

This comment has been minimized.

Copy link
Member

brian-mann commented Mar 8, 2018

@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.

@arsduo

This comment has been minimized.

Copy link

arsduo commented Mar 8, 2018

@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:

  1. Request comes in with no header -> login page
  2. Request comes in with header -> user is logged in
  3. Subsequent request (without header) after request with header -> user is still logged in

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.

@brian-mann

This comment has been minimized.

Copy link
Member

brian-mann commented Mar 8, 2018

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?

@arsduo

This comment has been minimized.

Copy link

arsduo commented Mar 8, 2018

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.

@brian-mann

This comment has been minimized.

Copy link
Member

brian-mann commented Mar 8, 2018

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.

@arsduo

This comment has been minimized.

Copy link

arsduo commented Mar 9, 2018

@brian-mann yep, you've got it. I know it's a relatively unusual use case, so I appreciate the consideration. Thanks!

@jianentrinsik

This comment has been minimized.

Copy link

jianentrinsik commented Mar 28, 2018

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;
        });
};
@brian-mann

This comment has been minimized.

Copy link
Member

brian-mann commented Apr 9, 2018

@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

@jblaketc

This comment has been minimized.

Copy link

jblaketc commented Apr 12, 2018

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.

image

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

redirect

@vedmant

This comment has been minimized.

Copy link

vedmant commented Jun 11, 2018

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?

@karolzawadzki

This comment has been minimized.

Copy link

karolzawadzki commented Aug 13, 2018

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.

@jatinkapoor

This comment has been minimized.

Copy link

jatinkapoor commented Aug 21, 2018

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.

@hobbeswalsh

This comment has been minimized.

Copy link

hobbeswalsh commented Aug 29, 2018

Hi there. I think I've got a similar use-case to #908 (comment).

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?

@KingScooty

This comment has been minimized.

Copy link

KingScooty commented Aug 31, 2018

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!

@Gachapen

This comment has been minimized.

Copy link

Gachapen commented Sep 18, 2018

Another use case is when kerberos authentication is used. Cypress does currently not work with kerberos (#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.

@kamilcwork

This comment has been minimized.

Copy link

kamilcwork commented Sep 20, 2018

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()

@hkusanic

This comment has been minimized.

Copy link

hkusanic commented Sep 26, 2018

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:
#1544

@jennifer-shehane jennifer-shehane changed the title Adding Authorization header to requests Adding Authorization header to cy.visit() Jan 28, 2019

@abu-wizata

This comment has been minimized.

Copy link

abu-wizata commented Feb 11, 2019

Just chiming in to say I also have a use case for this issue and am looking forward to the PR.

@asos-oliverwilson

This comment has been minimized.

Copy link

asos-oliverwilson commented Feb 21, 2019

Any update on this? I would find it extremely useful. In the meantime, are there any workarounds?

@cypress-bot

This comment has been minimized.

Copy link

cypress-bot bot commented Feb 27, 2019

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.

@soupman99

This comment has been minimized.

Copy link

soupman99 commented Mar 4, 2019

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.

@cypress-bot

This comment has been minimized.

Copy link

cypress-bot bot commented Mar 15, 2019

Released in 3.2.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.