Gorilla sessions not working for CORS from client - session

I have set up a Go rest api. And on login I do this:
session, _ := store.New(r, sessionId)
session.Options.MaxAge = 12 * 3600
err := session.Save(r, w)
//treat error
and for checking the session i have smth like this:
session, err := store.Get(r, sessionId)
//treat error
if session.IsNew {
http.Error(w, "Unauthorized session.", http.StatusUnauthorized)
return
}
If I do the requests from postman it works fine, but when I do them from my client I get 401. Has any of you experienced something like this? The store is a CookieStore.
I already checked the id's, I replaced sessionId variable with a static string. Gorilla session uses gorilla context to register a new request and when I do the request from postman context.data[r] is not null, but from the client it is always null -> always a new session.
https://github.com/gorilla/context/blob/master/context.go - line 33
it is called in
https://github.com/gorilla/sessions/blob/master/sessions.go - line 122
wich is used in the CookieStore.Get function in
https://github.com/gorilla/sessions/blob/master/store.go - line 77
EDIT 1:
For the client I use polymer and I tried xmlhttp too.
Polymer:
<iron-ajax
id="ajaxRequest"
auto
url="{{requestUrl}}"
headers="{{requestHeaders}}"
handle-as="json"
on-response="onResponse"
on-error="onError"
content-type="application/json"
>
</iron-ajax>
and the handlers
onResponse: function(response){
console.log(response.detail.response);
this.items = response.detail.response
},
onError: function(error){
console.log(error.detail)
},
ready: function(){
this.requestUrl = "http://localhost:8080/api/fingerprint/company/" + getCookie("companyId");
this.requestHeaders = {"Set-cookie": getCookie("api_token")}
}
and the cookie successfully reaches the backend.
And xmlhttp:
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE ) {
if(xmlhttp.status == 200){
//do stuff
}else if(xmlhttp.status == 401){
page.redirect("/unauthorized")
}else{
page.redirect("/error")
}
}
}
xmlhttp.open("GET","http://localhost:8080/api/fingerprint/company/" + getCookie("companyId"),true);
xmlhttp.setRequestHeader("Set-cookie", getCookie("api_token"));
xmlhttp.send();
EDIT 2:
So I tried debugging with fiddler(thanks for the suggestion) and i found out that the request from postman has an bold entry Cookies / Login and the request from the client does not. Any idea how to get/set that value? It is somehow automatically set in Postman. In the authentication request I get a set-cookie header that has all the data that I need but I can't get it on the client. I get Refused to get unsafe header set-cookie.

The problem is that in the client the requests need to have withCredentials = true and after that the browser deals with everything. It gets the cookie from the set-cookie header and it sends the cookies via the cookie header. So, after all, it was not a gorilla sessions problem.

If anyone else is having the same problem I was having and you want to whitelist all domains/wildcards (or have a list of domains in an array you can scan through), you can do something like this.
domain_raw := r.Host
domain_host_parts := strings.Split(domain_raw, ".")
domain := domain_host_parts[1] + "." + domain_host_parts[2]
domains := getDomains() // stores a slice of all your allowable domains
has_domain := false
for _, d := range domains {
if d == domain {
has_domain = true
break
}
}
if has_domain == false {
return
} else {
w.Header().Add("Access-Control-Allow-Origin", "https://"+domain_raw)
w.Header().Add("Access-Control-Allow-Credentials", "true")
}
I love go

Related

Indy doesn't get response text returned with error 404

My HTTP server returns custom 404 error text when REST route is not found:
{"sessionIdent":"051F-dUen7-tetW-kNf82-WxT","Details":[{"messageCode":60,"messageCategory":"","messageText":"No matching route for \"POST \/Warehouse\/A1\/Orders\/execute\""}]}
Following JavaScript code displays this response text in browser just fine:
function httpReq(method, url, headers, jsonStr, userName, password) {
try
{
var xmlhttp = new XMLHttpRequest();
xmlhttp.open(method, url, true);
xmlhttp.onreadystatechange = function () {
console.log("onreadystatechange");
if (xmlhttp.readyState == 4) {
console.log("ready");
console.log(xmlhttp.status);
console.log(xmlhttp.responseText);
}
}
// Send the request
xmlhttp.setRequestHeader('Cache-Control', 'no-cache, max-age=0');
xmlhttp.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
xmlhttp.setRequestHeader('Session-Ident', '051F-dUen7-tetW-kNf82-WxT');
xmlhttp.setRequestHeader('Accept', 'application/json');
if (headers) {
var headerKeys = Object.keys(headers);
Object.keys(headers).forEach(key => {
xmlhttp.setRequestHeader(key, headers[key]);
});
}
if ((userName !== "") && (password !== ""))
{
xmlhttp.setRequestHeader("Authorization", "Basic " + btoa(userName + ":" + password));
}
console.log("before send");
xmlhttp.send(jsonStr);
console.log("after send");
}
catch (ex)
{
console.log(ex);
}
}
Indy's TIdHTTP raises an EIdHTTPProtocolException exception with message HTTP/1.1 404 Not Found instead of my response text inside.
When I use the hoNoProtocolErrorException option:
_client.HTTPOptions := _client.HTTPOptions + [hoNoProtocolErrorException];
exception is not raised any more, but response text is empty.
procedure TFormRestTest._httpSend(AMethod, APath, AHeaders, ABody: string);
var
queryData, replyData: TStream;
resultText: string;
begin
queryData := TStringStream.Create(ABody, TEncoding.UTF8);
try
replyData := TMemoryStream.Create;
try
_client.Request.ContentType := 'application/json';
_client.Request.CharSet := 'UTF-8';
_client.Request.BasicAuthentication := True;
_client.Request.Username := 'Username';
_client.Request.Password := 'Password';
_client.Request.CustomHeaders.Clear;
_client.Request.CustomHeaders.Text := AHeaders;
_client.DoRequest(AMethod, APath, queryData, replyData, []);
replyData.Position := 0;
resultText = ReadStringAsCharset(replyData, _client.Response.CharSet)]);
_log(resultText); //resultText is empty
finally
replyData.Free();
end;
finally
queryData.Free();
end;
end;
How can I retrieve my response body?
When I use the hoNoProtocolErrorException option ... exception is not raised any more, but response text is empty.
That is by design. When disabling the exception, you need to also enable the hoWantProtocolErrorContent option to actually receive the response's body data into your replyData stream, eg:
_client.HTTPOptions := _client.HTTPOptions + [hoNoProtocolErrorException, hoWantProtocolErrorContent];
This is explained in more detail on the following article on Indy's Changelog Blog:
New TIdHTTP flags and OnChunkReceived event
Three new flag have been added to the TIdHTTP.HTTPOptions property:
...
hoWantProtocolErrorContent: when an HTTP error response is received, TIdHTTP normally reads and discards the response’s message body and then raises EIdHTTPProtocolException (the message body is available in the EIdHTTPProtocolException.ErrorMessage property). If the hoNoProtocolErrorException flag is enabled, or the ResponseCode number is specified in the request’s AIgnoreReplies parameter, then no EIdHTTPProtocolException is raised, as the caller would like to process the ResponseCode manually. Normally TIdHTTP would still discard the message body, though. If this new flag is enabled, the message body will no longer be discarded, it will be saved in the caller’s target TStream like a successful response would be. This flag is disabled by default to preserve existing behavior to discard error message bodies.
...
Based on your earlier comment to another question, you seem to not have the hoWantProtocolErrorContent option available in your version of Indy. In which case, you are using a very outdated version of Indy and should upgrade to the latest version from Indy's GitHub repository.
UPDATE: If that is not an option for you, for whatever reason, then you have no choice but to catch the EIdHTTPProtocolException and read the body content from its ErrorMessage property, eg:
try
_client.DoRequest(AMethod, APath, queryData, replyData, []);
replyData.Position := 0;
resultText := ReadStringAsCharset(replyData, _client.Response.CharSet)]);
except
on E: EIdHTTPProtocolException do
resultText := E.ErrorMessage;
end;

Express.js res.render not redirecting, just displayed in console

This time I want to use res.render to display html as success of DB update. I did it several times, but this time it doesn't work. It's not render html file, just displayed on chrome's console.
I think it caused because of async problem or duplicated response. I tried to many ways but I couldn't solve it, so pointers appreciated.
The code is related when the user paid service, increase user's level.
Get Access Token => Validate => res.render
app.post('/payment/validate', function(req, res, next){
// Get access token
request.post({
url : 'https://payment-company/get/token'
}, function(err, response, body) {
if(!err & response.statusCode == 200) {
var result = JSON.parse(body);
var accessToken = result.response.access_token;
// Validate payment (compare paid and would be paid)
request.get({
headers : { 'Authorization' : accessToken }
url : 'https://payment-company/find/paymentid'
}, function (err, response, body) {
if (!err && response.statusCode == 200){
var result = JSON.parse(body);
if (result.response.amount == req.body.price){
Members.findOne({id : req.user.id}, function(err, member){
// If no problem, update user level
member.level = 2;
member.save(function(err, result){
if (err) return next();
res.render('payment.view.result.ejs',
{
title : 'Success !',
description : 'level up.'
});
});
});
}
} else {
...
}
});
}
})
});
sorry to verbose code I tried to shorten code, No problem until res.render, res.render will work but it's not display page instead it just send html code to chrome's console.
Looks like there's a bit of a misunderstanding of how these requests work. What I think you intend:
Browser makes a GET request, server responds with an HTML document, the browser renders it
User takes an action
Browser makes a POST request, server responds with an HTML document, the browser renders it
What you've started coded on the frontend is an alternate method:
You make a POST request via AJAX, server responds with some JSON, you modify the current document with JavaScript to let the user know

Node Express as proxy for a server that requires basic authentication

So I'm using express and express-http-proxy as a proxy to an API web server that requires basic authentication. Then in my app, I'll be issuing Ajax calls to these APIs. After some effort I got this working but I'm sure there's a better way out there, hence this post.
Initially I set up the express proxy as follows:
var express = require('express'),
app = express(),
proxy = require('express-http-proxy');
app.use('/apis', proxy("https://myserver", {
forwardPath: function(req, res) {
return "/apis" + require('url').parse(req.url).path;
}
}));
When calling a URL directly in the browser (not via Ajax), eg. https://myserver/apis/myapi.ashx, I would see the authentication dialog asking me for my credentials, and I could authenticate and see the result.
However, when accessing the same URL via an Ajax call in my app, I was not getting a popup. Why this difference of behavior?
So I decided I needed to add my own basic authentication middleware using request and basic-auth as follows:
var express = require('express'),
app = express(),
proxy = require('express-http-proxy'),
request = require('request'),
basicAuth = require('basic-auth');
var myAuth = function (req, res, next) {
function unauthorized(res) {
res.set('WWW-Authenticate', 'Basic realm=Rimes');
return res.sendStatus(401);
};
var user = basicAuth(req);
if (!user || !user.name || !user.pass) {
return unauthorized(res);
};
var connectUrl = 'https://'+user.name+':'+user.pass+'#myserver/apis/connect.ashx';
request.get(connectUrl, function(error, response, body) {
if (!error && response.statusCode == 200) {
return next();
} else {
return unauthorized(res);
}
});
};
app.use('/apis', proxy("https://myserver", {
forwardPath: function(req, res) {
return "/apis" + require('url').parse(req.url).path;
}
}));
This worked fine, showing me the authentication popup during the Ajax call.
The obvious disadvantage here is:
Credential verification for every API request, although there may be a way to cache valid credentials. But in its defence, this is only on the development environment.
So is there a better way of doing this? Would a different middleware package do a better job?

Django csrf ajax fails only on production

I've set everything right as far as I can tell and it works in development:
I've included the following code:
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
what could be the reason the this would work on development but not in production?
One possible reason could be that you have CSRF_COOKIE_SECURE = True in your production setttings. This will send the cookie only with HTTPS connections. If you have enabled this, and yet would like a view to handle a POST request without a CSRF token, decorate the view with csrf_exempt.
Edit: Also check if SESSION_COOKIE_SECURE is enabled. If set to True, you can encounter this problem.
I had a similar problem and addressed it by moving urls for signed in users (Django admin pages) to be served only through HTTPS thereby negating the need for the latter setting. I still keep CSRF_COOKIE_SECURE = True and the single URL that I needed to handle a POST request (set_language) wrapped with #csrf_exempt so that it can bypass this setting. This means that any views that need to handle forms for anonymous sessions need to be similarly decorated as well.
All seems to work well for now, but am still doing research to see if there are any other security implications to my configuration. Also, there could be a better way to handle this. Until then, I can live with this approach. HTH.
In my case the following fixed the problem:
#ensure_csrf_cookie
def your_view(request)
According to docs, the csrf cookie may not be set if there's no form with {% csrf_token %} in it.
https://docs.djangoproject.com/en/1.10/ref/csrf/#django.views.decorators.csrf.ensure_csrf_cookie

AJAX Request gets cancelled with AngularJS and Spring Security

We're running an external Grails server-application with the Spring Security plugin.
The front-end is running locally on AngularJS.
Whenever I try to login, the request is immediately canceled.. Remarkably AngularJS sends a GET request first with the OPTIONS method; this returns a 200 OK response just fine.
The actual POST request does never reach the server though... what could possibly cancel my request?
The following code:
$scope.login = function() {
$http.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
$scope.loggingIn = true;
// Setup Config
var data = {
j_username: $scope.user.email,
j_password: $scope.user.password
}
var config = {method: 'POST', url: serverUri+'/j_spring_security_check/', data: data};
// Dispatch HTTP Request
$http(config)
.success(function(data, status, headers, config) {
if (data.status) {
// successful login
User.isLogged = true;
User.username = data.username;
}
else {
User.isLogged = false;
User.username = '';
}
$scope.loggingIn = false;
console.log("NOICE!");
})
.error(function(data, status, headers, config) {
$scope.loggingIn = false;
User.isLogged = false;
User.username = '';
if (status == 0) {
// Request got cancelled
console.log("Request got cancelled.");
return;
}
});
}
This is what the canceled request looks like: http://i.stack.imgur.com/kiWnb.png
This is what the OPTIONS request looks like: http://i.stack.imgur.com/FAj96.png
Apparently Chrome does not handle 302 Moved temporarily status codes efficiently when queried by AngularJS in my situation. Firefox properly shows there is a response where Chrome just shows the request as canceled with no response information whatsoever.
This question is solved, but there is still a mystery as to WHY AngularJS does not work. See my question here:
AngularJS $http ajax does not follow Location header

Resources