% Rocket-auth-login - Authentication and Login library for Rust's Rocket Framework
Tested using nightly-2017-11-22-x86_64-pc-windows-msvc To install using rustup in windows and set as default use:
rustup toolchain install nightly-2017-11-22-x86_64-pc-windows-msvc
rustup default nightly-2017-11-22-x86_64-pc-windows-msvc
Or on linux:
rustup toolchain install nightly-2017-11-22-x86_64-unknown-linux-gnu
rustup default nightly-2017-11-22-x86_64-unknown-linux-gnu
In the cargo.toml add:
rocket-auth-login = "0.5.*"
```
extern crate rocket_auth_login as auth;
use auth::authorization;
```
Rocket-auth-login is a library written in Rust for authentication and login processing.
This crate provides traits that you will implement on two different custom types that you define in your program. These traits contain helpful methods to process login form data as well as store and retrieve cookies. The two custom structures that you will define will do the following:
String that is stored in a private cookieThe Version 2.0 passes ownership of any cookies passed into the redirection methods which is not very convenient. Version 2.1 uses a mutable reference to the cookies allowing them to be modified after calling the redirect methods (the redirect structure can be held in a variable and returned when needed).
In your application define two custom data structures that will:
```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdministratorForm {
pub username: String,
pub password: String,
}
```
```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdministratorCookie {
pub userid: u32,
pub username: String,
pub display: Option<String>,
}
```
The trait CookieId is implemented by both data structures. the AuthorizeForm trait is implemented by only the login data structure while the AuthorizeCookie trait is implemented by the cookie data structure. For a more thorough example look at the administrator.rs file in any of the examples.
Each data structure will must implement the CookieId trait. The cookie_id() implementation should be the same for both data structures since the two will have the same cookie identifier (the login form data structure will create the cookie with the specified cookie identifier while the cookie data structure will read the cookie associated with the specified identifier). It is a simple trait with only a single method. The implementation for cookide_id() method looks like:
```
// Both structures will use the same code for the `CookieId`
impl CookieId for {StructNameHere} {
fn cookie_id<'a>() -> &'a str {
"acid"
}
}
```
The AuthorizeCookie trait defines methods for the cookie data structure. There are two functions you must implement on the cookie data structure, as well as a thrid function delete_cookie() which has a default implementation that deletes the private cookie with the name specified by CookieId::cookie_id(). The functions you must implement on the cookie data structure are:
store_cookie(&self) -> String
retrieve_cookie(String) -> Self
The AuthorizeForm trait defines several methods which are implemented on the login form data structure, only two of which must be implemented in your code. The rest are optional. Also the associated type CookieType should be set to the cookie data structure type
```
impl AuthorizeForm for AdministratorForm {
type CookieType = AdministratorCookie;
```
Required:
fn authenticate(&self) -> Result<Self::CookieType, AuthFail>
&self parameterOk( CookieType ) where CookieType is replaced by the cookie data structurefn new_form(&str, &str, Option<HashMap<String, String>>) -> Self
FromForm implementation on the LoginCont structure (which the container used to store the login form data structure, see the Routes example below). The parameters are:
usernamepasswordusername and password. This is used when the username and password fields have different names (then authenticate() can look at the fields in the extras hashmap instead of the regular username and password fields) or when other fields are needed in the login form.Optional (can be overridden):
fn flash_redirect(&self, ok_redir: &str, err_redir: &str, cookies: &mut Cookies) -> Result<Redirect, Flash<Redirect>>
authenticate() method and if successful creates the private cookie to log the user in. Redirects the user to the ok_redir page on successful authentication. If authentication fails it calls the fail_url() to build a query string which is appended to err_redir (this allows the username to persist and be filled in inside the login form) and also sets a FlashMessage (a cookie that is deleted once read) indicating why the form failedfn redirect(&self, ok_redir: &str, err_redir: &str, cookies: &mut Cookies) -> Result<Redirect, Redirect>
flash_redirect() except it does not set a Flashmessage, it only redirects to either the okredir page or errredir page (with the query string returned by fail_url() appended)fn fail_url(user: &str) -> String
fn clean_username(string: &str) -> String
sanitize() method from the sanitization module (sanitization.rs)fn clean_password(string: &str) -> String
sanitize_password() method from the sanitization module (sanitization.rs)fn clean_extras(string: &str) -> String
Sanitizes the any extra fields. By default it uses the sanitize() method from the sanitization module (sanitization.rs)It is possible and fairly simple to add multiple user types, like an administartor and a regular user type.
To accomplish this simply two data structures for each type (a login form data structure and a cookie data structure) and define different cookie identifiers for each user type (the admin type may have cookie_id() return something like "aid" while the user type may return something similar to "uid").
In your routes you will use the cookie data type as a request guard (ensuring that the user viewing the page is logged in as the specified user type). A route that uses the AdministratorCookie type looks like:
```
#[get("/login", rank = 1)]
fn logged_in(_user: AuthCont<AdministratorCookie>) -> Html<String> {
let admin: AdministratorCookie = _user.cookie;
Html( format!("Welcome {}, you are logged in as an administrator.", admin.username) )
}
// OR to use the type directly
fn logged_in(admin: AdministratorCookie
```
The login processing route will be a post route that
```
#[allow(unused_mut)]
#[post("/login", data = "<form>")]
fn process_login(form: Form<LoginCont<AdministratorForm>>, mut cookies: Cookies) -> Result<Redirect, Flash<Redirect>> {
let inner = form.into_inner();
let login = inner.form;
login.flash_redirect("/login", "/login", cookies)
}
```
The library will send passwords in plaintext. It is highly recommended that you use TLS. There is even an example showing how to use tls with this crate. The changes are minimal. If you absolutely need to hash a password before the password is sent the examples all include a sha256.js file from http://www.movable-type.co.uk/scripts/sha256.html which can be used for that very purpose. Also the login.js file contains commented out code around line 15 that can be used to hash the password before sending it using the sha256.js file. This method is extremely discouraged. Sha hashes are fast, and thus very susceptible to rainbow table attacks. Without using TLS the security is almost the same as plaintext when using just a hashed password. Use TLS. Let's Encrypt) offers free certificates so there's no reason not to use https for production purposes.
Copyright Note: The Rocket-auth-login crate and Rust code examples are licensed under the Apache 2.0 license. However the layout/design in the examples was created by me. You can use it however if you put in at least an HTML comment inside the HTML output saying Design © 2017 Andrew Prindle. The rest of the application you may use without any kind of credit displayed to users but must follow the terms of the Apache 2.0 license.