I have various country domains which I wish to share cookie data with the primary domain, ie for auth purposes so that users remain logged in or identified when transitioning between these domains.
All is working as expected in Chrome, however Firefox is refusing to send non-session cookies along with cross-site requests. (session cookies are being included successfully). There are no warnings or errors within the firefox console relating to this.
When a cookie is set on the primary domain, I am setting SameSite to none and secure to true:
set-cookie: nonsessioncookie=abc1234; expires=Sun, 03-Sep-2023 13:36:26 GMT; path=/; SameSite=none; secure=true;
I then perform a jQuery request from a secondary domain to the primary domain:
$.ajax({
url: primaryHost + "/Services/AuthRequest.ashx",
data: { 'sdtoken': stoken},
xhrFields: {
withCredentials: true
},
crossDomain: true,
success: function (data) {
// Do Something
}
});
The following Request headers are sent, however notice the only cookie being sent is a session cookie; the 'nonsessioncookie' cookie is missing from the request:
Origin: https://secondary.domain
Connection: keep-alive
Referer: https://secondary.domain/
Cookie: ASP.NET_SessionId=rpchfdrzqzp5zujwi24fjll2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
And the Response contains the following headers returned from the primary domain
access-control-allow-origin: https://secondary.domain
access-control-allow-methods: POST, GET, OPTIONS
access-control-allow-credentials: true
Chrome successfully passes the 'nonsessioncookie', however firefox doesn't, even though SameSite and secure is set.
Any thoughts what could be preventing non-session cookies from being passed?
Summary
Once set-cookie header is sent in a response it takes another request before the cookie is visible in handle() function in the hooks.ts file.
Example
User POSTs username & password to the login endpoint;
Enpoint responds with set access_token cookie header;
User should be redirected to a protected page. (FAILS)
It fails because auth guard checks if the cookie exists, but it can't be seen from a code side at this point, only in the browser end.
Refresh the page
User is now able to be redirected to a protected page.
Minimal reproduction
It has dummy login/logout functionality & protected user profile. There are also server side console logs which shows that cookie lags to be recognised in a hook.
As mentioned in the comments, your server returns the HTTP status code 200 which does not redirect the Client after the Login. The browser assumes it is already at its final destination due to the status code.
In this case, best practice is to use the status code 302: Wikipedia
Modified /src/routes/auth/login.ts
import * as cookie from 'cookie';
export const post = (request) => {
return {
status: 302,
headers: {
location: '/',
'set-cookie': `${cookie.serialize('token', 'VALUE_OF_THE_COOKIE')}; path=/; HttpOnly`
}
}
};
export const del = (request) => {
return {
status: 302,
headers: {
location: '/',
'set-cookie': `${cookie.serialize('token', '')}; path=/; HttpOnly; maxAge: 0`
}
}
};
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 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.
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