-
-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
163 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# Password hashing | ||
|
||
Password hashing is a technique for storing and validating user passwords without exposing what those passwords are. All modern web apps must use this technique | ||
to store credentials, and Rwf comes with built-in support for hashing using [Argon2](https://en.wikipedia.org/wiki/Argon2). | ||
|
||
## Generate hashes | ||
|
||
A password hash can be generated using the `rwf::crypto::hash` function, for example: | ||
|
||
```rust | ||
use rwf::crypto::hash; | ||
|
||
let hashed = hash("secret_password".as_bytes()).unwrap(); | ||
``` | ||
|
||
The hash generated by this function is a Rust string; it can be saved in a database. Since Argon2 is cryptographically secure, strong passwords are reasonably protected against brute force attacks in case the hashes are leaked. | ||
|
||
!!! note | ||
While hashes are hard to brute force, it's still inadvisable to allow hashes | ||
to be easily accessible to anyone. Make every effort to protect your production database | ||
against unauthorized access. | ||
|
||
## Validate passwords | ||
|
||
Hashes are used to check that some information the application has seen previously matches what the it's seeing now. For example, when one of your users wants to log into the application, | ||
they will provide the application with a login and a password. The password can be validated against an existing hash, and if the two match, it's safe to assume that the password is correct. | ||
|
||
Passwords can be validated using the `rwf::crypto::hash_validate` function, for example: | ||
|
||
```rust | ||
use rwf::crypto::hash_validate; | ||
|
||
let matches = hash_validate( | ||
"secret_password".as_bytes(), | ||
&hashed, | ||
).unwrap(); | ||
``` | ||
|
||
## Using with Tokio | ||
|
||
You'll note that both `hash` and `hash_validate` functions are slow. In fact, it can take upwards a second to generate or validate a hash. This is done on purpose, to make hashes hard to brute force. | ||
To avoid blocking the Tokio runtime and slowing down your application, make sure to use both functions inside blocking tasks: | ||
|
||
```rust | ||
use tokio::task::spawn_blocking; | ||
|
||
let hashed = spawn_blocking(move || { | ||
hash("secret_password".as_bytes()) | ||
}) | ||
.await | ||
.unwrap() | ||
.unwrap(); | ||
``` | ||
|
||
## Learn more | ||
|
||
- [examples/users](https://github.com/levkk/rwf/tree/main/examples/users) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
nav: | ||
- 'index.md' | ||
- 'build-your-app' | ||
- 'hot-reload.md' | ||
- '...' | ||
- 'deploy-to-prod.md' | ||
- 'admin.md' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# Add users | ||
|
||
!!! note | ||
This guide is a work-in-progress. | ||
|
||
Unless you're building simple demo applications or static informational websites, your web app will need a way for your users to sign up and personalize their experience. There are many ways to accomplish this, and your implementation should be specific to your use case. For example, many web apps allow users to sign up using an OAuth2 provider like [Google](https://developers.google.com/identity/protocols/oauth2) or [GitHub](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app). | ||
|
||
In this guide, we'll cover the most popular and simple way to create user accounts: using a username and a password. | ||
|
||
## Username and password | ||
|
||
Allowing your users to create accounts in your application using a username and password is pretty universal. Implementing this system requires using all 3 components of the MVC framework: creating a database model to store usernames and password hashes, controllers to process signup and login requests, and views to serve signup and login forms. | ||
|
||
Rwf supports all three components natively. | ||
|
||
### Create the model | ||
|
||
To create a model in Rwf, you need to define the schema in the database and define the model in Rust code. The two should match as closely as possible. | ||
|
||
#### Create the schema | ||
|
||
Starting with the data model, let's create a simple `"users"` table in your database. This table will store usernames, password hashes, and other metadata about our users, like when their accounts were created. | ||
|
||
Creating a table with Rwf should be done by writing a [migration](../../models/migrations.md). This makes sure changes to the database schema are documented and deterministic. To create a migration, use the Rwf CLI: | ||
|
||
=== "Command" | ||
``` | ||
rwf-cli migrate add -n users | ||
``` | ||
=== "Output" | ||
``` | ||
Created "migrations/1733265254409864495_users.up.sql" | ||
Created "migrations/1733265254409864495_users.down.sql" | ||
``` | ||
|
||
The migration is empty, so let's create the table by adding it to the `users.up.sql` file: | ||
|
||
```postgresql | ||
CREATE TABLE users ( | ||
id BIGSERIAL PRIMARY KEY, | ||
username VARCHAR NOT NULL UNIQUE, | ||
password_hash VARCHAR NOT NULL, | ||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | ||
); | ||
``` | ||
|
||
As mentioned above, our table will store usernames, password hashes, and metadata. The `id` column is the primary key of this table, allowing us to identify our users using a unique number. | ||
|
||
!!! note | ||
Rwf models by default expect the presence of the `id` column, and use it as the primary key. | ||
This is configurable on a per-model basis, and models can be created without a primary key, | ||
but this will prevent them from being updated or deleted by the ORM. | ||
|
||
Once the schema is ready, create the table in the database by applying the migration: | ||
|
||
=== "Command" | ||
``` | ||
rwf-cli migrate run | ||
``` | ||
=== "Output" | ||
``` | ||
applying migration "1733265254409864495_users" | ||
migration "1733265254409864495_users" applied | ||
``` | ||
|
||
#### Define the Rust model | ||
|
||
With the schema ready to go, we need to create a Rust struct which we'll use in code to reference the model records. The Rust struct should have the same fields as the columns in our table, and their data types should match as well: | ||
|
||
```rust | ||
#[derive(Clone, macros::Model)] | ||
pub struct User { | ||
id: Option<i64>, | ||
username: String, | ||
password_hash: String, | ||
created_at: OffsetDateTime, | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Build with Rwf | ||
|
||
Rwf has a lot of features and mixing them together can create powerful and efficient web apps. This guide will demonstrate how | ||
to build a generic application from scratch using Rwf as your web framework. | ||
|
||
!!! note | ||
This guide is a work in progress. Please check back soon for more updates. [Contributions](https://github.com/levkk/rwf/tree/main/docs/docs/user-guides/build-your-app) are welcome! | ||
|
||
## Getting started | ||
|
||
If you'd like to build an application with this guide, make sure make sure to install the Rwf CLI first: | ||
|
||
``` | ||
cargo install rwf-cli | ||
``` | ||
|
||
Once the CLI is installed, make sure to follow the [instructions](../../index.md) on creating a new Rwf application. | ||
|
||
## Chapters | ||
|
||
1. [Add users](add-users.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters