I am trying to access audio snippets within an application hosted on Azure blob storage during a cypress test and I keep getting
"403 Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature."
The app works fine when I use it manually through browser, but fails during Cypress run.
this is my cypress config
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
baseUrl: 'https://tts-sa-dev.azurewebsites.net',
setupNodeEvents(on, config) {
// implement node event listeners here
},
},
});
I use a basic auth before every test suite
beforeEach(() => cy.authenticate('basic'));
that looks like this
Cypress.Commands.add('authenticate', (authType: 'basic') => {
if (authType === 'basic') {
const username = 'XXXX';
const password = 'XXXX';
const authHeader = 'Basic ' + btoa(username + ':' + password);
// Intercept all requests and add the Authorization header
cy.intercept('**', (req) => {
req.headers['Authorization'] = authHeader;
});
}
});
Im not sure if the auth header could be somehow interacting with the cors signature from azure and there seems to be nobody else on stackoverflow dealing with this, which i find strange.
I've simplified the test to bare bones, but it fails to retrieve any data after calling the endpoint. I realize cypress is probably modifying the request header but I fail to log any difference when intercepting it.
Anybody has a similar problem?
I have compared request headers and there were some discrepancies: There was an "origin" header missing, "accept" was an array of options and "fetch-mode" was non-cors, but adjusting them made no difference.
cy.intercept('*', (req) => {
console.log('Request Headers at Intercept:', req.headers);
req.headers['Accept'] = 'application/json';
req.headers['Origin'] = 'https://xxxx.azurewebsites.net';
req.headers['Sec-Fetch-Mode'] = 'cors';
req.headers['Accept-Language'] = 'cs,en;q=0.9';
});
I think you need header names to start lower case.
If you look at Passing a request to the next request handler, it uses lower-case authorization
but you have upper-case Authorization
.
cy.intercept('http://api.company.com/', { middleware: true }, (req) => { req.headers['authorization'] = `token ${token}` })
If I try a sample test with req.headers['Origin']
it passes (but it shouldn't)
cy.intercept(/todos/, (req) => {
const defaultHeaders = Cypress._.cloneDeep(req.headers)
console.log('Request Headers at Intercept:', defaultHeaders)
req.headers['Accept'] = 'application/json';
req.headers['Origin'] = 'https://xxxx.azurewebsites.net';
req.headers['Sec-Fetch-Mode'] = 'cors';
req.headers['Accept-Language'] = 'cs,en;q=0.9';
})
.as('request-headers')
cy.window().then(win => {
win.fetch('https://jsonplaceholder.typicode.com/todos/1')
})
cy.wait('@request-headers').then(({request,response}) => {
console.log(request.headers)
expect(request.headers['Origin']).to.eq('https://xxxx.azurewebsites.net')
})
In the final console.log()
there are upper-case and lower-case headers for "Origin/origin" keys, which indicates the header key is case sensitive.
Origin: "https://xxxx.azurewebsites.net",
origin: "http://localhost:34272"
If I switch to lower-case req.headers['origin']
it now fails with a CORS error, which I think is correct behavior.
From that I would conclude the correct format for setting header keys is to use lower-case strings.
Not sure this is the complete answer, but should get you one step further.
The cy.visit() - Add basic auth headers documentation shows how to use basic auth in the visit command.
cy.visit('https://wile:coyote@tts-sa-dev.azurewebsites.net/')
or
cy.visit('https://tts-sa-dev.azurewebsites.net/', {
auth: {
username: 'wile',
password: 'coyote',
},
})
This should make a single test test authenticated, push to beforeEach()
to do it for each test.
Wrap in a cy.session to cache the login credentials.
Cypress.Commands.add('login', (username, password) => {
cy.session([username, password], () => {
cy.visit('https://wile:coyote@tts-sa-dev.azurewebsites.net/')
})
})
beforeEach(() => {
cy.login('wile', 'coyote')
})