I'm trying to set cookies with Go's net/http package. I have:
package main
import "io"
import "net/http"
import "time"
func indexHandler(w http.ResponseWriter, req *http.Request) {
expire := time.Now().AddDate(0, 0, 1)
cookie := http.Cookie{"test", "tcookie", "/", "www.domain.com", expire, expire.Format(time.UnixDate), 86400, true, true, "test=tcookie", []string{"test=tcookie"}}
req.AddCookie(&cookie)
io.WriteString(w, "Hello world!")
}
func main() {
http.HandleFunc("/", indexHandler)
http.ListenAndServe(":80", nil)
}
I tried googling 'Golang' with 'cookies', but didn't get any good results. If anyone can point me in the right direction it would be greatly appreciated.
I am not a Go expert, but I think you are setting the cookie on the request, aren't you? You might want to set it on the response. There is a setCookie function in net/http. This might help:
http://golang.org/pkg/net/http/#SetCookie
func SetCookie(w ResponseWriter, cookie *Cookie)
//ShowAllTasksFunc is used to handle the "/" URL which is the default ons
func ShowAllTasksFunc(w http.ResponseWriter, r *http.Request){
if r.Method == "GET" {
context := db.GetTasks("pending") //true when you want non deleted notes
if message != "" {
context.Message = message
}
context.CSRFToken = "abcd"
message = ""
expiration := time.Now().Add(365 * 24 * time.Hour)
cookie := http.Cookie{Name: "csrftoken",Value:"abcd",Expires:expiration}
http.SetCookie(w, &cookie)
homeTemplate.Execute(w, context)
} else {
message = "Method not allowed"
http.Redirect(w, r, "/", http.StatusFound)
}
}
There is a basic difference between Requests and ResponseWriter, a Request is what a browser will send like
Host: 127.0.0.1:8081
User-Agent: ...
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Referer: http://127.0.0.1:8081/
Cookie: csrftoken=abcd
Connection: keep-alive
and a response is what the handler will send, something like :
Content-Type: text/html; charset=utf-8
Date: Tue, 12 Jan 2016 16:43:53 GMT
Set-Cookie: csrftoken=abcd; Expires=Wed, 11 Jan 2017 16:43:53 GMT
Transfer-Encoding: chunked
<html>...</html>
When the browser will make a request, it'll include the cookie for that domain, since cookies are stored domain wise and can't be accessed from cross domains, if you set a cookie as HTTP only then it can only be accessed from the website which set it via HTTP and not via JS.
So when getting information from cookies you can do that from the r.Cookie method, like this
cookie, _ := r.Cookie("csrftoken")
if formToken == cookie.Value {
https://github.com/thewhitetulip/Tasks/blob/master/views/addViews.go#L72-L75
But when you are going to set a cookie, you have to do it in the response writer method, the request is a read only object which we respond to, think of it as a text message you get from someone, that is a request, you can only get it, what you type is a response, so you can type in a cookie at
for more details: https://thewhitetulip.gitbooks.io/webapp-with-golang-anti-textbook/content/content/2.4workingwithform.html
This Below code helps u
cookie1 := &http.Cookie{Name: "sample", Value: "sample", HttpOnly: false}
http.SetCookie(w, cookie1)
Below shows how we use cookie in our product:
func handleFoo(w http.ResponseWriter, r *http.Request) {
// cookie will get expired after 1 year
expires := time.Now().AddDate(1, 0, 0)
ck := http.Cookie{
Name: "JSESSION_ID",
Domain: "foo.com",
Path: "/",
Expires: expires,
}
// value of cookie
ck.Value = "value of this awesome cookie"
// write the cookie to response
http.SetCookie(w, &ck)
// ...
}
It was not working for me in Safari until I added the Path and MaxAge. Both secure and regular cookies worked for me
Sharing so that it helps someone who is stuck like me for more than 2 days :)
expire := time.Now().Add(20 * time.Minute) // Expires in 20 minutes
cookie := http.Cookie{Name: "username", Value: "nonsecureuser", Path: "/", Expires: expire, MaxAge: 86400}
http.SetCookie(w, &cookie)
cookie = http.Cookie{Name: "secureusername", Value: "secureuser", Path: "/", Expires: expire, MaxAge: 86400, HttpOnly: true, Secure: true}
http.SetCookie(w, &cookie)
First, you need to create Cookie and then using http package's SetCookie() function you can set the cookie.
expire := time.Now().Add(10 * time.Minute)
cookie := http.Cookie{Name: "User", Value: "John", Path: "/", Expires: expire, MaxAge: 90000}
http.SetCookie(w, &cookie)
You can use gorilla package for handling cookies or i would say secure cookies: http://www.gorillatoolkit.org/pkg/securecookie
Related
I am trying to access an endpoint on my Express server that has an http only cookie as part of the authentication dance to gain access.
Here we set the http only cookie:
res.cookie('jwt', refreshToken, { httpOnly: true, sameSite: 'none', secure: true, maxAge: 3 * 24 * 60 * 60 * 1000 });
return res.send({ accessToken, role: userModel.role, profileId: userModel.profile });
Here is my endpoint I am trying to integration test:
router.delete('/auth/delete/:id', basicAuth, checkAccountId, mentors.deleteMentorProfile);
Inside basicAuth there is this: export const basicAuth = passport.authenticate('jwt', { session: false, failWithError: true });
What I think is required is that the httponly cookie is sent with the request to this endpoint. The line says passport.authenticate('jwt', ...) hence I must need the 'jwt' value to be there in a cookie.
And here is the code from my integration test:
const logInResponse = await api.post('/api/users/authenticate').send(userAcctCreationDetails);
const jwtForTest = logInResponse.body.accessToken;
const allMentors = await Mentor.find({});
const testAccountMentorId = allMentors.find((mentor) => mentor.email === userToDelete.email)?._id.toString();
// act
const cookies = logInResponse.headers['set-cookie'][0];
// console.log(cookies);
const deletedProfileResponse = await api
.delete('/api/mentor/auth/delete/' + testAccountMentorId)
.set('Authorization', 'Bearer ' + jwtForTest)
.set('jwt', cookies); // this fails!
The line console.log(cookies) says some authentication credentials ending in jwt=eyJhbGciOi...-2n21vxTNPOlS94-YeFhSN7o; Max-Age=259200; Path=/; Expires=Thu, 15 Dec 2022 01:26:24 GMT; HttpOnly; Secure; SameSite=None
My coworker tells me "httponly means the client can't access it" but can I at least send it back? How does a browser send it back if not via javascript?
Note I did google it and i found extreme scarcity of information about supertest and HTTP only cookies.
My backend-api written in Node and Express.js sets a cookie using res.cookie:
Router.post('/login', async (req, res) => {
const email = req.body.email;
const password = req.body.password;
try {
let result = await sqlite.login(email, password);
res.cookie('token', result, {
'maxAge': 3600 * 1000
});
res.send({
'token' : result
});
} catch (err) {
res.send(err);
}
});
I can make a request to this route, and I do notice the Set-Cookie header is set on the response object within Chrome developer tools:
Set-Cookie: token=[...]; Max-Age=3600; Path=/; Expires=Mon, 11 Jul 2022 14:47:08 GMT
However, document.cookie is never set by the browser. From my searching, most people say to specify the credentials field as same-origin. I have done this and it made no change. My cookie is NOT being set as HttpOnly, so I am unsure why it's being set by the browser.
Here is where I call the /login route:
async login(email, password) {
let response = await fetch(apiURL + '/login', {
'method' : 'POST',
'headers' : {
'Content-Type' : 'application/json',
'Accept' : 'application/json'
},
'credentials' : 'same-origin',
'body' : JSON.stringify({
'email' : email,
'password' : password
})
});
return await response.json();
}
A token is successfully returned in the response, but again document.cookie returns an empty string ''.
From searching this problem, most of the issues seem to suggest that same-origin should fix the issue but it is not the case for myself. Another thing of note is that httpOnly cookies won't show in the browser, but I know that the cookies I am sending are not HttpOnly.
I am using Google Chrome version 103.0.5060.114.
If I set credentials to include, I get a CORS error:
The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
I was able to get it working with the following changes:
When initializing npm package cors, specify some options:
app.use(cors({ credentials: true, origin: 'http://lvh.me:3001' }));
The origin must include the http:// prefix as well as the correct port. Once that was done, set credentials to include when calling fetch() and it should work fine.
My response headers look like this
HTTP/1.1 200 OK
Server: nginx/1.9.7
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
X-On-Trial: 1
Cache-Control: no-cache
Access-Control-Allow-Origin: http://localhost:4200
Vary: Origin
Date: Sun, 29 May 2016 00:37:31 GMT
But when I do a console.log(headers) in the RESTAdapter handleResponse function, all that is included is
EmptyObject {Content-Type: "application/json", Cache-Control: "no-cache"}
How can I access the X-On-Trail header or any other custom headers I may need?
I'm not sure if this matters but I am using ember-simple-auth. Does that strip out headers?
I check sources. .handleResponse is called from .ajax
ajax(url, type, options) {
var adapter = this;
var requestData = {
url: url,
method: type
};
return new Ember.RSVP.Promise(function(resolve, reject) {
var hash = adapter.ajaxOptions(url, type, options);
hash.success = function(payload, textStatus, jqXHR) {
let response = adapter.handleResponse(
jqXHR.status,
parseResponseHeaders(jqXHR.getAllResponseHeaders()), // try to check both in debugger
payload,
requestData
);
So just try to stop at parseResponseHeaders(jqXHR.getAllResponseHeaders()) line and check jqXHR.getAllResponseHeaders(). If it's ok - check parseResponseHeaders()
I'll be glad to help with debug, if you have public link for your project or if you can give link for any public project with REstAdapter
About striping - it skips headers which doesn't contains colons
P.S> Thx to #Vlad
xmlHttp.getResponseHeader + Not working for CORS
"Access-Control-Expose-Headers"
Used in response to a preflight request to indicate which HTTP headers can be > used when making the actual request.
Access-Control-Allow-Headers: <field-name>[, <field-name>]*
i have an app running over Sencha Touch. It makes a POST request to a Django server with some data from a form. This contains textfields and a image file.
Aparently, everything goes ok. The app is capable to correctly send data to server, the serve is capable of receiving the data and process it adequately (including the image file) and answer it with a status 200. The client even receives this status 200. However, the callback function called in the sencha touch app is the failure one, not success.
This is the response header that the client receives:
HTTP/1.0 200 OK
Date: Thu, 08 May 2014 20:59:29 GMT
Server: WSGIServer/0.1 Python/2.7.6
Vary: Cookie
X-Frame-Options: SAMEORIGIN
Content-Type: text/html; charset=utf-8
Access-Control-Allow-Origin: *
I'm doing the POST using this:
values = form.getValues();
var request = {
url: 'http://127.0.0.1:8000/profiles/create/',
method: 'POST',
success: function(conn, response, options, eOpts) {
Ext.Msg.alert('Success!', 'We are happy!.');
},
failure: function(conn, response, options, eOpts) {
Ext.Msg.alert('Error: status code ', response.status);
},
disableCaching: true,
xhr2: true,
progress: progressIndicator
}
form.submit(request)
How to known what i'm doing wrong?
From sencha docs about form.submit
success : Function
The callback that will be invoked after a successful response. A
response is successful if a response is received from the server and
is a JSON object where the success property is set to true,
{"success": true}.
So your response should be a valid json and contain success:true like this:
{success: true, data: 'mydata'}
I created an API /user/auth where I can send using POST a Json object like:
var user = {"username":"alex", "password":"m"}
$http(
{
method: 'POST',
url: '/api/v1/user/auth',
data: user,
}
).
success(function(data, status, headers, config) {
console.log(data)
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
The response from Django is the following:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Content-Type,*
Access-Control-Allow-Methods:POST,GET,OPTIONS,PUT,DELETE
Access-Control-Allow-Origin:*
Content-Language:fr
Content-Type:application/json
Date:Fri, 30 Aug 2013 15:22:01 GMT
Server:WSGIServer/0.1 Python/2.7.5
Set-Cookie:sessionid=w63m0aoo8m3vfmvv0vk5d6w1708ftsrk; Path=/
Vary:Accept, Accept-Language, Cookie
So Django returns a good cookie but I don't know why, Chrome doesn't set this cookie in Resource.
The request is sent from 127.0.0.1:8000 to 127.0.0.1:8080; I use this middleware to handle CROS requests and I also set:
SESSION_COOKIE_HTTPONLY = False
The problematic line is:
Access-Control-Allow-Origin: *
The credential request doesn't work with a wildcard allow origin. You have to specifically set the name, like :
Access-Control-Allow-Origin: http://127.0.0.1:8080
You can find more information here:
https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS?redirectlocale=en-US&redirectslug=HTTP_access_control#Requests_with_credentials
Ok thanks to Bqm link to mozilla I finally found why the cookie was not set.
Indeed you need to set in the header you sent:
Access-Control-Allow-Credentials: true
In Angular this is done with this method:
$http(
{
method: 'POST',
url: '/api/v1/user/auth',
data: user,
withCredentials: true
}
)
Once your backend will answer with a setCookie, the browser will then be able to set the cookie in your browser.