Cypress send multiple requests and doesn't allow to login - javascript

I am using Cypress 4.3.0 version, the baseUrl = "https://tenant-demo.somesitedev.net" has been set in cypress.json file. While I am sending the cy.request() command, it is sending multiple request (please see Fig:1) . Also, when I observed the visit command I could see following Original Url, Resolved Url and Redirects. In this scenario, how do I login to the site using cy.request() command.
before(()=>{
cy.visit('/').then(()=>{
cy.get('input[type="hidden"]').invoke('val').then((val)=>{
const token = val;
cy.login(token);
})
})
})
Cypress.Commands.add('login', (token) => {
const username= 'test1.user';
const password= 'somepassword';
const accessToken = localStorage.getItem('tokens');
const cookieValue = document.cookie.split(';');
const configCat = localStorage.getItem('ConfigCat_');
cy.request({
method: 'GET',
url: '/dashboard',
failOnStatusCode: false,
form: true,
body:{
_token: token,
username,
password
},
headers: {
'accept': 'text/html',
'content-type': 'application/x-www-form-urlencoded',
'authorization': `bearer ${accessToken}`,
'ConfigCat_': `${configCat}`,
'cookie': `${cookieValue}`
}
}).then((res)=>{
visitDashboard();
})
})
const visitDashboard = () => {
cy.visit('dashboard')
}
Fig:1
Fig:2

Somehow I managed to find a way to resolve the problem. Since the baseUrl has got some path extension /auth/login, whenever I trigger a cy.request() it was always redirecting back to login page even though the credentials was correct. Also there were two requests in the console.
So the way I did was to send another cy.request() with GET method with body params immediately after the first POST cy.request() with qs parameters. From the request headers I find out there was a 'token' submitted every time when the user login.
If there is another easy way let me know.
Cypress version : 4.4.0
Inside beforeEach(), grab the 'token' value;
beforeEach(() => {
cy.visit('/');
cy.loadTokens();
cy.get('input[name="_token"]').invoke('val').then((val)=>{
const tokenValue = val;
cy.loginRequest(tokenValue);
})
})
Following is the commands.js file:
Cypress.Commands.add('loginRequest', function (tokenValue) {
return cy.request({
method: 'POST',
url: Cypress.config('baseUrl'),
followRedirect: true,
headers: {
'content-type': 'text/html; charset=UTF-8'
},
qs:{
_token: tokenValue,
username: 'your_username',
password:'your_password'
}
}).then(()=>{
return cy.request({
method: 'GET',
url: 'https://tenant-demo.somesitedev.net/dashboard',
followRedirect: false,
headers: {
'content-type': 'text/html; charset=UTF-8'
},
body:{
_token: tokenValue,
username: 'your_username',
password:'your_password'
}
})
})
});

Related

'unsupported_grant_type' when trying to get an access token from Discord API

So I am trying to implement Discord login to my website, but when trying to exchange the code for an access token from https://discord.com/api/oauth2/token I just get { error: 'unsupported_grant_type' }.
My code:
const tokenResponseData = await request('https://discord.com/api/oauth2/token', {
method: 'POST',
data: JSON.stringify({
client_id: config.clientId,
client_secret: config.clientSecret,
code: code,
grant_type: 'authorization_code',
redirect_uri: `http://localhost:3000/api/auth`,
scope: 'identify',
}),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
Been struggling with this for a while now, so any help would be great!
According to this issue on the Discord GitHub, this can happen when your body is incorrectly formatted.
After some digging, I saw this post on SO which has led me to believe that you should use URLSearchParams rather than stringifying your json data.
let params = new URLSearchParams();
params.append('client_id', config.clientId);
params.append('client_secret', config.clientSecret);
params.append('code', code);
params.append('grant_type', 'authorization_code');
params.append('redirect_uri', `http://localhost:3000/api/auth`);
params.append('scope', 'identify');
const tokenResponseData = await request(
'https://discord.com/api/oauth2/token',
{
method: 'POST',
data: params,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
);
Turns out I was doing it all wrong. I needed to have the request parameters formatted like URL queries and sent as a string in the body of the POST request.
const tokenResponseData = await fetch(
`https://discord.com/api/oauth2/token`,
{
method: 'POST',
body: `client_id=${encodeURIComponent(`${config.clientId}`)}&client_secret=${encodeURIComponent(`${config.clientSecret}`)}&grant_type=authorization_code&code=${code}&redirect_uri=${encodeURIComponent(`http://localhost:3000/api/auth`)}`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
);

How to add "Bearer" another route headers after user logged in?

I'm new to programming and I'm currently working on a small project.
I'm trying to implement some authorization using JWT.
I've watched a few videos online and found that most people have the "Bearer" + access token in their headers.
I've gone through a few posts and I found that I needed to add the authorization "Bearer" myself but I'm not quite sure how to get there.
Can I please get some help?
Here are some of my code
Login
if(response){
if(await bcrypt.compare(loginPW, response.password)){
const accessToken = jwt.sign({
email: response.email
},jwtSecret)
res.json({status: 'ok', accessToken: accessToken})
}else{
return res.json({status: 'error', error:'Invalid Credentials'})
}
}
Post request
const result = await fetch('/',{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
loginEmail, loginPassword, reqType
})
}).then((res) => res.json());
just add Authorization header with your token to request
const result = await fetch('/',{
method: 'POST',
headers: {
'Content-Type': 'application/json'
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify({
loginEmail, loginPassword, reqType
})
}).then((res) => res.json());
one possible way is ....on your Post requst result you can store the accessToken in localStorage
const result = await fetch('/',{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
loginEmail, loginPassword, reqType
})
}).then((res) => res.json()).then(({accessToken})=>{
localStorage.setItem('accessToken', accessToken)
});
then retrieve it in all of your requests
const anotherRequst = await fetch('/anything',{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('accessToken')}` // get it from localStorage
},
body: ... /// anything
}).then((res) => res.json());
that's the simplest way
... for more advanced techniques, try to use Axios
and you can simply set the default authorization header in all your requsts
axios.defaults.authorization = localStorage.getItem('accessToken')
then any request you make will have the accessToken in its header
Axios.post(....)
Axios.get(...)
....
You can add 'Authorization' headers within your request just like this
const result = await fetch('/',{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify({
loginEmail, loginPassword, reqType
})
}).then((res) => res.json());
Or if you're dealing with a big project and you have to send the token with every request then you can use Axios which allows you to add common headers with every request using only one line
axios.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;
Docs: https://www.npmjs.com/package/axios

Pass the value from an instance as an argument

I am retrieving the key and trying to pass it in a header while doing GET request, but getting an error.
Utils.js (Here I am retrieving the token)
class Utils {
getKey(){
let key
return cy.request({
method: 'POST',
url: 'http://127.0.0.1:3000/sign-in',
body: {
"password" : "password",
"email" : "email"
},
headers: {
'content-type': 'application/json'
}
}).then((response)=>{
expect(response.body).have.property('access_token')
key = response.body['access_token']
})
return key
}
}
export default Utils
Then I am trying to pass the token in headers
import Utils from '../../fixtures/Utils'
describe("HTTP Example", ()=>{
let utils = new Utils()
it('GET', ()=>{
console.log(utils.getKey())
cy.request({
method: 'GET',
url: 'http://127.0.0.1:3000/users',
headers: {
'Authorization': utils.getKey()
}
}).then((response)=>{
expect(response.body[0]).to.have.property('name', 'Test user')
console.log(response)
})
})
Getting Error: Target cannot be null or undefined.
When you are returning value from a method/function in Cypress, then you have to access the value by chaining only. Below is the snippet which solves your problem. Let me know if it worked or not.
utils.getKey().then(authToken => {
cy.request({
method: 'GET',
url: 'http://127.0.0.1:3000/users',
headers: {
'Authorization': authToken
}
}).then((response) => {
expect(response.body[0]).to.have.property('name', 'Test user')
cy.log(response)
})
})

React - GET and POST requests with axios

In my application I need to GET some data (for which I provide the native authtoken).
In the same event, however, I also need to POST a second token to be consumed by a few endpoints, for external backend api calls.
How do I POST this second token using my working code below using axios?
Should I extend Authorization bearer or simply POST Spotify Token as string data?
How so?
My code:
getData(event) {
const {token} = this.props.spotifyToken
const options = {
url: `${process.env.REACT_APP_WEB_SERVICE_URL}/endpoint`,
method: 'get',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${window.localStorage.authToken}`
}
};
return axios(options)
.then((res) => {
console.log(res.data.data)
})
.catch((error) => { console.log(error); });
};
For an async await applied to your code would look something like this.
async getData(event) {
const {token} = this.props.spotifyToken
let getRes = await axios.get(`${process.env.URL}/endpoint` {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${window.localStorage.authToken}`
}
}
let postRes = await axios.post(`${process.env.URL}/endpoint` {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${window.localStorage.authToken}`
}
}
console.log(getRes.data.data);
console.log(postRes.data.data);
};
In this specific case, where a token is needed to fetch data at backend, I found that passing token at url is more suitable, like so:
#endpoint.route('/endpoint/<select>/<user_id>/<token>', methods=['GET'])
def endpoint(name, user_id, token):
# business logic
and:
const options = {
url: `${process.env.REACT_APP_WEB_SERVICE_URL}/endpoint/${select}/${userId}/${this.props.spotifyToken}`,
method: 'get',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${window.localStorage.authToken}`
}
};
otherwise, backend code would run twice, for POST and GET, which is not desired in my case.

Cypress - How to use response body in another request header

I have a function that will return a token
var accessToken;
function TestToken(){
return cy.request({
method: 'POST',
url: "https://site/token",
headers: {
'Content-Type': 'application/json',
},
body: {
client_id: '1234',
client_secret: '1234',
audience: 'https://site/api/v1/',
grant_type: 'credentials'
}
}).then(response => {
return new Cypress.Promise((resolve, reject) => {
const rbody = (response.body);
var tokenPattern = "(?i)\"access_token\":\\s*\"([^\"]*)\"";
const authToken = rbody.access_token;
accessToken = authToken
cy.log(accessToken);
resolve(accessToken);
return accessToken;
})
});
I call that function in another cypress command, as I need to use the token in the authorization header
Cypress.Commands.add("ResetPwd", () => {
const authToken = TestToken();
cy.log(authToken);
cy.request({
method: 'POST',
url: "https://site/api/v2/users/1234",
headers: {
'Content-Type': 'application/json',
'authorization': 'Bearer '+authToken,
},
body: {
password: 'Test4321',
connection: 'DB',
}
});
});
When I execute the command, I don't see the token being used. Instead I see "authorization": "Bearer [object Object]" in the request.
In the TestToken funtion, I added cy.log(authToken);, to verify it was correctly grabbing the token. The cypress test failed saying
CypressError: cy.then() failed because you are mixing up async and
sync code.
but I could still see the correct token in the console.
Here is the cypress test I am trying to run
describe('Token Test', function() {
before(() => {
cy.ResetPwd();
})
it('Go to website', function() {
cy.visit('https://site/login')
});
});
functions is synchronous so in moment of call it the result will be equal Promise, promise is object and when you try to stringify object (by + operator) you get [object Object].
After some time promise will be resolved and value in authToken variable will be correct.
To fix it is suggest use Cypress.Promise like in example id cypress docs: https://docs.cypress.io/api/utilities/promise.html#Basic-Promise
I am assuming the TestToken() function is a promise and resolves asynchronously causing the authToken to be [ object Object ]
You could use async/await and the code should work fine.
Add async before the arrow function and use await const authToken = TestToken()
I solved this, after lots of work using cy.task:
cy.request({
method: 'POST',
url: '/where/you/retrieve/your/token',
body: {username: user, password: password},
failOnStatusCode: true
}).then((response) => {
cy.task('setToken', response.body);
});
then:
cy.task('getToken').then((token) => {
cy.request({
method: 'GET',
url: '/your/protected/endpoint/here',
headers: {
authorization: `Bearer ${token}`
},
failOnStatusCode: true
}).then((response) => {
expect(response.something).to.eq(something.else);
});
});

Categories