Session ID changes during session

111 views
Skip to first unread message

sieben tupel

unread,
Jan 26, 2016, 11:47:41 AM1/26/16
to Crossbar
Short version: 

After reading the docs i would expect to be able to get the caller information inside my callee procedure using the details argument. Unfortunately, i don't. I only get the details of myself, the callee. The disclose_me option is set to True on side of the caller. How do I get the details of the caller and not myself?

Long version:

When I join a realm with any client, the client is issued a random session id. As i understand it, the session id is used to identify each client/session as long as they don't disconnect. In the procedures I register in the router I can access the information about the clients who called the procedure using the implicit details argument.

Now, when I connect my client I get the session id, directly after that I call an endpoint (embedded worker in crossbar) which does nothing but returning my session details. But the session id my client got while joining the realm is different from the one i get back from crossbar. Same goes for authid
and authrole.

When I connect a second client, it gets a new session id when joining the realm. When i call the same echo endpoint with the client, again I get a different session id and not the one I got when I joined the realm. BUT the session id which is echoed from crossbar is the same for booth clients!

Being not enough, when I call some endpoint located at my first client from my second, then the first client gets its own session id (the one it got joining the realm) in details instead of the session id of the second client. (but the debugging logs shows the correct caller id ...)

So I only get the session id of the peer which hosts the endpoint.

The disclose_me option is set True for each call. My clients are written in Python. Crossbar is configured to accept booth ticket and anonymous authentication (first client uses ticket, second uses anonymous)

My echo procedure looks as follows:
def print_info():
   
"""
    """

   
return details.__str__()
...
try:
    yield self.register(print_info, settings.APP_BASE_URI+'info')
except: ...



Here are the logs

Crossbar:
2016-01-26T17:30:49+0100 [Router      23283] called info RPC
2016-01-26T17:30:49+0100 [Router      23283] SessionDetails(realm = realm1, session = 4075389642691519, authid = None, authrole = authenticator, authmethod = None) # <---- first client
2016-01-26T17:30:49+0100 [Router      23283] called info RPC
2016-01-26T17:30:49+0100 [Router      23283] SessionDetails(realm = realm1, session = 4075389642691519, authid = None, authrole = authenticator, authmethod = None) # <---- second client


Client1:
2016-01-26T17:30:49+0100 Client session connected. Starting WAMP-Ticket authentication on realm 'realm1' as principal 'kronos-auth' ..
2016-01-26T17:30:49+0100 session ready
2016-01-26T17:30:49+0100 session details: SessionDetails(realm = realm1, session = 7464678238511771, authid = kronos-auth, authrole = kronos-auth, authmethod = ticket) # <-- session issued when joining
2016-01-26T17:30:49+0100 calling info
2016-01-26T17:30:49+0100 TX WAMP CALL Message (request = 1, procedure = com.myapp.info, args = (), kwargs = {}, timeout = None, receive_progress = None, disclose_me = True)
2016-01-26T17:30:49+0100 RX WAMP RESULT Message (request = 1, args = ['SessionDetails(realm = realm1, session = 4075389642691519, authid = None, authrole = authenticator, authmethod = None)'], kwargs = None, progress = None)
2016-01-26T17:30:49+0100 call result: SessionDetails(realm = realm1, session = 4075389642691519, authid = None, authrole = authenticator, authmethod = None) # <-- getting back a different session id
2016-01-26T17:30:49+0100 TX WAMP REGISTER Message (request = 3, procedure = com.myapp.login, match = exact, invoke = single)
2016-01-26T17:30:49+0100 RX WAMP REGISTERED Message (request = 3, registration = 7876412218466890)
2016-01-26T17:30:49+0100 procedure 'login' registered
2016-01-26T17:30:49+0100 TX WAMP REGISTER Message (request = 4, procedure = com.myapp.kronos-auth.authenticate_peer, match = exact, invoke = single)
2016-01-26T17:38:41+0100 RX WAMP INVOCATION Message (request = 7, registration = 7876412218466890, args = [], kwargs = {'password': '123secret', 'user': 'peter'}, timeout = None, receive_progress = None, caller = 7053397799995983, procedure = None)
2016-01-26T17:38:41+0100 login RPC invoked
2016-01-26T17:38:41+0100 details: SessionDetails(realm = realm1, session = 7464678238511771, authid = kronos-auth, authrole = kronos-auth, authmethod = ticket) # <--- should be session id of client2 (7053397799995983)

Client2:
2016-01-26T17:38:41+0100 Client session connected. Starting WAMP-Ticket authentication on realm 'realm1' as principal 'anonymous' ..
2016-01-26T17:38:41+0100 session ready
2016-01-26T17:38:41+0100 session details: SessionDetails(realm = realm1, session = 7053397799995983, authid = T2ePzBr/NtvE6uyn1JV7VrfM, authrole = anonymous, authmethod = anonymous) # <-- session issued when joining
2016-01-26T17:38:41+0100 calling info
2016-01-26T17:38:41+0100 TX WAMP CALL Message (request = 1, procedure = com.myapp.info, args = (), kwargs = {}, timeout = None, receive_progress = None, disclose_me = True)
2016-01-26T17:38:41+0100 RX WAMP RESULT Message (request = 1, args = ['SessionDetails(realm = realm1, session = 4075389642691519, authid = None, authrole = authenticator, authmethod = None)'], kwargs = None, progress = None)
2016-01-26T17:38:41+0100 call result: SessionDetails(realm = realm1, session = 4075389642691519, authid = None, authrole = authenticator, authmethod = None) # <-- again getting back a different session id
2016-01-26T17:38:41+0100 calling login
2016-01-26T17:38:41+0100 TX WAMP CALL Message (request = 2, procedure = com.myapp.login, args = (), kwargs = {'user': 'peter', 'password': '123secret'}, timeout = None, receive_progress = None, disclose_me = True)
2016-01-26T17:38:41+0100 RX WAMP RESULT Message (request = 2, args = [None], kwargs = None, progress = None)
2016-01-26T17:38:41+0100 call result: None

Gareth Bult

unread,
Jan 28, 2016, 6:27:05 PM1/28/16
to Crossbar
Hi Sieben,

I share your concerns .. :) .. there as been some discussion on this topic, not least as it used to work differently, and when it changed to it's current form it broke lots of code. (and made things far more difficult to work with) What I'm doing now is working with a custom authenticator and on login, writing the session id AND the auth id into a shared database table. Then when other parts of the application need to know who the caller is, they do a database lookup based on the session id to get the authid back. This (technically, to my way of thinking) is "horrible" , but it's the only option I've come across at the moment. If/when the "authid" becomes available again, I'll switch back, but for now as a solution, "it works". I can see the logic in not wanting to carry more information in each request than is necessary (i.e. just the session id) but this "should" be the choice of the application, rather than something forced on the developer. Session ID's make sense for streaming data where bandwidth is critical, but for one transaction every few seconds, I'd far rather have the auth ID than do all the additional database work ...

gareth.

fyi; I'm using MongoDB as the database ..

...

sieben tupel

unread,
Jan 29, 2016, 7:57:06 AM1/29/16
to Crossbar
Hi Gareth,

thanks for the info. Its very unfortunately indeed, that the details arguments works this way.

I have thought of giving the session id as additional parameter to the user credentials in the login endpoint. But I'm afraid this will work only with a lot of unnecessary overhead in my case: A client joins my realm as an anonymous user and then calls the login procedure. The procedure then issues a token and the client leaves the realm and re-joins it, but now using the actual role it is supposed to have (e.g. 'user'). The client then authenticates using the token it got from the login procedure. When the client leaves and re-joins the realm it gets a new session including a new session id. Now my authenticator knows only the old session id linked with the token, but it does not know the current session id of the client. Now i could create another endpoint that can update the session id, but that is a lot of overhead and - in my opinion - not a very good design. (yet it looks like it is the only way to go...) 


Gareth Bult

unread,
Jan 29, 2016, 8:29:06 AM1/29/16
to Crossbar
Hi Sieben,

I do this too with my database table mechanism .. seems to work well for me, databse complexity aside .. but rather than using the "anonymous" feature, I actually use a real user called "guest" which essentially logs in with a dummy / common password. Then in addition to the custom authentication, I use custom "authorization" to limit what topics any given user can access. I found trying to do all this via roles and the json config overly complex, doing it in an authorizer is actually much easier.

Note I'm writing to the DB based on the "on Join" event, rather than as part of the authentication itself ...

Maybe this is useful;
    def _init__(self):
        self.mongo = MongoClient()

    @inlineCallbacks
   
def session_join_event(self,event):
       
""" login has been successful """
        log
.msg("{}> Join ({}) as ({})".format(event['session'],event['authid'],event['authrole']))
        session
= event.get('session',None)
        authid
= event.get('authid',None)
       
if not (session and authid):
            log
.msg('% ERROR - missing session or authid')
           
return
        record
= yield self.mongo.ionman.users.find_one({'authid':authid})
       
if not record:
            log
.msg("% ERROR - missing user record")
           
return
        update
= {
           
'authid'    : authid,
           
'session'   : session,
           
'when'      : datetime.now()
       
}
       
yield self.mongo.ionman.sessions.update({'session':session},{'$set':update},upsert=True)

    @inlineCallbacks
   
def session_leave_event(self,session_id):
       
""" come here when the session terminates """
        log
.msg("{}> Leave".format(session_id))
       
yield self.mongo.ionman.sessions.remove({'session':session_id})

   
@wamp.register(u'ionman.security.authorize')
   
def security_authorize(self,session, uri, action):
       
""" we need to add checks here to validate the caller is allowed """
        log
.msg('{}> Authorize - {}({})'.format(session['session'],action,uri))
       
# --- all your user control ACL code can go here ---
       
return True

Tobias Oberstein

unread,
Jan 29, 2016, 9:06:09 AM1/29/16
to cross...@googlegroups.com
Hi Sieben,

fwiw, caller/publisher details will be extended to (sessionid, authid,
authrole) in CB 0.13.

Unfort., it won't make it into 0.12 .. which will be released the coming
days.

Cheers,
/Tobias

Am 29.01.2016 um 13:57 schrieb sieben tupel:
> Hi Gareth,
>
> thanks for the info. Its very unfortunately indeed, that the /details/
> --
> You received this message because you are subscribed to the Google
> Groups "Crossbar" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to crossbario+...@googlegroups.com
> <mailto:crossbario+...@googlegroups.com>.
> To post to this group, send email to cross...@googlegroups.com
> <mailto:cross...@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/crossbario/6e46ac68-8d78-4fc6-a18a-854a1fe956b0%40googlegroups.com
> <https://groups.google.com/d/msgid/crossbario/6e46ac68-8d78-4fc6-a18a-854a1fe956b0%40googlegroups.com?utm_medium=email&utm_source=footer>.
> For more options, visit https://groups.google.com/d/optout.

Tobias Oberstein

unread,
Jan 29, 2016, 9:11:12 AM1/29/16
to cross...@googlegroups.com
Hi Gareth,

not sure why avoid to use authmethod "anonymous" (and use a dummy
password with WAMP-CRA?) .. if it's because there is no dynamic
authenticator available with that authmethod, that will be available in
0.12:

https://github.com/crossbario/crossbarexamples/tree/master/authentication/anonymous/dynamic

In fact, I've recently been going over all the authmethods (Python 3
bugs and the like), and 0.12 will have two completely authmethods as well:

- TLS client certificates
- WAMP-cryptosign

The latter really rocks IMO;) It's Curve25519 based. State of the art
stuff. We'll be using it instead of WAMP-CRA in the future for own apps.

Checkout the entry page
https://github.com/crossbario/crossbarexamples/tree/master/authentication for
an overview .

Cheers,
/Tobias
> yieldself.mongo.ionman.sessions.update({'session':session},{'$set':update},upsert=True)
>
> @inlineCallbacks
> defsession_leave_event(self,session_id):
> """ come here when the session terminates """
> log.msg("{}> Leave".format(session_id))
> yieldself.mongo.ionman.sessions.remove({'session':session_id})
>
> @wamp.register(u'ionman.security.authorize')
> defsecurity_authorize(self,session,uri,action):
> """ we need to add checks here to validate the caller is allowed """
> log.msg('{}> Authorize -
> {}({})'.format(session['session'],action,uri))
> # --- all your user control ACL code can go here ---
> returnTrue
> |
>
>
>
>
> On Friday, 29 January 2016 12:57:06 UTC, sieben tupel wrote:
>
> Hi Gareth,
>
> thanks for the info. Its very unfortunately indeed, that the
> /details/ arguments works this way.
>
> I have thought of giving the session id as additional parameter to
> the user credentials in the login endpoint. But I'm afraid this will
> work only with a lot of unnecessary overhead in my case: A client
> joins my realm as an anonymous user and then calls the login
> procedure. The procedure then issues a token and the client leaves
> the realm and re-joins it, but now using the actual role it is
> supposed to have (e.g. 'user'). The client then authenticates using
> the token it got from the login procedure. When the client leaves
> and re-joins the realm it gets a new session including a new session
> id. Now my authenticator knows only the old session id linked with
> the token, but it does not know the current session id of the
> client. Now i could create another endpoint that can update the
> session id, but that is a lot of overhead and - in my opinion - not
> a very good design. (yet it looks like it is the only way to go...)
>
>
> --
> You received this message because you are subscribed to the Google
> Groups "Crossbar" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to crossbario+...@googlegroups.com
> <mailto:crossbario+...@googlegroups.com>.
> To post to this group, send email to cross...@googlegroups.com
> <mailto:cross...@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/crossbario/51824a28-86f0-49f5-97d1-028d2da869d4%40googlegroups.com
> <https://groups.google.com/d/msgid/crossbario/51824a28-86f0-49f5-97d1-028d2da869d4%40googlegroups.com?utm_medium=email&utm_source=footer>.

Gareth Bult

unread,
Jan 29, 2016, 11:05:37 AM1/29/16
to Crossbar
Ok, the crypto stuff looks quite cool.
When I get a break I'll take a look in more depth, but unless there's a specific security issue with WAMP-CRA I think I'm stuck with the current code for now, at least on the current project.
(which is effectively a website)

I think from memory my aversion to "anonymous" was linked to the "authid" issue.
For consistent operation, I need to link a session id to a user id, and there was a problem making this link for an anonymous user, I guess because the link is made with a custom authenticator.
Sounds like this problem will go away with new versions ..

Bear in mind I've not been able to do an update to Crossbar for a while, the version I'm on it the most recent that doesn't break if I try to use it.
(inconsistencies between published versions of Crossbar and Autobahn)

Now I've entered live testing, I'm not going to be updating Crossbar for the moment on the main instances, I'll need to set up "another" QA environment for this when there's a potential candidate available.

Do know if there's any movement on the WSGI support in Python 3?

Gareth.

Tobias Oberstein

unread,
Jan 29, 2016, 1:01:27 PM1/29/16
to cross...@googlegroups.com

wsgi py3: yes, fixed https://twistedmatrix.com/trac/ticket/7993

not in release I think .. ping hawkie .. she has done the porting and is Twisted release mgr

wamp-cra: no security issues known.

wamp-cryptosign is brings a whole different level of security though. beginning with: no secrets on the server side at all! it's a public-private key method. safeguards against credential forwarding. elliptic curves .. but NO nist/nsa tuned ones;) etcetc

but 0.12 will bring alpha of sth even more awesome: wamp-cryptobox.

this is end-to-end encryption for app payload. you can make it that even CB is unable to read your call args/kwargs payload at all!

means: even if someone breaks into your router (or the nsa asks for a backdoor or the data), your app payload is safe!

and the best: 2 lines code at one place in your app to activate .. fully transparent otherwise.

I will write more about that when doing the release. personally, I think this is one of the coolest new features ..

Sent from Mobile (Google Nexus 5)

--
You received this message because you are subscribed to the Google Groups "Crossbar" group.
To unsubscribe from this group and stop receiving emails from it, send an email to crossbario+...@googlegroups.com.
To post to this group, send email to cross...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/crossbario/02b2abce-7f7b-4997-99fe-32d331764289%40googlegroups.com.

Gareth Bult

unread,
Jan 29, 2016, 1:17:25 PM1/29/16
to Crossbar
Ok, will do, looks like it might be usable now, would be good to get onto V3 .. :)

Crypto does sound very cool / useful, will have a read ... :)

sieben tupel

unread,
Jan 30, 2016, 11:47:53 AM1/30/16
to Crossbar
thanks for the additional information. I'm doing authentication (and authorization) much like you do Gareth. Instead of MongoDB I'm using Postgres with a homebrew object relational mapper i build on top of sqlalchemy and alchimia (when using alchimia for asynchronous db interaction with twisted, many functions of sqlalchemy are not net supported, so i build my own class to table mapping mechanism. it's not as powerful or complete as the sqlalchemy mappings but its much more comfortable than using the bare metal declarative style sqlalchemy provides)

How do you handle switching from a user authenticated as 'guest' to a new session with the actual role the user gets? Is there any nice and special mechanism build in crossbar/autobahn to do this or do i have to close my ApplicationSession and then start a new one manually?

The new authentication mechanisms look very nice, but in my setup i can not use them (yet) as i have to use and existing token based authentication and authorization service which is tied very deep in our existing platform.

I will use the tokens for authorization too, but i need a little more graduated authorization besides which client may access which procedure etc. E.g. having an endpoint which a student can call to get his grades can be called by all students, but a student can only get his own grades, so i need an additional authorization step inside my endpoint. (Here i will use our existing authorization service and extend it to do dynamic authentication for crossbar too)

Gareth Bult

unread,
Jan 30, 2016, 5:01:20 PM1/30/16
to Crossbar
Ok, my mechanism may look a little "dirty", but in practice it works very well. Assuming you have "guest" connection already running I have;

 authenticate: function() {
    ionman
.debug('authenticate','Attempting to log in');
    ionman
.new_credentials = ionman.credentials;
    ionman
.new_credentials.authid = $('[name=login-username]','#login-form').val();
    ionman
.new_credentials.password = $.md5($('[name=login-password]','#login-form').val());
    ionman
.new_connection = new autobahn.Connection(ionman.new_credentials);
    ionman
.new_connection.onopen = ionman.connection_open_replace;
    ionman
.new_connection.onclose = ionman.connection_close;
    ionman
.new_connection.open();
 
},

I use "new_connection" so I don't upset the current connection, just in case the login fails(!) , so I'm actually opening a second connection.

Then I have;

 connection_open_replace: function(session,details) {
    ionman
.debug('open','Session opened (replace)');
    ionman
.connection.close();
    ionman
.connection = ionman.new_connection;
    ionman
.credentials = ionman.new_credentials;
    ionman
.session = session;
 
},

i.e. if the new authenticated connection is successful, it closes the first connection and supplants itself in it's place ...
Reply all
Reply to author
Forward
0 new messages