3

I'm currently trying to implement a user notification system using Websockets via Crossbar/Autobahn. I have done multiple tests and gone through the documentation, however, I'm not sure if there's a solution to having the following workflow work:

  1. User signs in with web app -- this is done through JWT
  2. Frontend establishes a websocket connection to a running crossbar instance.
  3. Frontend attempts to subscribe to a URI specifically for the user's notifications: i.e. com.example.notifications.user.23 or com.example.user.23.notifications'. Where23` is the user id.
  4. User's JWT is checked to see if user is allowed to access subscription.
  5. When activity is generated and causes a notification, the backend publishes the user-specific URIs.

For step 3, I can't tell if the current support auth methods have what I need. Ideally, I would like an auth method which I can customize (in order to implement a JWT authenticator within Crossbar) that I can apply to a URI pattern, but NOT give access to the entire pattern to the subscribing user. This is partially solved by the dynamic auth methods, but is missing the latter half:

For example (my ideal workflow):

  1. User attempts to subscribe to a URI com.example.user.23.notifications.
  2. URI matches com.example.user..notifications (wildcard pattern in http://crossbar.io/docs/Pattern-Based-Subscriptions/)
  3. Auth token is validated and user is given access to only com.example.user.23.notifications.

Is the above achievable in a simple way? From what I can tell, it may only be possible if I somehow generate a .crossbar/config.json which contains URI permutations of all user ids...and automatically generate a new config for each new user -- which is completely not a reasonable solution.

Any help is appreciated!

ineedtosleep
  • 167
  • 1
  • 6

1 Answers1

3

Use authorizer.

See http://crossbar.io/docs/Authorization/#dynamic-authorization

Register a dynamic authorizer for the user role that session was assigned when joining/authenticating:

           {
              "name": "authorizer",
              "permissions": [
                {
                  "uri": "com.example.authorize",
                  "register": true
                }
              ]
            },
            {
              "name": "authenticator",
              "permissions": [
                {
                  "uri": "com.example.authenticate",
                  "register": true
                }
              ]
            },
            {
              "name": "user",
              "authorizer": "com.example.authorize"
            },
...
"components": [
    {
      "type": "class",
      "classname": "example.AuthenticatorSession",
      "realm": "realm1",
      "role": "authenticator",
      "extra": {
        "backend_base_url": "http://localhost:8080/ws"
      }
    },
    {
      "type": "class",
      "classname": "example.AuthorizerSession",
      "realm": "realm1",
      "role": "authorizer"
    }
  ]

Write a class

class AuthorizerSession(ApplicationSession):
    @inlineCallbacks
    def onJoin(self, details):
        print("In AuthorizerSession.onJoin({})".format(details))
        try:
            yield self.register(self.authorize, 'com.example.authorize')
            print("AuthorizerSession: authorizer registered")
        except Exception as e:
            print("AuthorizerSession: failed to register authorizer procedure ({})".format(e))

    def authorize(self, session, uri, action):
        print("AuthorizerSession.authorize({}, {}, {})".format(session, uri, action))
        if session['authrole'] == u'backend':  # backnend can do whatever
            return True
        [Authorization logic here]
        return authorized
ImplexOne
  • 549
  • 3
  • 7
  • 1
    Indeed that is it. I actually did try that previously, but I didn't realize that was being called on multiple events. The data provided in `session`, `uri`, and `action` are also not enough for my purposes, so I have hacked in passing URI data into `authorize`. – ineedtosleep Feb 26 '16 at 23:26