gammazero / nexus

Full-feature WAMP v2 router and client written in Go
MIT License
267 stars 58 forks source link

ticket dynamic authentication like crossbar.io #125

Open goldcode88 opened 6 years ago

goldcode88 commented 6 years ago

how to implement ticket dynamic? "user.token.authenticate.d10" is provided by client in autobahn.py. In nexus, I think "user.token.authenticate.d10" should be implemented in nexusd router. But no example can be referenced.

crossbar.io config.json partially.

"auth": { "anonymous": { "type": "static", "role": "public" }, "ticket": { "type": "dynamic", "authenticator": "user.token.authenticate.d10" }, "wampcra": { "type": "static", "users": { "authenticator": { "secret": "**", "role": "authenticator" }, "usermanagement": { "secret": "****", "role": "backend" } } } }

gammazero commented 6 years ago

This is definitely something I need provide both examples and additional documentation for. Until then, probably the best help is to look at some of the test code.

Client that tests simple CR auth handler: https://github.com/gammazero/nexus/blob/master/aat/auth_test.go#L28

On the server side, a very simple CRAuthenticator is defined, and given a KeyStore: https://github.com/gammazero/nexus/blob/master/aat/main_test.go#L105-L108 This is then provided to the server: https://github.com/gammazero/nexus/blob/master/aat/main_test.go#L127

For a ticket authenticator, you can create your own or use the skeleton TicketAuthenticator provided with nexus (it is just a type of CRAuthenticator). Create a new TickerAuthenticator: https://github.com/gammazero/nexus/blob/master/router/auth/ticket.go#L23 ...and supply a KeyStore implementation to return the user info from where your application keeps it.

Here is a unit test that provides a ticket authenticator and a CR authenticator: https://github.com/gammazero/nexus/blob/master/router/auth/crauth_test.go#L116

The AuthKey method of the KeyStore returns the ticket: https://github.com/gammazero/nexus/blob/master/router/auth/crauth_test.go#L37

I will provide some examples and docs very soon.

Consider using TLS Generally, due to the need to protect ticket data, TLS is used to secure the client-server connections. If TLS is already in place, then the server can be configured to only trust certain client certificates. Doing that may remove the need to implement CR/Ticket authentication.

martin31821 commented 6 years ago

In our advanced router (to be open sourced soon) there is dynamic ticket authentication, as well as TLS authentication using a custom PKI, maybe you want to use this...

gammazero commented 6 years ago

@martin31821 I would be very interested to see how your authentication system fits with nexus.

I think the current ticket/CR authentication interface could be improved and made much easier to work with. TLS support is provided, but is basically what the golang stdlib provides, so having something that is better integrated into the rest of the auth stuff could be a good improvement.

martin31821 commented 6 years ago

I'lll see if I can get this into a good shape within the next week :) should work, we have currently docker images which are production ready

gammazero commented 6 years ago

@goldcode88 It looks like there is one thing nexus is missing to be able to provide dynamic ticket authentication as described in Crossbar. That is specifically the ability to make the ticket available in the session details after authentication success. If that is done, the the ticket can be used in the authorizer for the authorization of each message. I will fix this!

All of the other information available to Crossbar's dynamic authorization is also available in nexus. If you set EnableRequestCapture in the WebsocketServer then the authorizer will have access to all information from the websocket HTTP upgrade request.

goldcode88 commented 6 years ago

@gammazero @martin31821 Thanks! I have implemented wampcra just now. Autobahn js client can connect to nexus server by wampcra authmethods.

"wampcra": {
                            "type": "static",
                            "users": {
                                "authenticator": {
                                    "secret": "******************",
                                    "role": "authenticator"
                                },
                                "usermanagement": {
                                    "secret": "****************",
                                    "role": "backend"
                                }
                            }
                        }

I'll try to implement dynamic ticket authentication after your fix.

martin31821 commented 6 years ago

Just pushed it: https://github.com/EmbeddedEnterprises/autobahnkreuz. However, docs are still lacking, we're working on it. Docker image is available on docker hub, feel free to play with it.

goldcode88 commented 6 years ago

Thanks. wampcra's config is a little complex. I change testKeyStore to CraKeyStore. CraKeyStore is compatible with crossbar's wampcra model.

type testKeyStore struct {
    provider string
    secret   string
    ticket   string
    cookie   *http.Cookie

    authByCookie bool
}

// new struct
type CraKeyStore struct {
  provider string
  users    map[string]CraUsers
}

type CraUsers struct {
  role     string
  secret   string
  ticket   string
  cookie   *http.Cookie

  authByCookie bool
}

By the way, Crossbar's wampcra model has a problem. It can't support the following request.

"wampcra": [{
                            "type": "static",
                            "users": {
                                "authenticator": {
                                    "secret": "******************",
                                    "role": "authenticator"
                                },
                                "usermanagement": {
                                    "secret": "****************",
                                    "role": "backend"
                                }
                            }
                        },
                     {
                            "type": "dynamic",
                            ... ...
                      }
                    ]

Maybe nexus is easy to implement the model

gammazero commented 6 years ago

One note of caution... If putting ticket into session details, then it will probably be necessary to enable MetaStrict in the RealmConfig. At this point I am thinking that making MetaStrict=true be the default/only behavior and if it is necessary for additional values from the session meta API, then explicitly list these in MetaIncludeSessionDetails.

gammazero commented 6 years ago

@martin31821 The autobahnkreuz API looks like it will make things simpler for the user. I see that your AnonymousAuth is providing a list of authroles: https://github.com/EmbeddedEnterprises/autobahnkreuz/blob/master/auth/anon.go#L15 Is that something new in the WAMP spec or is this specific to your router?

I would like to integrate DynamicTicketAuth directly into nexus, if that is alright.

I still need a way to return the ticketObj, as mentioned to @goldcode88, so that it can be put into the session details. I could stash it in the Welcome.Details, under a special key that gets removed before the Welcome message is sent to the client, but that seems like a huge hack. I do not want to change the interface and break backward compatibility. I may need to create a place where extra details can be stored and retrieved later by the router, and provide additional API.

martin31821 commented 6 years ago

For the first part: the spec states that the authrole should be a string, but the session details are specified as dictionary, so any router should tolerate the list (this is what we tested). Basically it allows us to add features to our application with different authroles at runtime and (re-) assign privileges. I'd also like to keep support for list of authroles within nexus, if that's fine for you @gammazero.

All of our authproviders return lists of authroles. You can integrate the dynamic ticket auth into nexus, however our endpoints has not the same signature that is used in the crossbar router (that was changed in order to support keep-me-logged-in tokens).

I also think that crossbar does not store the ticket object anywhere within the router, since its only purpose is to assign authid/authrole to the client. IMHO storing the user passwords (==token) within the router seems like a security issue to me.

Edit: Our primary motivation to write autobahnkreuz was to provide a router which is super easy to use, solves real applications problems (broken binary serialization, keep-me-logged-in, easy setup of TLS between your microservices, integration into any existing authentication providers, feature based procedure access etc.)

gammazero commented 6 years ago

@martin31821 Yes, certainly want keep support for list of authroles. My concern was was about what what to return in session meta events: wamp.session.get specifically calls for authrole to be returned as string. What do you return? https://wamp-proto.org/static/rfc/draft-oberstet-hybi-crossbar-wamp.html#wampsessionget

martin31821 commented 6 years ago

The current implementation just returns the list, which is exactly what is desired. Maybe It would be feasible to add this to the spec, but I don't know what the maintainers think of this...

gammazero commented 6 years ago

@martin31821 Maybe there is a why to have it both ways? The session details could contain two different items: authrole and authroles. The first, authrole, would be the "effective" authrole and the second would be all the authroles that the client has membership in. This would be similar to the Unix group concept:

> id
uid=1001(ajg) gid=1001(ajg) groups=1001(ajg),0(wheel),20(staff)

An administrative meta procedure (wamp.modify.session_details perhaps) could be used to set the effective authrole, much like the Unix newgrp command.

martin31821 commented 6 years ago

@gammazero What's the advantage of having authrole and authroles? An authorizer would need to know both values anyway and it adds more complexity into the router implementation. Or do you think of any difference between the "primary" role and the other roles within the authroles dict?

For now, I'd just keep the implementation as-is...

gammazero commented 6 years ago

@martin31821 Yes, primary vs others is what I had in mind. Anyway, I think the current implementation is best since it does not force the session details to be any particular type, as long as they serialize. This allows whatever flexibility the router implementer needs.