connection.id and cookies

184 views
Skip to first unread message

David S Taylor

unread,
Jan 7, 2014, 4:18:06 PM1/7/14
to action...@googlegroups.com
I've written a little integration test with the 'request' http client. I may be making the wrong assumption, but I was expecting the connection.id to be set based on the 'sessionID' cookie passed by the http client. However, in the code below, the connection.id changes on the second request. Im using the session initializer from here http://blog.evantahler.com/blog/authentication-with-actionHero-again.html, although its probably been modified some


        var j = request.jar()
        var cookie = request.cookie('sessionID')
        j.setCookie(cookie, 'http://localhost:8080/api', function (err, cookie){
            console.log('err = ' + err)
            console.log('cookie = ' + cookie)
            request({url: 'http://localhost:8080/api/admin/login', jar: j, method: "POST", form: {username: 'dtaylor', password: '*****'} },
                function (err, response, body) {
                    console.log(body)
                    console.log(response.headers['set-cookie'])
                    request({url: 'http://localhost:8080/api/users', jar: j}, function(err, response, body) {
                        console.log(body)
                        console.log(response.headers['set-cookie'])
                        done()
                })
            })
        })

David S Taylor

unread,
Jan 7, 2014, 7:53:52 PM1/7/14
to action...@googlegroups.com
Just wanted to clarify the problem, last description might have been confusing


1. When I make the original call to login (url: 'http://localhost:8080/api/admin/login',) it succeeds, and it places the connection.id in the session via api.session.generateAtLogin

2. With the next and subsequent calls to http://localhost:8080/api/users, I get a different connection.id. Thus the connection placed in the session in step #1 is never found again

Evan Tahler

unread,
Jan 8, 2014, 1:07:41 AM1/8/14
to David S Taylor, action...@googlegroups.com
You shouldn't mess with the cookie itself at all, it's taken care of for you (request follows set-cookie directives).

Here's a test I added to ensure that sessionIDs are persisting across requests



--
You received this message because you are subscribed to the Google Groups "actionHero.js" group.
To unsubscribe from this group and stop receiving emails from it, send an email to actionhero-j...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

David S Taylor

unread,
Jan 8, 2014, 11:14:07 AM1/8/14
to action...@googlegroups.com, David S Taylor
I changed my test to mimic your test, and still the first request is returning a different connection.id than the second request. So its still failing. The difference is, my first request is a POST, not a GET, and its actually 'logging the user on' with middleware, storing the connection.id in api.session.save etc

However, if I replace the first request, which is a POST 

request({url: 'http://localhost:8080/api/admin/login', method: "POST", jar: j, form: {username: 'dtaylor', password: '****'} }, function (err, response1, body1)

with a GET

request({url: 'http://localhost:8080/api/users', jar: j}, function (err, response1, body1) {

the connection.id always comes back the same and the integration test passes. Of course thats not very useful for me, because I need to test login :)

I also rewrote the test using a different programming language with the same sequence (login/POST, GET, GET), hitting a running ActionHero server, and it logged in and returned connection ids perfectly. So it appears Im doing something wrong with the request client and posts, or, something in my login action has a side-effect on the connection.id....

David S Taylor

unread,
Jan 8, 2014, 11:26:54 AM1/8/14
to action...@googlegroups.com, David S Taylor
Here's a test I added to ensure that sessionIDs are persisting across requests

Thanks, that sparked an idea, this bit:

body1.requesterInformation.id.should.equal(body2.requesterInformation.id);

It would be useful to store the username of the logged in user on the requestInformation. Is there a recommended way to extend building the requesterInformation?

Evan Tahler

unread,
Jan 8, 2014, 12:45:29 PM1/8/14
to David S Taylor, action...@googlegroups.com
I changed the fingerprint test to start with POST and went though a number of HTTP methods... they all work OK.  I can't quite see where your bugs are coming from.

No, there isn't a way to change requestorInformation, as it's a metadata response form the server, and most people turn it off in production.  Any response you want to send to the client should be set in `connection.response.stuff`. 

Are you changing connection.id anywhere in your login action?  You shouldn't ever be setting connection.id, as the server handles that for you.  If you want to store information about the user (like perhaps their username), don't store that on the connection object itself, as the connection object will be created and destroyed on each request... but it will get rebuilt with the same id if the request is coming from the same browser.   

Store any information you need in api.cache or something else persistent and use a middleware to load it up each request and append it to connection.username if that is how you want to access it within actions.



David S Taylor

unread,
Jan 8, 2014, 2:53:31 PM1/8/14
to action...@googlegroups.com, David S Taylor
> Are you changing connection.id anywhere in your login action?  You shouldn't ever be setting connection.id, as the server handles that for you.

No. In fact I just commented out my authorization middleware, and commented out all code in my login action (except next(connection, true) ). And still he same assertion failure, see:

https://gist.github.com/bluesunrise/8322944

Suspicious its something wrong with my test included in the gist above, mystery at this point :-(

> Store any information you need in api.cache or something else persistent and use a middleware to load it up 

Yes, I am storing the some minimal information about the user (username, roles, loggedInAt, loggedIn) in the api.session initializer like in your blog example. I retrieve it out in my authorization middleware. The idea is to make that information available to actions without them having to go retrieve it again, so I placed it on the connection object. Was just concerned that the connection might get reused and value wouldn't get cleared, but you have assured me thats not the case with HTTP connections

> No, there isn't a way to change requestorInformation,

OK, just a thought, seemed generic enough to go in requestorInformation, but I see your point. I can put it back in the general response 


> I changed the fingerprint test to start with POST and went though a number of HTTP methods... they all work OK.

Thanks, I really appreciate your efforts, sorry about this mess I've seemed to have gotten myself into

David S Taylor

unread,
Jan 8, 2014, 3:54:09 PM1/8/14
to action...@googlegroups.com, David S Taylor
Added a debug statement to my middleware:

    var authorization = function(connection, actionTemplate, next){
        console.log('cookies = ' + JSON.stringify(connection.rawConnection.cookies))
        next(connection, true)
        // removed everything else!
    })

and that debug line prints for all requests:

cookies = {}

Yet, I've verified that ActionHero is clearly returning the cookie in the response.

Added a route (the second one):

        { path: '/admin/login', action: 'login'},

        { path: '/login', action: 'login'},

And then

        request.post({url: 'http://localhost:8080/api/login', jar: j, form: {username: 'dtaylor', password: '****'} },

and now the cookie is sent back to the server, and the tests passes correctly with the /login route. The original route /admin/login still fails to send cookies. 

Evan Tahler

unread,
Jan 9, 2014, 12:13:05 AM1/9/14
to David S Taylor, action...@googlegroups.com
I've got it!  

Your gist above was the missing key.  I emulated what you were doing and was also finally able to make the test suite pass, and was as perplexed as you were.  I started digging into the jar request was using and noticed that it eventually contained more than one cookie for session, which was weird.  It turns out that request is a *very* rigid follower of HTTP specs, and it correctly assumed that a set-cookie issued without a 'path' is only valid for the path the request was made too.  That means if you get a cookie from site.com/thing the types of cookies we were setting wouldn't be valid on site.com/stuff.  

I'll wager whatever other language/library you were using wasn't so formal, and allowed the cookie to work everywhere.

tldr:  just do THIS and you should be OK.  This is now the default in all new actionHero projects.  

FYI, you can set all sorts of cookie options in this param, like expiration times, 'secure-ness', etc.

Whew.

--
 Evan Tahler
 412.897.6361
 evant...@gmail.com



David S Taylor

unread,
Jan 9, 2014, 12:28:45 AM1/9/14
to action...@googlegroups.com, David S Taylor
> I've got it!  

Dude! I thought you'd written me off for a nut case :-) Im glad you stuck with it. Wow.

So I made your one change to config.js, and ran the integration test with my /api/admin/login route, and voila my tests now all pass!

Note that printing the cookies has a little bit more information now:

[ 'sessionID=934df4cd34bcd2cafbef32cb077f335c2863cb03;path=/;' ]

Thanks!!!

>  a *very* rigid follower of HTTP specs

yes sounds like it, but its still not clear why /api/login has the same cookie path as /api/users but not /api/admin/login 

btw: the other language/library was JerseyClient 
Reply all
Reply to author
Forward
0 new messages