A secure-by-default rest api stack implemented with hyper, tokio, bb8 and postgres. This project is focused on providing end-to-end encryption by default for 12-factor applications looking to customize functionality using environment variables as needed. Includes a working user management and authentication backend written in postgresql with async S3 uploading for POST-ed data files.
- User authentication enabled by default and implemented with custom tls assets to encrypt all JWT tokens with storage in postgres.
- Users can upload and manage files stored on AWS S3 (assuming valid credentials are loaded outside this rust project).
- User password reset and user email change support using one-time-use tokens that are stored in postgres.
- User passwords are hashed using argon2.
- The hyper server hosts tls assets that can be re-generated with the tools in this repository.
- JWT encryption and decryption keys included and documentation for building new keys as needed.
- Includes a tls asset generator tool (./certs/generate-tls-assets.sh) for building self-signed tls assets (requires docker).
- The postgres database requires each client include the postgres tls certificate authority file for encrypting data in-transit.
- The rest api server utilizes postgres with a bb8 client threadpool.
- Includes pg4admin for database management in a browser (deployed with docker compose).
Component | Status |
---|---|
Rest API Server | Listening for encrypted client connections on tcp port 3000 |
JWT | Encrypting and decrypting tokens with ECDSA using SHA-256 |
Postgres | Listening for encrypted client connections on tcp port 5432 (tls Certificate Authority required) |
pgAdmin | Listening for encrypted HTTP client connections on tcp port 5433 |
AWS S3 | Encrypted at rest with AES256 |
git clone https://github.com/jay-johnson/restapi
cd restapi
The repository restapi includes default tls assets, but for security purposes you should generate your own. Please refer to the Generate TLS Assets doc for more information.
Here's how to generate them under the ./certs
directory:
cd certs
./generate-tls-assets.sh -f -c ./configs/dev-network.yml
cd ..
Authentication using JWT requires encrypting and decrypting using your own keys. Please refer to the How to build JWT private and public keys for the jsonwebtokens crate doc for more information.
Here's how to generate the jwt keys under the ./jwt
directory:
cd jwt
openssl ecparam -name prime256v1 -genkey -out private-key.pem
openssl pkcs8 -topk8 -nocrypt -in private-key.pem -out private-key-pkcs8.pem
openssl ec -in private-key.pem -pubout -out public-key.pem
cd ..
Please refer to the Build and Deploy a Secured Postgres backend doc for more information.
cargo build --example server
export RUST_BACKTRACE=1 && export RUST_LOG=info && ./target/debug/examples/server
Here are the supported json contracts for each Request
and Response
based off the url. Each client request is handled by the ./src/handle_requests.rs module and returned as a response back to the client (serialization using serde_json
)
Create a single users
record for the new user
- URL path:
/user
- Method:
POST
- Handler: create_user
- Request: ApiReqUserCreate
- Response: ApiResUserCreate
Update supported users
fields (including change user email and password)
- URL path:
/user
- Method:
PUT
- Handler: update_user
- Request: ApiReqUserUpdate
- Response: ApiResUserUpdate
Get a single user by users.id
- by default, a user can only get their own account details
- URL path:
/user/USERID
- Method:
GET
- Handler: get_user
- Request: ApiReqUserGet
- Response: ApiResUserGet
Delete a single users
record (note: this does not delete the db record, just sets the users.state
to inactive 1
)
- URL path:
/user
- Method:
DELETE
- Handler: delete_user
- Request: ApiReqUserDelete
- Response: ApiResUserDelete
Search for matching users
records in the db
- URL path:
/user/search
- Method:
POST
- Handler: search_users
- Request: ApiReqUserSearch
- Response: ApiResUserSearch
Create a one-time-use password reset token that allows a user to change their users.password
value by presenting the token
- URL path:
/user/password/reset
- Method:
POST
- Handler: create_otp
- Request: ApiReqUserCreateOtp
- Response: ApiResUserCreateOtp
Consume a one-time-use password and change the user's users.password
value to the new argon2-hashed password
- URL path:
/user/password/change
- Method:
POST
- Handler: consume_user_otp
- Request: ApiReqUserConsumeOtp
- Response: ApiResUserConsumeOtp
Consume a one-time-use verification token and change the user's users.verified
value verified (1
)
- URL path:
/user/verify
- Method:
GET
- Handler: verify_user
- Request: ApiReqUserVerify
- Response: ApiResUserVerify
Upload a local file on disk to AWS S3 asynchronously and store a tracking record in the users_data
table. The documentation refers to this as a user data
or user data file
record.
- URL path:
/user/data
- Method:
POST
- Handler: upload_user_data
- Request: ApiReqUserUploadData
- Response: ApiResUserUploadData
Update the users_data
tracking record for a file that exists in AWS S3
- URL path:
/user/data
- Method:
PUT
- Handler: update_user_data
- Request: ApiReqUserUpdateData
- Response: ApiResUserUpdateData
Search for matching records in the users_data
db based off the request's values
- URL path:
/user/data/search
- Method:
POST
- Handler: search_user_data
- Request: ApiReqUserSearchData
- Response: ApiResUserSearchData
Log the user in and get a json web token (jwt) back for authentication on subsequent client requests
- URL path:
/login
- Method:
POST
- Handler: login
- Request: ApiReqUserLogin
- Response: ApiResUserLogin
This project focused on integration tests for v1 instead of only rust tests (specifically everything has been tested with curl):
Please refer to the Integration Tests Using curl Guide