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'"