why deepstreamHub? compare us getting started feature roadmap faq
use cases pricing
products
developers
company
blog contact

Security in deepstreamHub is based on three interrelated concepts:

  • encrypted connections
  • user authentication
  • granular permissions

Here's how they work together:

Encrypted Connections

deepstreamHub supports transport layer security for web-facing connections Secure Web Sockets. WSS is the default protocol and will be part of your app-url.

Authentication

Every incoming connection needs to pass an authentication step. This happens when the client calls login( data, callback ). deepstreamHub offers a variety of ways to handle incoming authentication data:

  • Open Authentication allows every connection. Choose this option for public sites that don't require access controls.
  • Email Auth uses deepstreamHub's built-in user management capabilities. Users can be registered manualy via the dashboard or programmatically via the http api
  • HTTP Webhook contacts a configurable HTTP webhook to ask if a user is allowed to connect. This is the most flexible option as it allows you to write a tiny http server in any language that can connect to databases, active directories, oAuth providers or whatever else your heart desires.

Apart from just accepting / denying incoming connections, the authentication step can also provide two extra bits of information:

  • clientData is returned to the client upon login. This is useful to provide user specific settings, e.g. { "view": "author" } for a blog CMS.
  • serverData is kept privately on the server and can be used within Valve permissions whenever a client attempts an action. This makes certain security concepts possible, e.g. role based authentication.
"system-settings": {
    //publicly readable
    "read": true,
    //writable only by admin
    "write": "user.data.role === 'admin'"
}

Authentication FAQ

  • When exactly does authentication happen? When a deepstream client is created it establishes a connection straight away. The connection however remains in a quarantine state until login() is called. This makes sure that auth-data is sent over an encrypted connection and helps to get the at times time-consuming connection establishment out of the way while the user is busy typing passwords.
  • Can I read usernames for auth purposes out of a deepstream record? There's no built-in support for this, but it's easy to read data from deepstreamHub from within an HTTP Webhook.
  • Can a user connect more than once at the same time? Yes. The same user can connect multiple times from separate browser windows or devices.

Permissioning

Permissioning is the act of deciding whether a specific action, e.g. writing to a record or subscribing to an event is allowed. To help with this, deepstream uses an expressive, rule-based permissioning language called Valve. There's a lot to say about Valve. Here's a small Valve File that should give you a first impression, to learn more though head over to the simple valve tutorial

record:

  "*":
    create: true
    delete: true
    write: true
    read: true
    listen: true

  public-read-private-write/$userid:
    read: true
    create: "user.id === $userid"
    write: "user.id === $userid"

  only-increment:
    write: "!oldData.value || data.value > oldData.value"
    create: true
    read: true

  only-delete-egon-miller/$firstname/$lastname:
    delete: "$firstname.toLowerCase() === 'egon' && $lastname.toLowerCase() === 'miller'"

  only-allows-purchase-of-products-in-stock/$purchaseId:
    create: true
    write: "_('item/' + data.itemId ).stock > 0"

event:

  "*":
    listen: true
    publish: true
    subscribe: true

  open/"*":
    listen: true
    publish: true
    subscribe: true

  forbidden/"*":
    publish: false
    subscribe: false

  a-to-b/"*":
    publish: "user.id === 'client-a'"
    subscribe: "user.id === 'client-b'"

  news/$topic:
    publish: "$topic === 'tea-cup-pigs'"

  number:
    publish: "typeof data === 'number' && data > 10"

  place/$city:
    publish: "$city.toLowerCase() === data.address.city.toLowerCase()"

rpc:

  "*":
    provide: true
    request: true

  a-provide-b-request:
    provide: "user.id === 'client-a'"
    request: "user.id === 'client-b'"

  only-full-user-data:
    request: "typeof data.firstname === 'string' && typeof data.lastname === 'string'"