JAX-RS, RestEasy: No session cookie - jax-rs

Folks,
Used to be that when you hit a servlet/jsp, the app server would automatically start a session. It would put a session cookie in the first dynamic response that would get tracked throughout.
I have a rest backend and I notice that no session cookies are being traded. So I manually add code to send the JSESSIONID cookie:
#Context
private HttpServletRequest httpRequest;
// ...
#GET
#Path( "/{rcpGuid}" )
public Response myMethod( ... )
{
final HttpSession session = httpRequest.getSession();
final String sSessionId = session.getId();
...
return Response.status( Response.Status.SEE_OTHER ).
location( redirectUrl ).cookie( new NewCookie( "JSESSIONID", sSessionId ) );
}
Now this is causing 2 copies of the JSESSIONID cookie being returned where before there was no Set-Cookie header. This is what I see now in my browser's inspector:
Set-Cookie:JSESSIONID=sdm-Q1P6pRoQbKd4-9cJylGb; Path=/nn, JSESSIONID=sdm-Q1P6pRoQbKd4-9cJylGb; Version=1
I don't care as long as this would work. But unfortunately, when my browser requests the URL being redirected to (notice that the response is "SEE_OTHER"), that request does not bear the session id. This causing my app to not function right.
Any insights?

Used to be that when you hit a servlet/jsp, the app server would automatically start a session. It would put a session cookie in the first dynamic response that would get tracked throughout.
The app server doesn't create a session until httpRequest.getSession() is called.
I have a rest backend and I notice that no session cookies are being traded. So I manually add code to send the JSESSIONID cookie:
In your example, you call getSession() and create a separate jsessionid cookie. That would explain why you have two cookies. If you do neither, you wont have a jessionid at all.

If it is a REST back-end, so you should not have JSESSIONID cookie and keep your back-end endpoints as Stateless.
All informations required to get a result from one of your web service should be contained in the request to the web service. A web service is idempotent.

Related

How to handle JWT refreshing on server side (Next.js or any other)

I have two cookies being stored: JWT and refresh token, both are httponly; path=/, so they are send on all requests, including the API (which doesn't use Bearer, but instead reads the JWT directly from the cookies).
The implementation is as usual, the JWT is short lived, and the refresh token is used to get a new one. The refresh token is rotating and after used is invalidated.
On the client, refreshing the token is no issue. When a 401 is returned by the API a call is made to /auth/refresh-token and the request is retried.
On the server however, (e.g. on getServerSideProps) it seems to be quite difficult to refresh the JWT. What I have attempted is to create a custom server and a middleware that checks when a JWT is expired and attempts to refresh it.
I can think of two issues with that, first is that the custom server is called on every resource, that includes all json, js, static files, etc... that Next.js serves. When two requests are made with the same tokens (I can handle this when making API calls, but Next.js also sends requests to the server and I cannot control those):
1. Two requests with expired JWT are sent to the server
2. The back-end receives the requests and on both determines it needs to refresh the token
3. Eventually one of the requests will complete, invalidating the refresh-token
4. The other request now has an invalidated refresh token and cannot get a new JWT
Second issue, what if the user doesn't receive the response, scenario:
1. A request with an expired JWT is sent
2. The back-end refreshes it and sets the new cookies
3. The back-end then has to read lots of data from a database which takes a few seconds
4. User closes the page before receiving the response
5. At this point the user has an invalidated refresh token and an expired JWT because the response with the new cookies was never received
How are these cases usually handled? It seems like it would be a common issue with rotating refresh tokens, but I couldn't find anything useful on this online.
You can follow this practice.
Save refresh token in the http-only cookie
No need to save JWT in the cookie for better security and keep it in the response of refresh token as well as login endpoint.
Save JWT expiry in a normal cookie
Call refresh token endpoint to get new JWT when expiry token is not present or getting 401 error.
In getServerSideProps also you can call the refresh token endpoint always as you don't need to persist JWT anywhere.
You may need to get the cookie from req.headers.cookie and pass it in the header when you are calling the refresh token endpoint from the server.
//inside getServerSideProps
const browserCookie = req.headers.cookie;
const refreshJWTTokenResponse: any = await refreshJWTToken(browserCookie);
//service call
export async function refreshJWTToken(refreshTokenCookie: any): Promise<{}> {
let headers = new Headers();
headers.append("Cookie",`${refreshTokenCookie}`);
const options = {
method: 'POST',
headers: headers,
credentials:'include',
};
...
}

Cloudflare Worker redirect stripping auth headers

I set up a Cloudflare worker to redirect to our API gateway since we don't have control of the DNS and can't just set up a CNAME. The redirect works and it passes along the body and all the headers except Authorization. It receives it, and when I look at the worker console it lists it as redacted. It also redacts the user_key param I'm passing but it passes that through.
const base = 'https://myurl.com'
const statusCode = 308;
addEventListener("fetch", event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const url = new URL(request.url);
const { pathname, search } = url;
const destinationURL = base + pathname + search;
return Response.redirect(destinationURL, statusCode);
}
First, note that the redactions you are seeing are purely for display in the workers console. This is a feature to protect sensitive secrets from being logged, but it doesn't affect the content of any live request.
Now, with regard to what your Worker is actually doing:
This worker returns a 308 redirect response back to the client. It is then up to the client to follow the redirect, sending the same request to the new URL.
It is the client, then, that decides whether to send the Authorization header to the new location -- the behavior is NOT controlled by Cloudflare Workers. As it turns out, many clients intentionally drop the Authorization header when following redirects to a different domain name. For example, the Go HTTP client library does this, and node-fetch recently started doing this as well. (I happen to disagree with this change, for reasons I explained in a comment.)
If the client is a web browser, then the behavior is complicated. If the Authorization header was added to the request as part of HTTP basic auth (i.e. the user was prompted by the browser for a username and password), then the header will be removed when following the redirect. However, if the Authorization header was provided by client-side JavaScript code when it called fetch(), then the header will be kept through the redirect.
Probably the best way to solve this is: Don't use a 3xx redirect. Instead, have the Worker directly forward the request to the new URL. That is, instead of this:
return Response.redirect(destinationURL, statusCode);
Try this:
return fetch(destinationURL, request);
With this code, the client will not receive a redirect. Instead, the Worker will directly forward the request to the new URL, and then forward the response back to the client. The Worker acts as a middleman proxy in this case. From the client's point of view, no forwarding took place, the original URL simply handled the request.

Store selenium request in jmeter to use in the next request

I am trying to use the login from selenium to use in the next http request in jmeter. I currently have the below
My selenium script works perfectly and logs me into my website as per the below
WDS.sampleResult.sampleStart()
WDS.browser.get('https://www.testwebsite.com')
WDS.browser.findElement(org.openqa.selenium.By.linkText("Login")).click();
WDS.browser.findElement(org.openqa.selenium.By.id("username")).sendKeys("myusername");
WDS.browser.findElement(org.openqa.selenium.By.id("password")).sendKeys("mypassword");
WDS.browser.findElement(org.openqa.selenium.By.xpath("//button[#type='submit']")).click();
java.lang.Thread.sleep(5000)
WDS.browser.findElement(org.openqa.selenium.By.xpath("//*[contains(text(),'Skip for now')]")).click();
var cookies = WDS.browser.manage().getCookies()
java.lang.Thread.sleep(5000)
WDS.sampleResult.sampleEnd()
What i want to do is keep this session alive and then on the next request use something like this
GET - https://test/anotherpage.com
so i want it to recognise im still logged in. How can i keep the session alive so my jmeter http request can work?
In your WebDriver Sampler you need to store the cookies object into JMeter Variables like:
WDS.vars.putObject('cookies', cookies)
Add HTTP Cookie Manager to your Test Plan
Add JSR223 PreProcessor as a child of the HTTP Request sampler where you need to get the authentication context and put the following code into "Script" area:
def cookies = vars.getObject('cookies')
log.info('cookies=' + cookies)
cookies.collect { cookie ->
new org.apache.jmeter.protocol.http.control.Cookie(cookie.getName(),
cookie.getValue(),
cookie.getDomain(),
cookie.getPath(),
cookie.isSecure(),
cookie.getExpiry().getTime())
}.each { cookie -> sampler.getCookieManager().add(cookie) }
This way you can copy the cookies from the browser to HTTP Request sampler so the request will be authenticated
More information: Modifying Cookies in JMeter with Groovy
Using cookies in selenium, which it looks like yuou've fetched all of them:
https://www.selenium.dev/documentation/en/support_packages/working_with_cookies/
YHou should be able to store those cookies, which should be enough to preserve your session.
https://www.blazemeter.com/blog/using-http-cookie-manager-jmeter
To save cookies as variables, define the property
"CookieManager.save.cookies=true". The names of the cookies contain
the prefix "COOKIE_" before they are stored (this avoids accidental
corruption of local variables). To revert to the original behavior,
define the property "CookieManager.name.prefix= " (with one or more
spaces). If enabled, the value of a cookie with the name TEST can be
referred to as ${COOKIE_TEST}.
That being said, you probably don't need to use selenium (and launch a full browser) to do the login, you could probably fire a simple http request to post the login form. This would be less over head
https://guide.blazemeter.com/hc/en-us/articles/207421705-How-to-use-JMeter-for-Login-Authentication-How-to-use-JMeter-for-Login-Authentication

HttpClient 4.x: how to retain cookies across multiple requests?

I need to use Apache HttpClient (4.x) to make 3 consecutive web calls and essentially log me into my app programmatically:
An HTTP GET to a login page (http://myapp01.example.com)
The server will respond to this GET with a response cookie "JSESSIONID"
An HTTP POST to the same page (using the same JSESSIONID as a request cookie)
The server now authenticates me and validates the JSESSIONID.
An HTTP GET to a different page under the same domain (http://myapp01.example.com/fizz), again using the same JSESSIONID as a request cookie
The first GET's response will contain a cookie named JSESSIONID. The POST will then log me in to the server (sending username and password data in the POST request body). This POST will also send (Set-Cookie) the JSESSIONID cookie received from the first GET. If my logins are successful, the JSESSIONID will now be authenticated, and I am logged in. I can then make the 2nd GET call (still using the same JSESSIONID) to /fizz which is ordinarily an authenticated URL.
Can this be done in HttpClient 4? I see there is a method HttpClient.getCookieStore(). but this seems to only store cookies per GET/POST/PUT/etc.
Any ideas as to how I can get this holding cookies across multiple requests, such that any cookies returned by the server are then added to subsequent requests?
Apache HttpClient takes care of that automatically (since version 2)

JMeter Cookie Manager

I am using JMeter to make a simple login test on my website. I have a Thread Group which contains a Cookie Manager, an HTTP request to the root page, then an HTTP Post which posts a username and password, and then a Results view. When I run the test and view the response, the results show that the first HTTP request correctly calls set-cookie with the JSessionID, but the second HTTP request that POSTS credentials has no cookies - shouldn't this cookie be posted from the Cookie Manager? Thanks.
In most apps that I've tested, I don't see the actual cookie information after the initial request.
If your session isn't being maintained, there are a few quick things to check:
1. there are no hard coded JSessionID values in the HTTP Requests;
2. try different cookie manager types - different applications expect different settings.