419

I read that sending cookies with cURL works, but not for me.

I have a REST endpoint like this:

class LoginResource(restful.Resource):
    def get(self):
        print(session)
        if 'USER_TOKEN' in session:
            return 'OK'
        return 'not authorized', 401

When I try to access the endpoint, it refuses:

curl -v -b ~/Downloads/cookies.txt -c ~/Downloads/cookies.txt http://127.0.0.1:5000/
* About to connect() to 127.0.0.1 port 5000 (#0)
*   Trying 127.0.0.1...
* connected
* Connected to 127.0.0.1 (127.0.0.1) port 5000 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.27.0
> Host: 127.0.0.1:5000
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 401 UNAUTHORIZED
< Content-Type: application/json
< Content-Length: 16
< Server: Werkzeug/0.8.3 Python/2.7.2
< Date: Sun, 14 Apr 2013 04:45:45 GMT
<
* Closing connection #0
"not authorized"%

Where my ~/Downloads/cookies.txt is:

cat ~/Downloads/cookies.txt
USER_TOKEN=in

and the server receives nothing:

127.0.0.1 - - [13/Apr/2013 21:43:52] "GET / HTTP/1.1" 401 -
127.0.0.1 - - [13/Apr/2013 21:45:30] "GET / HTTP/1.1" 401 -
<SecureCookieSession {}>
<SecureCookieSession {}>
127.0.0.1 - - [13/Apr/2013 21:45:45] "GET / HTTP/1.1" 401 -

What is it that I am missing?

Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
daydreamer
  • 87,243
  • 191
  • 450
  • 722
  • 1
    I think adding the `-c` option tells `curl` to use your cookie file as the output cookie jar, which might not be what you want. – Blender Apr 14 '13 at 04:55
  • the one with `-b` option alone also not working, giving same error :( – daydreamer Apr 14 '13 at 05:00
  • the format of the `-b` cookie file is not just `var=value`, it should be the same as the format of the cookie jar written using `-c`. Go to a site that sends cookies with this option, and take a look at the resulting file. – Barmar Apr 14 '13 at 05:09
  • 2
    The -b cookie_file should either be in Netscape/Mozilla format or plain HTTP headers. Here's an example of plain http headers: Set-cookie: cookie_name=cookie_value; This is the bare minimum. Don't forget the semicolon at the end. – Alex Aug 25 '18 at 17:28

10 Answers10

702

This worked for me:

curl -v --cookie "USER_TOKEN=Yes" http://127.0.0.1:5000/

I could see the value in backend using

print(request.cookies)
Francisco
  • 10,918
  • 6
  • 34
  • 45
daydreamer
  • 87,243
  • 191
  • 450
  • 722
  • 20
    As long as you never, ever have a boolean for a user token as a cookie, given that they can just authenticate themselves without logging in that way. – matts1 Mar 22 '14 at 03:50
  • 19
    According to the man page, for option `-b, --cookie`, e.g. `curl -b `, if the argument is a string having the `'='` symbol, it's passed as is, otherwise it's treated as a filename to read cookie from. – ryenus Oct 21 '14 at 02:10
  • 93
    multiple cookies can be set with semicolons `--cookie "key1=val1;key2=val2;..."` –  Aug 05 '17 at 17:46
  • For sending multiple cookies, I had to put a space after `;` (I am using a mac) Eg: `--cookie "key1=val1; key2=val2"` – tharinduwijewardane Aug 05 '21 at 09:41
117

You can refer to https://curl.haxx.se/docs/http-cookies.html for a complete tutorial of how to work with cookies. You can use

curl -c /path/to/cookiefile http://yourhost/

to write to a cookie file and start engine and to use cookie you can use

curl -b /path/to/cookiefile  http://yourhost/

to read cookies from and start the cookie engine, or if it isn't a file it will pass on the given string.

simhumileco
  • 31,877
  • 16
  • 137
  • 115
Moeen M
  • 1,352
  • 1
  • 9
  • 10
  • 1
    IMO you did not enhance on the official doc which is clear as mud - __beside the overloading__ of the `-b` flag what is the essential diff between `-c` & `-b` they both start the engine and point to a cookie file? – nhed May 24 '16 at 03:46
  • 8
    @nhed `-c` *writes* to the cookie file, `-b` reads from it. So when sending credentials for a login form you would specify `-c` to write the resulting cookie to a file, then you would use `-b` to read from and include the cookie in your next request. – Madbreaks Sep 30 '16 at 21:36
  • 43
    Or do `curl -b cookiefile -c cookiefile https://yourhost/` to read *and* write to the same cookie store like browsers do. – LinuxDisciple Oct 12 '16 at 16:35
54

You are using a wrong format in your cookie file. As curl documentation states, it uses an old Netscape cookie file format, which is different from the format used by web browsers. If you need to create a curl cookie file manually, this post should help you. In your example the file should contain following line

127.0.0.1   FALSE   /   FALSE   0   USER_TOKEN  in

having 7 TAB-separated fields meaning domain, tailmatch, path, secure, expires, name, value.

yurloc
  • 2,268
  • 1
  • 15
  • 20
  • 2
    Yes, this is the cURL cookie format. These are TABS and not SPACES. – m3nda May 26 '15 at 07:31
  • 5
    This should be marked as the official answer, since this truly addresses the point as to why @daydreamer's setup was failing. – Valber Nov 29 '18 at 18:30
11

curl -H @<header_file> <host>

Since curl 7.55 headers from file are supported with @<file>

echo 'Cookie: USER_TOKEN=Yes' > /tmp/cookie

curl -H @/tmp/cookie <host>

docs & commit

afirth
  • 141
  • 1
  • 3
6

If you have made that request in your application already, and see it logged in Google Dev Tools, you can use the copy cURL command from the context menu when right-clicking on the request in the network tab. Copy -> Copy as cURL. It will contain all headers, cookies, etc..

Flip
  • 6,233
  • 7
  • 46
  • 75
  • This solution is much easier if the cookie you need is already available via a browser. +1 – JSuar May 19 '21 at 10:42
4

I'm using Debian, and I was unable to use tilde for the path. Originally I was using

curl -c "~/cookie" http://localhost:5000/login -d username=myname password=mypassword

I had to change this to:

curl -c "/tmp/cookie" http://localhost:5000/login -d username=myname password=mypassword

-c creates the cookie, -b uses the cookie

so then I'd use for instance:

curl -b "/tmp/cookie" http://localhost:5000/getData
daydreamer
  • 87,243
  • 191
  • 450
  • 722
  • 4
    `~` is not expanded to `$HOME` if it's put within quotes, in which case it will be treated as a literal tilde. ;] – Sahbi Dec 14 '20 at 19:14
3

Another solution using json.

CURL:

curl -c /tmp/cookie -X POST -d '{"chave":"email","valor":"hvescovi@hotmail.com"}' -H "Content-Type:application/json" localhost:5000/set

curl -b "/tmp/cookie" -d '{"chave":"email"}' -X GET -H "Content-Type:application/json"  localhost:5000/get

curl -b "/tmp/cookie" -d '{"chave":"email"}' -X GET -H "Content-Type:application/json" localhost:5000/delete

PYTHON CODE:

from flask import Flask, request, session, jsonify
from flask_session import Session

app = Flask(__name__)

app.secret_key = '$#EWFGHJUI*&DEGBHYJU&Y%T#RYJHG%##RU&U'
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)

@app.route('/')
def padrao():
    return 'backend server-side.'
    
@app.route('/set', methods=['POST'])
def set():
    resposta = jsonify({"resultado": "ok", "detalhes": "ok"})
    dados = request.get_json()  
    try:  
        if 'chave' not in dados: # não tem o atributo chave?
            resposta = jsonify({"resultado": "erro", 
                            "detalhes": "Atributo chave não encontrado"})
        else:
            session[dados['chave']] = dados['valor']
    except Exception as e:  # em caso de erro...
        resposta = jsonify({"resultado": "erro", "detalhes": str(e)})

    resposta.headers.add("Access-Control-Allow-Origin", "*")
    return resposta  

@app.route('/get')
def get():
    try:
        dados = request.get_json()  
        retorno = {'resultado': 'ok'}
        retorno.update({'detalhes': session[dados['chave']]}) 
        resposta = jsonify(retorno)
    except Exception as e:  
        resposta = jsonify({"resultado": "erro", "detalhes": str(e)})
    
    resposta.headers.add("Access-Control-Allow-Origin", "*")
    return resposta 

@app.route('/delete')
def delete():
    try:
        dados = request.get_json()  
        session.pop(dados['chave'], default=None)
        resposta = jsonify({"resultado": "ok", "detalhes": "ok"})        
    except Exception as e:  # em caso de erro...
        resposta = jsonify({"resultado": "erro", "detalhes": str(e)})
            
    resposta.headers.add("Access-Control-Allow-Origin", "*")
    return resposta  

app.run(debug=True)
hvescovi
  • 41
  • 3
1

Here is an example for the correct way to send cookies. -H 'cookie: key1=val2; key2=val2;'

cURL offers a convenience of --cookie as well. Run man curl or tldr curl

This was copied from Chrome > inspect >network > copy as cURL.

curl 'https://www.example.com/api/app/job-status/' \
  -H 'authority: www.example.com' \
  -H 'sec-ch-ua: "Chromium";v="92", " Not A;Brand";v="99", "Google Chrome";v="92"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.111.111 Safari/111.36' \
  -H 'content-type: application/json' \
  -H 'accept: */*' \
  -H 'origin: https://www.example.com' \
  -H 'sec-fetch-site: same-origin' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-dest: empty' \
  -H 'referer: https://www.example.com/app/jobs/11111111/' \
  -H 'accept-language: en-US,en;q=0.9' \
  -H 'cookie: menuOpen_v3=true; imageSize=medium;' \
  --data-raw '{"jobIds":["1111111111111"]}' \
  --compressed
ldmtwo
  • 419
  • 5
  • 14
0

I am using GitBash on Windows and nothing I found worked for me.

So I settled with saving my cookie to a file named .session and used cat to read from it like so:

curl -b $(cat .session) http://httpbin.org/cookies

And if you are curious my cookie looks like this:

session=abc123
Valerij Dobler
  • 1,848
  • 15
  • 25
0

This worked for me:

curl -v -H 'cookie: _appname_session=lksjdflsdkj...; path=/; HttpOnly; SameSite=Lax' localhost:3000/

Note that the session name is _appname_session.

GeorgeH
  • 1
  • 1