broker/README.md

396 lines
12 KiB
Markdown
Raw Permalink Normal View History

2020-02-03 17:04:07 +00:00
## Broker - Real-time BaaS (Backend as a Service)
2019-12-16 18:04:14 +00:00
2020-01-01 05:09:38 +00:00
[![crates.io](https://meritbadge.herokuapp.com/broker)](https://crates.io/crates/broker)
2019-12-31 20:30:58 +00:00
2020-01-01 02:28:56 +00:00
### Purpose
2021-03-20 09:51:36 +00:00
The purpose of this service is to be your real-time BaaS (Backend as a Service).
2020-01-01 02:28:56 +00:00
2020-01-01 20:04:51 +00:00
Broker is a SSE message broker that requires you write no backend code to have a full real-time API.
Broker is born from the need that rather than building a complex REST API with web-sockets and a SQL database to provide reactive web forms (like for React) there must be a simpler way.
2020-01-01 02:28:56 +00:00
2020-01-01 05:09:38 +00:00
Broker follows an insert-only/publish/subscribe paradigm rather than a REST CRUD paradigm.
2020-01-01 02:28:56 +00:00
2021-04-11 02:22:30 +00:00
Broker also provides full identity services using JWT, HTTP Basic, Two Factor, and TOTP.
2021-04-11 02:14:18 +00:00
2021-04-15 01:02:31 +00:00
Broker is a competitor to [Firebase](https://firebase.google.com/), [Parse Server](https://github.com/parse-community/parse-server), [Auth0](https://auth0.com), [AWS Cognito](https://aws.amazon.com/cognito/), [AWS IAM](https://aws.amazon.com/iam/), [AWS SimpleDB](https://aws.amazon.com/simpledb/), and [AWS SNS](https://aws.amazon.com/sns/).
2021-04-11 02:14:18 +00:00
2020-01-11 05:14:37 +00:00
### Features
2021-03-20 21:59:22 +00:00
* Very performant with almost no CPU and memory usage
2021-04-05 21:31:05 +00:00
* Under 1000 lines of code
2020-01-26 10:28:55 +00:00
* Secure Real-time Event Stream via SSE - requires the use of [broker-client](https://www.npmjs.com/package/broker-client)
2020-02-05 10:06:02 +00:00
* Supports CORS
2021-04-08 23:40:00 +00:00
* JSON API
2021-03-23 22:11:41 +00:00
* Add users with admin token permission
* Multi-tenant
* Supports SSL - full end-to-end encryption
2021-03-25 20:19:27 +00:00
* Provides user authentication with JWTs or HTTP Basic
2021-04-08 23:40:00 +00:00
* Issues JWTs for authentication (username) and authorization (scopes) for external services
2021-04-13 00:26:29 +00:00
* Uses [biscuit](https://crates.io/crates/biscuit-auth) for user authorization scoping
2021-04-16 01:10:58 +00:00
* Verify endpoint for external services like [portal](https://crates.io/crates/portal) and [files](https://crates.io/crates/files)
2021-04-08 23:40:00 +00:00
* Secure password storage with Argon2 encoding
2021-04-09 07:01:39 +00:00
* Uses Global NTP servers and doesn't rely on your local server time for JWT expiry timing and Two Factor timing
2020-01-11 05:14:37 +00:00
* Sync latest events on SSE client connection
2021-04-07 00:25:09 +00:00
* Auto-provision and renews SSL cert via LetsEncrypt or use your own SSL cert
2021-04-08 23:40:00 +00:00
* User Management API endpoints (create, revoke, unrevoke, list, get, update)
* User Email Address Validation (regex and blacklist check against throwaway emails) using [mailchecker](https://crates.io/crates/mailchecker)
2021-04-07 03:21:05 +00:00
* Password Strength Checker using [zxcvbn](https://crates.io/crates/zxcvbn)
2021-04-09 07:01:39 +00:00
* Two Factor Authenication with QR code generation for Google Authenticator, Authy, etc.
* Secure user password resets with a TOTP with a configurable time duration
2020-01-04 09:03:18 +00:00
2020-01-01 06:35:44 +00:00
### How it works
2021-03-20 09:25:29 +00:00
In Broker you create a user, login, then insert an event with its data. Broker then publishes the event via SSE.
2020-01-01 02:28:56 +00:00
2020-01-06 07:24:24 +00:00
When the client first subscribes to the SSE connection all the latest events and data is sent to the client. Combined with sending the latest event via SSE when subscribed negates the necessity to do any GET API requests in the lifecycle of an event.
2020-01-01 02:28:56 +00:00
2021-03-20 09:25:29 +00:00
The side-effect of this system is that the latest event is the schema. This is pure NoSQL as the backend is agnostic to the event data.
2020-01-01 02:28:56 +00:00
2020-01-13 19:13:39 +00:00
### Recommeded Services/Libraries to use with Broker
2020-01-26 10:28:55 +00:00
* [broker-client](https://www.npmjs.com/package/broker-client) - the official front-end client for broker
2020-01-26 12:48:30 +00:00
* [broker-hook](https://www.npmjs.com/package/broker-hook) - the official react hook for broker
2020-01-13 19:13:39 +00:00
* [React Hook Form](https://react-hook-form.com/) - Best form library for React
* [React Debounce Input](https://www.npmjs.com/package/react-debounce-input) - React input for Real-time Submission (Edit in Place forms)
2021-03-20 09:25:29 +00:00
### Use
2020-01-24 16:12:28 +00:00
#### Step 1 - create a user
2020-01-07 19:58:46 +00:00
2020-01-06 10:44:37 +00:00
```html
2021-04-05 19:11:07 +00:00
POST /create_user
2020-01-06 10:44:37 +00:00
```
- public endpoint
2020-01-06 07:24:24 +00:00
```json
2021-04-05 19:11:07 +00:00
{
"username": "bob",
"password": "password1",
"admin_token": "letmein",
2021-04-05 21:30:22 +00:00
"tenant_name": "tenant_1",
"email": "bob@hotmail.com",
2021-04-09 07:01:39 +00:00
"two_factor": true,
2021-04-08 23:40:00 +00:00
"scopes": ["news:get", "news:post"],
2021-04-05 21:30:22 +00:00
"data": {
"name": "Robert Wieland",
"image": "https://img.com/bucket/123/123.jpg"
}
2021-04-05 19:11:07 +00:00
}
2020-01-06 07:24:24 +00:00
```
2021-04-05 21:30:22 +00:00
- `admin_token` is required and can be set in the command args - it is for not allowing everyone to add a user - the default is `letmein`
2021-04-09 07:01:39 +00:00
- `email`, `scopes`, `two_factor`, and `data` are optional fields
2021-04-13 00:26:29 +00:00
- `scopes` are [biscuit](https://crates.io/crates/biscuit-auth) authority scopes/facts so the first part before the colon is the resource while the second part after the colon is the operation. Don't add any additional colons in the scopes.
2020-01-06 07:24:24 +00:00
2021-03-20 09:56:40 +00:00
will return `200` or `500` or `400`
2020-01-07 00:22:22 +00:00
2021-04-13 00:26:29 +00:00
#### For JWT Auth: Step 2 - login with the user
2020-01-07 19:58:46 +00:00
2020-01-06 10:44:37 +00:00
```html
2020-01-07 21:21:38 +00:00
POST /login
2020-01-06 10:44:37 +00:00
```
- public endpoint
2020-01-06 07:24:24 +00:00
```json
2021-04-05 19:11:07 +00:00
{
"username": "bob",
2021-04-09 07:01:39 +00:00
"password": "password1",
"totp": "123456",
2021-04-05 19:11:07 +00:00
}
2020-01-06 07:24:24 +00:00
```
2021-04-09 07:03:36 +00:00
- `totp` is required if two factor is enabled for the user - if not the field can be omitted
2020-01-06 07:24:24 +00:00
2021-04-11 02:14:18 +00:00
will return: `200` or `500` or `400` or `401`
200 - will return a JWT
2020-01-06 07:24:24 +00:00
```json
2021-04-05 19:11:07 +00:00
{
"jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MTc2NzQ5MTUsImlhdCI6MTYxNzU4ODUxNSwiaXNzIjoiRGlzcGF0Y2hlciIsInN1YiI6ImZvbyJ9.OwiaZJcFUC_B0CA0ffRZVTWKRf5_vQ7vt5USNJEeKRE"
}
2020-01-06 07:24:24 +00:00
```
2021-04-08 23:42:33 +00:00
- note: if you need to debug your JWT then visit [jwt.io](https://jwt.io)
2020-01-06 07:24:24 +00:00
2021-04-11 02:14:18 +00:00
2020-01-26 10:28:55 +00:00
#### Step 3 - connect to SSE
2020-01-07 19:58:46 +00:00
2020-01-06 10:44:37 +00:00
```html
2021-03-20 09:25:29 +00:00
GET /sse
2020-01-06 10:44:37 +00:00
```
2021-03-25 20:19:27 +00:00
- authenticated endpoint (Authorization: Bearer {jwt}) or (Authorization: Basic {username:password})
2020-01-26 10:28:55 +00:00
- connect your sse-client to this endpoint using [broker-client](https://www.npmjs.com/package/broker-client)
2021-03-20 18:23:37 +00:00
- `note`: broker-client uses fetch as eventsource doesn't support headers
2020-01-26 10:28:55 +00:00
#### Step 4 - insert an event
2020-01-04 09:03:18 +00:00
2020-01-06 10:44:37 +00:00
```html
2020-01-07 21:21:38 +00:00
POST /insert
2020-01-06 10:44:37 +00:00
```
2021-03-25 20:19:27 +00:00
- authenticated endpoint (Authorization: Bearer {jwt}) or (Authorization: Basic {username:password})
2020-01-04 09:03:18 +00:00
```json
2021-04-05 19:11:07 +00:00
{
"event": "test",
"data": {
"name": "robert",
"image": "https://img.com/bucket/123/123.jpg"
}
}
2020-01-04 09:03:18 +00:00
```
2021-03-20 09:56:40 +00:00
will return: `200` or `500` or `400` or `401`
2020-01-06 10:44:37 +00:00
2021-04-03 00:18:45 +00:00
#### Optional - verify user
```html
GET /verify
```
- authenticated endpoint (Authorization: Bearer {jwt}) or (Authorization: Basic {username:password})
- verifies that the user is authenticated on broker - used for external services like [portal](https://crates.io/crates/portal)
will return: `200` or `500` or `401`
2021-04-16 20:41:17 +00:00
200 - will return a biscuit public key, biscuit token, username, and JWT expiry for your microservice (use from_bytes to hydrate the key and token)
2021-04-13 00:26:29 +00:00
```json
{
"key": [136,133,229,196,134,20,240,80,159,158,154,20,57,35,198,7,156,160,193,224,174,209,51,150,27,86,75,122,172,24,114,66],
2021-04-13 22:29:11 +00:00
"token": [122,133,229,196,134,20,240,80,159,158,154,20,57,35,198,7,156,160,193,224,174,209,51,150,27,86,75,122,172,24,114,121],
2021-04-16 20:41:17 +00:00
"expiry": 1618352841,
"username": "bob",
2021-05-01 00:58:29 +00:00
"scopes": ["news:get", "news:post"]
2021-04-13 00:26:29 +00:00
}
```
2021-04-05 19:11:07 +00:00
#### Optional - revoke user
```html
POST /revoke_user
```
- public endpoint
```json
{
"admin_token": "letmein",
"username": "bob"
}
```
will return: `200` or `500` or `400` or `401`
- note: revoked users cannot login
#### Optional - unrevoke user
```html
POST /unrevoke_user
```
- public endpoint
```json
{
"admin_token": "letmein",
"username": "bob"
}
```
will return: `200` or `500` or `400` or `401`
#### Optional - list users
```html
POST /list_users
```
- public endpoint
```json
{
2021-05-01 00:58:29 +00:00
"admin_token": "letmein"
2021-04-05 19:11:07 +00:00
}
```
will return: `200` or `500` or `400` or `401`
200 - will return an array of objects
```json
[
{
"id": "69123c04-fa42-4193-a6c5-ab2fc27658b1",
"password": "***",
2021-04-09 07:01:39 +00:00
"totp": "***",
2021-04-05 19:11:07 +00:00
"revoked": false,
"tenant_name": "tenant_1",
2021-04-05 21:30:22 +00:00
"username": "bob",
"email": "bob@hotmail.com",
2021-04-08 23:40:00 +00:00
"scopes": ["news:get", "news:post"],
2021-04-05 21:30:22 +00:00
"data": {
"name": "Robert Wieland",
"image": "https://img.com/bucket/123/123.jpg"
}
2021-04-05 19:11:07 +00:00
}
]
```
2021-04-09 07:01:39 +00:00
- note: `email`, `scopes`, `two_factor`, and `data` can be `null`
2021-04-05 19:11:07 +00:00
#### Optional - get user
```html
POST /get_user
```
- public endpoint
```json
{
"admin_token": "letmein",
"username": "bob"
}
```
will return: `200` or `500` or `400` or `401`
200 - will return an array of objects
```json
{
"id": "69123c04-fa42-4193-a6c5-ab2fc27658b1",
"password": "***",
2021-04-09 07:01:39 +00:00
"totp": "***",
2021-04-05 19:11:07 +00:00
"revoked": false,
"tenant_name": "tenant_1",
2021-04-05 21:30:22 +00:00
"username": "bob",
"email": "bob@hotmail.com",
2021-04-08 23:40:00 +00:00
"scopes": ["news:get", "news:post"],
2021-04-05 21:30:22 +00:00
"data": {
"name": "Robert Wieland",
"image": "https://img.com/bucket/123/123.jpg"
}
}
```
2021-04-09 07:01:39 +00:00
- note: `email`, `scopes`, `two_factor`, and `data` can be `null`
2021-04-05 21:30:22 +00:00
#### Optional - update user
```html
POST /update_user
```
- public endpoint
```json
{
"admin_token": "letmein",
"username": "bob",
"tenant_name": "tenant_2",
"password": "new_password",
"email": "bober@hotmail.com",
2021-04-08 23:40:00 +00:00
"scopes": ["news:get", "news:post"],
2021-04-05 21:30:22 +00:00
"data": {
"name": "Robert Falcon",
"image": "https://img.com/bucket/123/1234.jpg"
}
2021-04-05 19:11:07 +00:00
}
```
2021-04-08 23:54:14 +00:00
- note: `tenant_name`, `password`, `email`, `scopes`, `data` are optional fields
2021-04-05 21:30:22 +00:00
will return: `200` or `500` or `400` or `401`
2021-04-03 00:18:45 +00:00
2021-04-07 00:26:24 +00:00
#### Optional - Health Check
```html
GET or HEAD /
```
- public endpoint
will return: `200`
2021-04-09 07:01:39 +00:00
#### Optional - generate two factor QR Code
```html
POST /create_qr
```
- public endpoint
```json
{
"issuer": "Broker",
"admin_token": "letmein",
"username": "bob"
}
```
- note: put the name of your application in the issuer field
- note: the ID of the QR will be the user's username and your issuer field
will return: `200` or `500` or `400` or `401`
200 - will return the qr code in PNG format in base64
```json
{
"qr": "dGhpc2lzYXN0cmluZw=="
}
```
#### Optional - create totp
```html
POST /create_totp
```
- public endpoint
```json
{
"admin_token": "letmein",
2021-05-01 00:58:29 +00:00
"username": "bob"
2021-04-09 07:01:39 +00:00
}
```
will return: `200` or `500` or `400` or `401`
200 - will return the totp
```json
{
"totp": "622346"
}
```
- note: these TOTPs can only be used with the password reset endpoint
#### Optional - user password reset
```html
POST /password_reset
```
- public endpoint
```json
{
"totp": "622346",
"username": "bob",
"password": "password1"
}
```
will return: `200` or `500` or `400` or `401`
2021-03-20 09:25:29 +00:00
### Install
2020-01-08 00:19:18 +00:00
2020-01-24 16:12:28 +00:00
``` cargo install broker ```
2020-02-05 05:29:55 +00:00
2021-04-07 00:25:09 +00:00
- the `origin` can be passed in as a flag - default `*`
- the `port` can be passed in as a flag - default `8080` - can only be set for unsecure connections
2021-04-13 22:29:11 +00:00
- the `jwt_expiry` for jwts can be passed in as a flag in seconds - default `86400`
2021-04-07 00:25:09 +00:00
- the `jwt_secret` for jwts should be passed in as a flag - default `secret`
- the `secure` flag for https and can be true or false - default `false`
- the `auto_cert` flag for an autorenewing LetsEncrypt SSL cert can be true or false - requires a resolvable domain - default `true`
- the `key_path` flag when `auto_cert` is `false` to set the SSL key path for your own cert - default `certs/private_key.pem`
- the `cert_path` flag when `auto_cert` is `false` to set the SSL cert path for your own cert - default `certs/chain.pem`
- the `certs` flag is the storage path of LetsEncrypt certs - default `certs`
- the `db` flag is the path where the embedded database will be saved - default `db`
- the `domain` flag is the domain name (e.g. api.broker.com) of the domain you want to register with LetsEncrypt - must be fully resolvable
- the `admin_token` flag is the password for the admin to add users - default `letmein`
2021-04-07 03:21:05 +00:00
- the `password_checker` flag enables zxcvbn password checking - default `false`
2021-04-09 07:01:39 +00:00
- the `totp_duration` flag is the duration of the TOTP for user generated password reset - default 300 seconds (5 min)
2021-04-07 03:21:05 +00:00
- production example: `./broker --secure="true" --admin_token"23ce4234@123$" --jwt_secret="xTJEX234$##$" --domain="api.broker.com" --password_checker="true"`
2021-03-20 09:25:29 +00:00
2021-03-22 05:12:39 +00:00
### Service
There is an example `systemctl` service for Ubuntu called `broker.service` in the code
2019-12-31 20:29:09 +00:00
2021-03-20 09:25:29 +00:00
### TechStack
2020-01-02 14:31:57 +00:00
2021-03-20 09:25:29 +00:00
* [Tide](https://crates.io/crates/tide)
* [RocksDB](https://crates.io/crates/rocksdb)
2020-01-02 14:31:57 +00:00
2020-01-01 03:55:10 +00:00
### Inspiration
2021-04-05 19:11:07 +00:00
* [Auth0](https://auth0.com)
2020-01-01 03:55:10 +00:00
* [React Hooks](https://reactjs.org/docs/hooks-intro.html)
* [Meteor](https://meteor.com)
* [MongoDB](https://www.mongodb.com/)
* [Pusher](https://pusher.com)
* [Event Sourcing](https://microservices.io/patterns/data/event-sourcing.html)
* [Best in Place](https://github.com/bernat/best_in_place)
* [Brock Whitten](https://www.youtube.com/watch?v=qljYMEfVukU)