How to pass login parameters in the artillery.io request - artillery

I have started using 'Artillery.io for load testing. Here how to pass authentication parameters like email, password in the load-test.yml, the below throws 401 an unauthorised error while sending the request. Could someone guide me on how to pass authentication parameters like email and password
config:
target: 'https://test-url.com'
phases:
- duration: 70
arrivalRate: 10
defaults:
headers:
Content-Type: application/json
scenarios:
- flow:
- get:
url: /findAMembership
headers:
Content-Type: application/json
- post:
url: /auth
body: '{"username":"test#test.com", "password": "some_testpassword"}'
headers:
Content-Type: application/json

There are something to do with your script that makes it much easier to read, troubleshoot and maybe solve your problem.
First, about the 401 problem, I think your request does not meet the authentication endpoint requirements. Please check the endpoint. (like how to do the auth in a normal way?)
I assume that the endpoint accepts an json request (username and password) and response a Bearer token, you can try this (use json instead of body, and define the key-value):
- post:
url: "/auth"
json:
username: "test#test.com"
password: "some_testpassword"
headers:
Content-Type: "application/json" # no need this, you have specified the default Content-Type
capture:
json: "$.token"
as: "token" # Should have this, read below
And just a note, you sent an auth request and do not capture anything from it, which makes no sense. Remember to capture the response (like Bearer token, etc.) for chaining requests that need the authentication. Read more

If you want to read the users from file, you can specify a payload path, and it's fields. This also allowed you to test scenarios where you have several users, and/or failed attempts.
config:
target: "<TARGET>"
payload:
- path: "./users.csv"
fields:
- "username"
- "password"
order: sequence
skipHeader: true
- path: "./products.csv"
fields:
- "id"
order: sequence
skipHeader: true
phases:
- duration: 60
arrivalRate: 20
scenarios:
- flow:
- post:
url: "/login"
json:
username: "{{ username }}"
password: "{{ password }}"
This way the username and passwords will be read from the specified CSV- file and POSTed to the <TARGET>/login endpoint with the json body object
{
username: <READ USERNAME>
password: <READ PASSWORD>
}
An example CSV- file may look like
username,password
some1#example.com,super1
email#example.com,secret

Related

Can't access cookies in localhost even if httpOnly is false

as I wrote in the title of the question I can't access cookies in localhost.
I can see them from the developer tools as you can see here
and I can see the them from Express too if I print them, like here.
But, if I try to print the document.cookie an empty string is returned (as well as if I try to use other libraries).
I use React for the frontend and Express for backend.
React is hosted both in localhost at this URL: http://local.example.com/ and in remote at https://www.example.com/.
I can access the APIs at https://www.example.com/api.
I have no problems reading cookies in the remote React app while I can't in the local one.
React Fetch:
fetch(FETCH_URL + "/user/login", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: "include",
body: JSON.stringify(data)
})
.then((response) => {
if(response.status === 200){
return response.json()
}else{
setError("Username or password are wrong")
}
})
.then((data) => {
if(data){ // Do stuff }
})
Express Cookies:
res.cookie("username", username, {
maxAge: 1000 * 60 * 60 * 2,
httpOnly: false,
secure: true,
domain: "",
sameSite: "None"
})
res.cookie("token", token, {
maxAge: 1000 * 60 * 60 * 2,
httpOnly: true,
secure: true,
domain: "",
sameSite: "None"
})
I know that I can't read from React the "token" cookie because it has the httpOnly flag true, but I expect to be able to read the "username" one (as I can with the remote React App).
I thought it could maybe happend because of the secure flag setted on true, but googling it seems that it should not affect the apps on localhost.
I took a look at many differen questions:
credentials-include, setting to an empty string the domain of the cookie, redirecting from local.example.com to 127.0.0.1 (hosts.ics), sameSite and secure attributes, fixing the cors policies of the backend, tried to set the domain to .example.com and the result is that I receive the cookies, they're saved in the browser, as I said, I can see them from the developer tools, as well from the backend, but I can't access them with JavaScript while I should be able to.
I figured it out. I actually needed to set-up HTTPS for the local domain.
I followed
this guide (it is for both windows and mac/linux and it was pretty quick too). Once done, my final npm-run script (for windows) is similar to the following: set HTTPS=true&&set SSL_CRT_FILE={PATH}&&set SSL_KEY_FILE={PATH}&&set HOST=local.example.com&&react-scripts start
Hope it helps :D

Artillery.io Login and capture response and cookies before each VU runs

Using Artillery.io load tester I am trying to login each VU Before running the Scenarios flow (I do not want the login requests to be part of the scenario results)
I tried moving the login flow to the beforeScenario but that doesn't seem to run
config:
target: '{{ $processEnvironment.URL }}'
scenarios:
- name: 'Warm Up'
beforeScenario:
flow:
- post:
url: '/login'
json:
username: 'admin'
password: 'password'
capture:
- json: '$.user._id'
as: 'userId'
flow:
- post:
url: '/api/graphql'
json:
- operationName: 'apiRequest'
query: 'query aQuery($userId: ID!) { aQuery: testQuery(userId: $userId) { id name } }'
variables:
userId: '{{ userId }}'
Is there any way to achieve this?
Not sure if you have figured this out but i used the "before" section that comes before the scenario and calls a function to helper function that login
and saves the access_token
before:
flow:
- function: "login"
scenarios:
- name: "Create"
- flow:
- post:
url: ""
headers:
Authorization: "Bearer {{access_token}}"
Before your "scenarios" section, you must use something like this (just an example):
before:
flow:
- log: "Running the Authorization before virtual users run"
- post:
url: "/users/login"
json:
username: "{{user}}"
password: "{{password}}"
capture:
json: "$.data.token"
as: "accessToken"
strict: false
expect:
- statusCode: 200

Axios - extracting http cookies and setting them as authorization headers

I am building authentication for my application and I am using access and refresh tokens.
Upon user login, the API issues 3 things
refresh token
access token string with headers and payload
access token string with signature
These tokens are all jwt tokens.
This article discusses why access tokens should be split.
using express, I send the tokens back to the browser in my controller like so:
res.cookie(
ACCESS_TOKEN_COOKIE_HEADER_PAYLOAD,
headerAndPayload,
COOKIE_OPTIONS,
)
res.cookie(
ACCESS_TOKEN_COOKIE_SIGNATURE,
signature,
COOKIE_OPTIONS_HTTP_ONLY,
)
res.cookie(REFRESH_TOKEN_COOKIE, refreshToken, COOKIE_OPTIONS)
return res.json({ username, uid })
auth.constants.ts
export const COOKIE_OPTIONS: CookieOptions = {
secure: true,
sameSite: 'lax',
}
export const COOKIE_OPTIONS_HTTP_ONLY: CookieOptions = {
httpOnly: true,
secure: true,
sameSite: 'lax',
}
export const ACCESS_TOKEN_COOKIE_HEADER_PAYLOAD = 'access_token_header_payload'
export const ACCESS_TOKEN_COOKIE_SIGNATURE = 'access_token_signature'
export const REFRESH_TOKEN_COOKIE = 'refresh_token'
In the ui (react) I go into Chrome devtools -> application -> storage -> cookeis and I can see that they are updated everytime I login. This is the behavior I want so that's good so far.
Now when I want to send a request to my API to create something (let's say I am creating a new blog post), I want to grab those cookies and pass them as an Authorization Header.
I am following this person's suggestion except I noticed he is using store which I am guessing is some form of state. Since I am not doing that and multiple sources (source 1, source 2) point to the fact that the standard for sending tokens to the API for authentication is using Authorization header, I would like to follow that.
Currently, when I make an API request using axios, I console log the express request object and can see my tokens in cookies like so:
headers: {
host: 'localhost:3001',
connection: 'keep-alive',
'content-length': '0',
accept: 'application/json, text/plain, */*',
'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36',
origin: 'http://localhost:3000',
'sec-fetch-site': 'same-site',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
referer: 'http://localhost:3000/',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'en-US,en;q=0.9',
cookie: 'access_token_header_payload=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InJvc3R5cG9vIiwiaWF0IjoxNTk2ODM0MDIwLCJleHAiOjE1OTY4MzQwODB9; access_token_signature=3pUbxjWgly9xmYSJObOvTgps9qwjOIrHWWE4LPYidmQ; refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InJvc3R5cG9vIiwiaWF0IjoxNTk2ODM0MDIwLCJleHAiOjE1OTc0Mzg4MjB9.IKdRsaTTgAeUfwicLcBpRvw89WgYXy_rCRN5o2BJFqY'
},
but I want to send these cookies as Authorization: Bearer <tokens> instead. How would I do that in axios? Or is what I am doing secure?
this is my axios interceptor
import axios from 'axios'
const service = axios.create({
withCredentials: true,
baseURL: process.env.REACT_APP_API_BASE_URL,
timeout: 5000,
})
// Request interceptors
service.interceptors.request.use(
config => {
return config
},
error => {
return Promise.reject(error)
},
)
// Response interceptors
service.interceptors.response.use(
response => {
console.log('response', response)
return response.data
},
error => {
return Promise.reject({ ...error })
},
)
export default service
HttpOnly means the client script can't access the cookie, as well as you can't read it from document.cookie and pass to axios.
In fact, HttpOnly cookie is more secure than http request headers I think. What you need is parsing the auth cookie in the server side, instead of parsing the request header.
As stated in this response by chinesedfan. The way to authorize your requests to your backend API is through query parsing since your cookie is HttpOnly and can't be accessed by any client.
With express, this can be done by creating a global middleware that sets your authorization header. The following snippet shows how to do this, assuming you are using Bearer <accessToken>.
// global middleware for setting authorization header
app.use((req, res, next) => {
const authHeader = req.cookies.accessToken;
if (authHeader) {
req.headers.authorization = `Bearer ${authHeader}`;
}
next();
});
// initialize passportjs
app.use(passport.initialize())
Add this middleware in your server.js, assuming you named your initializing file this way. This is where you declare your express app variable.
In express, middleware order matters, so add this middleware before you initialize your passport middleware.
In your frontend, you don't have to add anything to axios, just make the request to the backend, and if that request needs authorization, it will be added to you automatically.

Axios not authenticating to API

Ive been trying all day to get data from my Asp.Net Api but with no avail. I login and get an authentication token from the server and store it locally but when I try to perform any action that requires authentication, the server returns a 401 response. Is there something Im doing wrong in my code? When I use a tool like postman, everything works okay but not in my app.
This is my login
try {
response = await API.post(AuthUrl, credentials)
if(response.status >= 200 || response.status <= 299){
let Auth = {
Username: response.data.Username,
Roles: response.data.Roles,
Expires: response.data.Expires,
Token: response.data.Token
};
localStorage.setItem(window.location.host, JSON.stringify(Auth));
}
}
This is my axios encapsulator
export default axios.create({
baseURL: BaseUrl,
responseType: "json",
auth: `Bearer ${localStorage.getItem(window.location.host) == null? "" : JSON.parse(localStorage.getItem(window.location.host)).Token}`
})
and this is how im consuming it
try{
const response = await API.get(getUrl)
setLoading(false);
//........Do something with response
}
This is what is logged at the server
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/2.0 GET https://localhost:44307/api/classes/getclasses/
Microsoft.AspNetCore.Cors.Infrastructure.CorsService:Information: CORS policy execution successful.
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executing endpoint 'SchoolManager.Web.Controllers.ClassesController.GetClasses (SchoolManager.Web)'
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Route matched with {action = "GetClasses", controller = "Classes", page = "", area = ""}. Executing controller action with signature System.Collections.Generic.IEnumerable`1[SchoolManager.Dtos.Tenancy.ClassDto] GetClasses(System.String, System.String) on controller SchoolManager.Web.Controllers.ClassesController (SchoolManager.Web).
Microsoft.AspNetCore.Cors.Infrastructure.CorsService:Information: CORS policy execution successful.
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization failed.
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Microsoft.AspNetCore.Mvc.ChallengeResult:Information: Executing ChallengeResult with authentication schemes (Bearer).
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:Information: AuthenticationScheme: Bearer was challenged.
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action SchoolManager.Web.Controllers.ClassesController.GetClasses (SchoolManager.Web) in 146.8824ms
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executed endpoint 'SchoolManager.Web.Controllers.ClassesController.GetClasses (SchoolManager.Web)'
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 218.2724ms 401
The way the axios.create method is used is not right.
Ref: https://github.com/axios/axios#request-config
The documentation clearly shows that config auth: indicates that HTTP Basic auth should be used, and supplies credentials. For Bearer tokens and such, use Authorization custom headers instead so in your case you can do something like this
export default axios.create({
baseURL: BaseUrl,
responseType: "json",
headers: {'Authorization': "bearer " + JSON.parse(localStorage.getItem(window.location.host)).Token}})

React - fetching from API, how to skip the cors response

I want to make a simple POST request from my React app to my Spring back-end to authenticate the user. What i am doing :
fetch('http://localhost:8080/login', {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
username: this.state.username,
password: this.state.password,
}),
})
.then(response => response.json()).then(resposne => console.log(resposne))
Trying to make this call will get me a SyntaxError: Unexpected end of JSON input. If i log the response i can see that it is a response with a type:cors. I assume i am getting the response from the OPTIONS request that goes out. How can i skip that response? If i check the headers on the response, the header that i want to get, which is the Authorization header, is non-existant. If i go to chrome devtools - network section, and look at the response i am getting, i can see the resposne is as it should be, and i can even see the token returned in the Authorization header. How can i access that header in my React app? Server is properly configured since it returns the token, i just cant get it in the React app.
Thanks!
You should add the Access Control Expose Headers with the Authorization header like so:
Access-Control-Expose-Headers: Authorization
It doesn't seems an error on the token generation. Did you take a look on Response body?
Seems like the response body is not a valid json.

Resources