Skip to content
This repository has been archived by the owner on Feb 1, 2023. It is now read-only.
/ rust-spa-auth Public archive

Example application using a Vue frontend with Rust backend that has authentication + authorization.

License

Notifications You must be signed in to change notification settings

jtroo/rust-spa-auth

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

43 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Rust SPA + Auth

This project contains a Rust server that serves a single page application and has authentication + JWT-based authorization.

It was written as a learning exercise and can hopefully be a useful example for a Rust-backed website that uses authentication + authorization. It's a bit more complete and closer to prodution-ready than other example code I've seen online, e.g. JWT with warp.

Warning

Though I am somewhat informed, I am not a security expert. Don't deploy this code to production.

Demo video

2021-04-03.19-16-28.mp4

Dependencies

  • A recent version of Rust+Cargo (MSRV unknown)
  • A recent version of npm (minimum unknown)

Note regarding Warp

If you check Cargo.toml, you'll see that the warp dependency is my personal warp fork. This is due to waiting on my PR for more convenient rejection handling to be merged.

Notable content

Server

  • Rust with a Warp web server
  • Authentication using Argon2 password hashing to produce refresh token cookies
  • Authorization with 2 basic roles using JWT access tokens for claims
  • Optional CORS for more rapid client side development
  • Example for abstracting a data store with a trait
    • In-memory implementation exists

Client

  • Vue 2.X framework
  • Axios for API requests
  • Login
  • Logout
  • Conditionally visible UI components based on JWT claims
  • Automatic refreshing of access tokens on 403 error

I am not the most proficient client-side dev, so the structure of the client side code may not be what you want to emulate. The API requests using axios are probably the most useful to look at with regards to using the server APIs.

Note on server framework and async runtime

Most of the code is hopefully not closely tied to Warp framework details — most of the Warp-specific code is in routes.rs with a sprinkle in main.rs and error.rs. As long as the server framework used is async capable, the rest of it should be a decent starting point for use with other server frameworks.

Since the webserver uses Warp, the code uses the tokio runtime. Apart from the Warp related code, the auth module has a few instances where it is reliant on tokio. These are pretty minimal so it should be simple to adapt for webservers with another runtime, e.g. Tide.

Instances of tokio reliance:

  • init_default_users: uses block_on to run async code in a sync function
  • store_user: spawns a blocking task to run password hashing
  • authenticate: spawns a blocking task to run password verification
  • pretend_password_processing: uses tokio sleep
  • #[tokio::test] is used for async tests

Serve the SPA with Rust

The command sequence below uses an in-memory data store. To use sqlite, different commands are needed.

cd $(git rev-parse --show-toplevel)
./build-debug.sh
cd build-output
./rust-spa-auth

Serve the SPA separately

To serve the SPA and the server separately for more rapid client side code development, you can use the following commands:

Note - you may have to navigate to https://localhost:9090 manually and accept the certificate warning before this works.

Serve client files:

cd $(git rev-parse --show-toplevel)/client
npm run serve

Run server:

cd $(git rev-parse --show-toplevel)/server
cargo run --features in_memory,dev_cors
# Can omit `in_memory` from above, but a database will need to be specified
# e.g.
export DATABASE_URL=sqlite:///tmp/db.sql
cargo run --features dev_cors -- --database $DATABASE_URL

Example API Usage

You can check the API functionality without your browser using cURL.

See an example sequence below.

curl -v https://localhost:9090/api/login \
  --cacert tls/server.rsa.crt \
  -d '{"email": "user@localhost", "pw": "userpassword"}' \
  -H 'Content-Type: application/json'

# result is in set-cookie header:
# set-cookie: refresh_token=QpOddMUkW9wk/S4B.s/a3k3JttPFH3v4j43gxx7KL+3y05Opm1rjiQBV+07z9NXacLv8PeQn6DRDoblFDerGQ9qeUp1TpaNAg5f1cYtLf3t3xnvGkHUDW2TK/mDJr4A=="; Max-Age=2592000; path=/api/auth/access; Secure; HttpOnly; SameSite=Lax;


curl https://localhost:9090/api/auth/access \
  --cacert tls/server.rsa.crt \
  --cookie "refresh_token=QpOddMUkW9wk/S4B.s/a3k3JttPFH3v4j43gxx7KL+3y05Opm1rjiQBV+07z9NXacLv8PeQn6DRDoblFDerGQ9qeUp1TpaNAg5f1cYtLf3t3xnvGkHUDW2TK/mDJr4A=="

# result:
# eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJlbWFpbCI6InVzZXJAbG9jYWxob3N0Iiwicm9sZSI6InVzZXIiLCJleHAiOjE2MTY5MjY2NTd9.kj9GR-FPUVmZh2BEvGmbqg6tAz4lsjvLxtcTXOjdDXLwD0KGZ2NrDueuuyJ1Y4z8z98q9VcpDNHYjS4veM2hYw

curl https://localhost:9090/api/user \
  --cacert tls/server.rsa.crt \
  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJlbWFpbCI6InVzZXJAbG9jYWxob3N0Iiwicm9sZSI6InVzZXIiLCJleHAiOjE2MTcwNjUxMDJ9.imixaRk8YgoEv8Hh33qidty_jGBAo9ewIOd7vWqAjAHiN-MZJOFeSXg25nWx86SW9Pc_QFH_qlFYaSmPG_MfRA'

# result:
# user user@localhost

curl https://localhost:9090/api/admin \
  --cacert tls/server.rsa.crt \
  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJlbWFpbCI6InVzZXJAbG9jYWxob3N0Iiwicm9sZSI6InVzZXIiLCJleHAiOjE2MTcwNjUxMDJ9.imixaRk8YgoEv8Hh33qidty_jGBAo9ewIOd7vWqAjAHiN-MZJOFeSXg25nWx86SW9Pc_QFH_qlFYaSmPG_MfRA'

# result:
# {"message":"no permission","status":"403 Forbidden"}⏎

curl https://localhost:9090/api/auth/logout \
  -X POST \
  --cacert tls/server.rsa.crt \
  --cookie "refresh_token=QpOddMUkW9wk/S4B.s/a3k3JttPFH3v4j43gxx7KL+3y05Opm1rjiQBV+07z9NXacLv8PeQn6DRDoblFDerGQ9qeUp1TpaNAg5f1cYtLf3t3xnvGkHUDW2TK/mDJr4A=="

Potential changes/additions

  • auth rate limit
  • http to https redirect
  • delete the cookie on the client on logout
    • not really necessary, but can do for cleanliness
  • lets-encrypt certificates
  • add APIs for add/delete user
  • casbin

Special mentions

These sources were useful starting points.

License

This project is licensed under the MIT license.

Contribution

Pull requests are welcome. The goal of this project is to serve as a useful example for building a website with a Rust backend that includes some security.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion by you shall be licensed as MIT without any additional terms or conditions.

About

Example application using a Vue frontend with Rust backend that has authentication + authorization.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published