eykrehbein / strest

⚡️ CI-ready tests for REST APIs configured in YAML
MIT License
1.74k stars 59 forks source link

How to use a cookie from response in the next request? #147

Open WimmerAlex opened 4 years ago

WimmerAlex commented 4 years ago

This is a rather general question for stRest authentication.

Authentication is already covered by your documentation:

requests:
  login: # will return { authenticated: true }
    ...
  authNeeded:
    request:
    ...
      headers:
      - name: Authorization
        value: Bearer <$ login.content.authenticated $>  # It's possible to use the status code, headers, and status text from previous calls.

That is exactly what I need, but (for me) it is only the second step.

The way I need to authenticate and where I would need your help to manage this looks like this:

Step 1:
reqest to the webserver -> webserver authenticates the session and returns a cookie

Step 2: <- this is where I fail
request user data with the cookie -> webserver returns the user data AND a token.

Step 3: <- your documented auth approach (works fine)
Further requests can be done with the approach you have already documented. The returned token from step 2 is used.

What I am missing now is how session-handling works with stRest, how to pass a cookie to the server. Do I need to transfer the cookie in chaining or is there a way to use the cookie cache? I've tested several approaches but unfortunately had no success yet.

In the code example below I am listing some of my most promising approaches:

requests:
  authenticationRequest: # initial request that authenticates the session and returns a cookie 
    request:
      url: <$ base_url $>/foo/login 
      method: POST              
      postData:        
        mimeType: application/json
        text: 
          { 
            "Username":"username",
            "Password":"password"
          }         

  userDataRequest1: # approach 1 -> did not work
    request:
      url: <$ base_url $>/foo/userdata
      method: GET
      headers: 
        - name: Cookie
          value: <value of 'Set-Cookie' in response of authenticationRequest>

  #-------------------------------------------

  userDataRequest2: # approach 2 -> did not work
    request:
      url: <$ base_url $>/foo/userdata
      method: GET
    cookies: 
      - name: <cookie name>
        value: <cookie value>
        path: "/"
        secure: true

  #-------------------------------------------

  userDataRequest3: # approach 3 -> did not work
    request:
      url: <$ base_url $>/foo/userdata
      method: GET
    headers:
     - name: <cookie name>
       value: <cookie value>

  furherDataRequest:
    request:
      url: <$ base_url $>/foo/bar
      method: GET    
      headers:
        - name: Authorization
          value: Bearer <$ userDataRequest.content.token $>
    validate:
    - jsonpath: content

The response to the first request (authenticationRequest) looks like this:

{
  "base_url": "https://somewebsite.net/api",
  "loginRequest": {
    "status": 201,
    "statusText": "Created",
    "headers": {
      "cache-control": "private,private, no-cache, no-store, must-revalidate",
      "location": "https://somewebsite.net//user/id",
      "vary": "Accept",
      "server": "Microsoft-IIS/10.0",
      "set-cookie": [
        "ss-id=EXdBqX1EftWlIdbp3bDE; path=/; secure",
        "ss-pid=E1L7iXV2OkAr285Chd1B; expires=Fri, 30-Mar-2040 08:53:50 GMT; path=/; secure",
        "X-UAId=179; expires=Fri, 30-Mar-2040 08:53:58 GMT; path=/",
        "ARRAffinity=08de20d64c245f8af5775d6657a76ccfd5v4d42f70f6ba8f981ce3670547e89f;Path=/;HttpOnly;Domain=somewebsite.net",
        "ARRAffinity=196fe2fe6a8ae3ed160790b414ecb9f7f16rfaf6e05a51f227672a762e5e300e;Path=/;HttpOnly;Domain=somewebsite.net"
      ],
      "x-powered-by": "ServiceStack/5.50 Net45/Windows, ASP.NET, ARR/3.0",
      "access-control-allow-origin": "https://somewebsite.net",
      "access-control-allow-methods": "GET, POST, PATCH, PUT, DELETE, OPTIONS",
      "access-control-allow-headers": "Content-Type,withCredentials,Authorization",
      "access-control-allow-credentials": "true",
      "x-aspnet-version": "4.0.30319",
      "date": "Mon, 30 Mar 2020 08:53:58 GMT",
      "connection": "close",
      "content-length": "0"
    },
    "content": ""
  }

Thanks in advance!

WimmerAlex commented 4 years ago

Well, I found a solution by myself, not perfect but I am glad that I can use strest now.

For anyone who might be interersted in the solution:

The problem was the further usage of set-cookie.

Strest-Client allows to use values from fields of the previous request like this:

loginRequest.headers.set-cookie[0]

That is a valid JSONPath and would show following result:

"ss-id=EXdBqX1EftWlIdbp3bDE; path=/; secure"

Unfortunately this does not work with strest. The response is saved in HAR format and can also be accessed with JSON Syntax. I guess Nunjucks is used for JSON parsing and adds some other behaviour. Every JSONPath without a "-" is valid and returns the correct result.
In my case "set-cookie" seems to lead to a math operation where cookie is substracted from set (set - cookie). The result is "NaN" (not a number).

A valid JSONPath to avoid this behaviour looks like this:

loginRequest.headers["set-cookie"][0]

Now that I've got the value, I need to cut the resulting string to pass it in the next request with just "name=value" without "path=/; secure".

The only way I found to accomplish this task is to use Nunjucks' 'slice()'.

With 'slice(x, y)' it is possible to cut a string starting from index x to index y. While the length of the cookie value was different over several approaches, the string ending (path=/; secure) remained the same. This approach leads to:

loginRequest.headers["set-cookie"][0].slice(0, -15) // slice(0, string.length-15)

"ss-id=EXdBqX1EftWlIdbp3bDE; path=/; secure" 
leads to:
"ss-id=EXdBqX1EftWlIdbp3bDE;"

The complete request now looks like this:

userDataRequest:
    request:
      url: <$ base_url $>/foo/
      method: GET
      headers: 
        - name: cookie
          value: <$ loginRequest.headers["set-cookie"][0].slice(0, -15) $> <$ loginRequest.headers["set-cookie"][1].slice(0, -54) $> <$ loginRequest.headers["set-cookie"][2].slice(0, -46) $> <$ loginRequest.headers["set-cookie"][3].slice(0, -57) $>

leads to:

userDataRequest:
    request:
      url: <$ base_url $>/foo/
      method: GET
      headers: 
        - name: cookie
          value: ss-id=EXdBqX1EftWlIdbp3bDE; ss-pid=E1L7iXV2OkAr285Chd1B; X-UAId=179; ARRAffinity=08de20d64c245f8af5775d6657a76ccfd5v4d42f70f6ba8f981ce3670547e89f;