Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple 'Set-Cookie' headers in one response #1237

Open
JohnnyNiu opened this issue Jun 29, 2017 · 35 comments
Open

Multiple 'Set-Cookie' headers in one response #1237

JohnnyNiu opened this issue Jun 29, 2017 · 35 comments
Assignees
Labels
headers param serialization Issues related to parameter and/or header serialization

Comments

@JohnnyNiu
Copy link

Is it possible to have multiple 'Set-Cookie' headers in one response? As is known there are two ways to set cookies header in the response:
- Having separated headers
- Folding into 1 header and using comma separated

The later way however is deprecated in (RFC6265)[http://www.rfc-editor.org/rfc/rfc6265.txt] and not supported by some latest browsers.

   Origin servers SHOULD NOT fold multiple Set-Cookie header fields into
   a single header field.  The usual mechanism for folding HTTP headers
   fields (i.e., as defined in [RFC2616]) might change the semantics of
   the Set-Cookie header field because the %x2C (",") character is used
   by Set-Cookie in a way that conflicts with such folding.

So that below can be valid:

responses:
   200:
   description: "Response with content"
   headers:
       Set-Cookie:
           type: String
           description: "eg. key1=value1"
       Set-Cookie:
           type: String
           description: "eg. key2=value2"
@ePaul
Copy link
Contributor

ePaul commented Jun 29, 2017

You can of course have multiple headers with the same name in your HTTP response, but it looks like OpenAPI 2.0 has no way of documenting that. The way shown by you doesn't work (a JSON or YAML object can have each key just once, and if you repeat one, most parsers will retain just one of them).

I think it looks like the same with the current RC of OpenAPI 3.0 – while the handling of plural parameters changed (via style instead of collectionFormat), I didn't find anything for "multiple headers of the same name". (I guess that could be handled by adding a new style value here.)

(By the way, I think that Cookies should not be necessary in a Restful API – but the same applies to other headers too.)

@RobDolinMS
Copy link
Contributor

Per @darrelmiller - We have a way to capture this in the request but not in the response.

@JohnnyNiu Is that sufficient for your use case?

@honzajavorek
Copy link

The Set-Cookie header is typically a response header.

@jwalton
Copy link
Contributor

jwalton commented Apr 6, 2018

@RobDolinMS Also, you can't have multiple cookie headers in the request. As per RFC 6265 S5.4:

When the user agent generates an HTTP request, the user agent MUST NOT attach more than one Cookie header field.

@mikejav

This comment was marked as outdated.

@dfornika
Copy link

I'm interested in this issue for the purpose of using the Link header for web linking. There's an example in the RFC that shows multiple links being serialized either in a single Link header or multiple headers. It seems like it would be simpler to parse the links if they were in separate headers.

@njr-11
Copy link

njr-11 commented Jun 24, 2019

I found this issue when looking for a way to represent the ability to have multiple occurrences of the same header. In my case, the number of occurrences is unknown, and so using an array seemed ideal. It almost works, except that explode: true doesn't appear to be working,

    My-Header-Example:
      name: My-Header-Example
      in: header
      description: "Useful description goes here"
      schema:
        type: array
        items:
          type: string
      explode: true

I was expecting it to generate multiple header entries like this,

-H  "My-Header-Example: headerItem1" -H  "My-Header-Example: headerItem2" ...

But instead ends up with a comma-delimited list within a single field,

-H  "My-Header-Example: headerItem1,headerItem2" ...

Would it be possible to fix so that explode: true is honored for header? That might be one way of addressing concerns that others have raised, although they would need to comment on that themselves because you would end up with just a single description field for all of the entries, which in my case is exactly what I want, but might or might not be adequate in the other scenarios documented here.

@darrelmiller
Copy link
Member

@njr-11 How this headers are actually rendered is a function of the tool you are using, not the specification. I would suggest bringing this up with whatever tooling you are using. On the other hand, for HTTP headers that allow duplicate headers, the two forms you show are considered semantically equivalent. See https://tools.ietf.org/html/rfc7230#section-3.2.2

@Foorack
Copy link

Foorack commented Aug 26, 2021

@RobDolinMS Also, you can't have multiple cookie headers in the request. As per RFC 6265 S5.4:

When the user agent generates an HTTP request, the user agent MUST NOT attach more than one Cookie header field.

You definitively can, that is how the entire internet is built up. S5.1 specifies the user is sending the Set-Cookie header. Look in S3.1, there is a clear example of this being allowed if the server is sending it:

bild

@Foorack

This comment was marked as outdated.

@sunxia0
Copy link

sunxia0 commented Sep 23, 2021

@RobDolinMS Also, you can't have multiple cookie headers in the request. As per RFC 6265 S5.4:

When the user agent generates an HTTP request, the user agent MUST NOT attach more than one Cookie header field.

You definitively can, that is how the entire internet is built up. S5.1 specifies the user is sending the Set-Cookie header. Look in S3.1, there is a clear example of this being allowed if the server is sending it:

bild

It seems what RobDolinMS means is that cookie header cannot be attached more than once while set-cookie header can according to the specification you give.

@Foorack
Copy link

Foorack commented Sep 26, 2021

@sunxia0 This PR is regarding the Set-Cookie header though.

@rpsirois
Copy link

This still doesn't appear to be supported (3.0.3)

@karenetheridge
Copy link
Contributor

It appears it is supported (see the use of type: array above), although the implementation you are using may be doing it wrong.

@rpsirois
Copy link

Ah gotcha. I had tried allOf whic didn't work. I'll report back after I try that, thank you.

@razb-viola
Copy link

Still, any plan to fix?

@karenetheridge
Copy link
Contributor

Fix what? Nothing is broken. What behaviour are you expecting?

@razb-viola
Copy link

How to send 2 cookies in one response?

@kbolino
Copy link

kbolino commented Jan 5, 2024

@karenetheridge The Set-Cookie response header is not well represented by type: array because each time the header is repeated it uses a different cookie name, and those names are part of the interface being documented.

@darrelmiller
Copy link
Member

Unfortunately Set-Cookie is an exception to the rule of HTTP headers https://www.rfc-editor.org/rfc/rfc9110.html#section-5.3

I wonder if we could use a schema of type object to describe the returned cookie pairs. e.g.

responses:
  200:
    description: ok
    headers:
      Set-Cookie:
        schema:
          type: object
          properties
            foo:
              type: string
            bar:
              type: string

This doesn't help terribly if you want to be able to describe the cookie attributes. Maybe this is a case for trying to invent an extension.

responses:
  200:
    description: ok
    headers:
      Set-Cookie:
        x-cookies-defn:
         - name: bar
           path: /
           domain: example.com
         - name: foo
           path: /
           domain: example.com

@razb-viola
Copy link

So how to respond with accessToken + csrfToken + refreshToken in one response?

@darrelmiller
Copy link
Member

Something like this? I may be missing something completely. I have never used an API that uses Set-Cookie, so I have no experience with it. Do you have any insight on why people build APIs that rely on Set-Cookie?

responses:
  200:
    description: ok
    headers:
      Set-Cookie:
        x-cookies-defn:
         - name: accessToken
           path: /
           domain: example.com
         - name: csrfToken 
           path: /
           domain: example.com
         - name: refreshToken
           path: /
           domain: example.com

@razb-viola
Copy link

Something like this? I may be missing something completely. I have never used an API that uses Set-Cookie, so I have no experience with it. Do you have any insight on why people build APIs that rely on Set-Cookie?

responses:
  200:
    description: ok
    headers:
      Set-Cookie:
        x-cookies-defn:
         - name: accessToken
           path: /
           domain: example.com
         - name: csrfToken 
           path: /
           domain: example.com
         - name: refreshToken
           path: /
           domain: example.com

Cookies are set to store credentials, especially since they support HttpOnly flag that prevents from JS to access its contents. This is in order to mitigate XSS.

@kbolino
Copy link

kbolino commented Jan 8, 2024

@darrelmiller It's not so much that I want to build an API that relies on cookies, so much as I want to document an API that already does, or has to, use cookies. In particular, cookies are useful for session management, especially since as @razb-viola mentioned, they can be hidden from client-side (JavaScript) code entirely. Obviously, the kind of APIs we're talking about are primarily browser-facing, though they can be server-to-server as well.

Also, @razb-viola, you should not be using a cookie for your (anti-)CSRF token. The whole purpose of the token is to indicate that the browser initiated the request from a legitimate origin. Since cookies are sent by the browser to the server based upon the destination and not the origin, they are useless at preventing cross-site request forgery. The CSRF token can be sent back to the server in just about any other way, though (query parameter, header, inline form parameter, etc.).

@razb-viola
Copy link

razb-viola commented Jan 8, 2024

@kbolino The CSRF token will be stored in a cookie - without HttpOnly flag - and then be sent in the header after read from JS. The HttpOnly flag is only for the accessToken itself (and refresh token). While reading it explicitly from the cookie using JS ensures no one trying to forge this request from an email or something.

@kbolino
Copy link

kbolino commented Jan 8, 2024

@razb-viola As long as the CSRF token is externally verifiable (e.g. by the server's state) then it shouldn't be a problem; you should still not trust cookie value == header value alone due to the risk of subdomains being able to overwrite parent domain cookies. It's probably better to return the CSRF token outside of cookies, since it can be misleading (as I was misled) even if done properly.

@rafalkrupinski
Copy link

Would it make sense to add cookies to the OAS response object?

@razb-viola
Copy link

I check in my server using hmac crypto mechanism - the user provides his CSRF token in the headers (after reading from the cookie explicitly), then the server takes the actual accessToken and run hmac on it, the result should be equal to the user provided CSRF token in the header.

@razb-viola
Copy link

Would it make sense to add cookies to the OAS response object?

Why not? (Response headers to be precise*)

@rafalkrupinski
Copy link

Would it make sense to add cookies to the OAS response object?

Why not? (Response headers to be precise*)

Not response headers, response object :)

Set-Cookie header is a special case and could use a separate object in OpenAPI document, just like request cookies have. It would solve problems reported earlier in this discussion.

@handrews handrews added the param serialization Issues related to parameter and/or header serialization label Jan 29, 2024
@handrews
Copy link
Contributor

@kbolino

@karenetheridge The Set-Cookie response header is not well represented by type: array because each time the header is repeated it uses a different cookie name, and those names are part of the interface being documented.

Why not use a oneOf or anyOf to ensure that any of the different names (and any additional constraints that go with them) are allowed? I assume that you'd use pattern to document the specific names. But that way you essentially have an order-independent list of possible cookies.

It's certainly not the most elegant solution, but it's accurate (unless the cookies are always sent in a set order, in which case prefixItems solves the problem without anyOf).

@rafalkrupinski
Copy link

Why not use a oneOf or anyOf

Could you write an example?

@handrews
Copy link
Contributor

handrews commented May 2, 2024

@rafalkrupinski yes, but I'm realizing that there may be more to this than I initially thought. How much do you need to model the contents of each Set-Cookie value? I see that there's a name=value but then there are all sorts of other post-; parameters. Is the problem modeling those things, or is it fine to just use type: string, pattern: whatever as long as you can specify a separate pattern for each expected cookie?

@rafalkrupinski
Copy link

I thought cookies were identified with more than just their names, like domain, path or secure-only (they are for storage purposes), but the server SHOULD* only send one Set-Header with a given cookie name...

So response cookies could be modelled with JSON Schema as an object, with properties following a specific schema, only the encoding is non-standard.

The example (the first one)

responses:
  200:
    description: ok
    headers:
      Set-Cookie:
        schema:
          type: object
          properties
            foo:
              type: string
            bar:
              type: string

could be used, if it was specified by the OAS.

Full definition for a cookie header value would look more like this:

    headers:
      Set-Cookie:
        schema:
          type: object
          properties
            foo:
              type: object
              properties:
                value:
                  type: $type
                domain:
                   type: string
                secure-only:
                   ...

But, except the value property the schema is constant, so the first example should be sufficient to model cookies.

This is really a special case and re-purposing of JSON Schema, since the type doesn't even describe the header value, rather the cookie value, which is only a part of the header value, and we don't even model a JSON Object here.

* Severs should send only one cookie with a given name - that's what the RFC says. SHOULD means that there might be exceptions 🤦

@handrews
Copy link
Contributor

handrews commented May 2, 2024

@rafalkrupinski yeah, this falls under "more complex than I had been thinking" - what is needed is an agreed-upon way of modeling the header contents with JSON Schema (which we don't have), at which point we could model potential values using an array. The anyOf/oneOf trick is just a way to use items (or contains in OAS 3.1) to define an order-independent list. If the order is significant, than prefixItems can be used directly (in OAS 3.1, at least).

I started a discussion on modeling headers in the OAS 4 "Moonwalk" SIG:

and I already listed this issue as one of the motivations. If we can manage to model headers like that, we will backport it to 3.x if possible. Since it's not yet clear that that approach to modeling headers will be accepted into Moonwalk, I'm leaving issues like this open here for now. If it moves farther along, I might close this and others in favor of the Moonwalk discussion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
headers param serialization Issues related to parameter and/or header serialization
Projects
None yet
Development

No branches or pull requests