An implementation for an authentication API for Rocket applications.

Overview

rocket_auth

rocket_auth provides a ready-to-use backend agnostic API for authentication management. For more information visit the documentation at https://docs.rs/rocket_auth. It supports connections for SQLite and Postgresql. It lets you create, delete, and authenticate users. The available features are:

  • sqlite-db: for interacting with a SQLite database.
  • postgres-db: for interacting with a Postgresql database.
  • redis-session: for storing sessions on a redis server.

By default this crate stores sessions on a concurrent hashmap. As a result, sessions will only be stored as long as the rocket application runs uninterrupted. In order to store persistent sessions, it is recommended to connect the Users(Users::open_redis) instance to a redis server . This requires the redis-session feature to be enabled.

rocket_auth uses private cookies to store session data. This means that in order for cookies to be properly decrypted between launches, a secret_key must be set. For more information visit rocket's configuration guide.

To use rocket_auth include it as a dependency in your Cargo.toml file:

[dependencies.rocket_auth]
version = "0.3.0"
features = ["sqlite-db", "redis-session"]

Quick overview

This crate provides two guards:

  • Auth: manages authentication.
  • Session: retrieves session data from client cookies.
  • User: It restricts content, so it can be viewed by authenticated clients only.

It also includes two structs to be parsed from forms and json data:

  • Signup: used to create new users.
  • Login: used to authenticate users.

Finally it has two structures for queries:

  • Users: it allows to query users to the database.
  • User: it is the response of a query.

The Auth guard allows to log in, log out, sign up, modify, and delete the currently (un)authenticated user. For more information see Auth. Because of rust's ownership rules, you may not retrieve both rocket::http::Cookies and the Auth guard simultaneously. However, retrieveng cookies is not needed since Auth stores them in the public field Auth::cookies. A working example:

use rocket::{get, post, Form};
use rocket_auth::{Users, Error, Auth, Signup, Login};

#[post("/signup", data="
")] fn signup(form: Form, mut auth: Auth) { auth.signup(&form); } #[post("/login", data="")] fn login(form: Form, mut auth: Auth) { auth.login(&form); } #[get("/logout")] fn logout(mut auth: Auth) { auth.logout(); } fn main() -> Result<(), Error>{ let users = Users::open_sqlite("mydb.db")?; rocket::ignite() .mount("/", routes/[signup, login, logout]) .manage(users) .launch(); Ok(()) }

Users struct

The Users struct administers interactions with the database. It lets you query, create, modify and delete users. Unlike the Auth guard, a Users instance can manage any user in the database. Note that the Auth guards includes a Users instance stored on the public users field. So it is not necesary to retrieve Users when using Auth. A simple example of how to query a user with the Users struct:

#[get("see-user/")]
fn see_user(id: i32, users: State) -> String {
   let user = users.get_by_id(id);
   fortmat!("{}", json!(user))
}

A Users instance can be constructed by connecting it to the database with the methods open_sqlite(Users::open_sqlite), open_postgres(Users::open_postgres). Furthermore, it can be constructed from a working connection.

User guard

The User guard can be used to restrict content so it can only be viewed by authenticated users. Additionally, yo can use it to render special content if the client is authenticated or not.

 #[get("/private-content")]
fn private_content(user: User) -> &'static str {
     "If you can see this, you are logged in."
} 

#[get("/special-content")]
fn special_content(option: Option) -> String {
   if let Some(user) = option {
     format!("hello, {}.", user.email)
   } else {
      "hello, anonymous user".into()
   }
} 
Comments
  • Fixed compile error and lowercase email on signup

    Fixed compile error and lowercase email on signup

    As you probably did not have time to fix these problems, i did it myself in a fork to use in my own project. You can merge this PR if this is useful to you

    opened by DerPizzaBoi 3
  • Check if String is same as hashed password

    Check if String is same as hashed password

    I am currently implementing a feature to change a users password and email. To do that the user has to reenter the password, even if he is logged in. After that i want to hash the password and compare it to the current one. My Problem is: I can't find a method to easily hash a String with the same salt as the current password. For now i can try to use the argon2 crate as you do and hash it that way, but It would be nice to have a function to compare the hashed password to a string. Maybe you can add that in the next release :)

    opened by DerPizzaBoi 3
  • Derestrict rusqlite dependency version

    Derestrict rusqlite dependency version

    This commit removes depending exactly on rusqlite 0.25.3 (which has been yanked btw).

    From quick testing, depending on the latest version (0.27.0) works too, so alternatively, you could change the version to be 0.27.0 rather than this commit's suggestion.

    As an alternative, running cargo upgrade to update all dependencies to latest versions, it seems like everything builds fine (and the tests seem to produce some minor fixable suggestions), so maybe if making a new release, it might be good to update all dependencies. Happy to make a separate PR if you think it would be useful.

    Thanks for this project btw! :)

    opened by jaybosamiya 2
  • compare password functionality

    compare password functionality

    I have now implemented a method called compare_password(). It is located both in the Auth and User guard. I implemented it in both, so that you can decide where it is more useful. I myself think it is better to have it in the User guard, because that makes sure that a logged in user exists, but that is up to you to decide. The function was also tested in one of my projects, where i temporary used my fork of rocket_auth as a dependencie. The documentation and function name is just an idea, feel free to change it :)

    If you think it should be implemented otherwise, write a comment i will fix it

    opened by DerPizzaBoi 2
  • Rusqlite should be supported.

    Rusqlite should be supported.

    Heya, I'm using this crate for my project: https://github.com/knarkzel/elixir, where I'm using rusqlite instead of sqlx. Could you add rusqlite to this crate? I have forked it and added needed changes here: https://github.com/knarkzel/rocket_auth/blob/master/src/db/sqlite/mod.rs.

    Thanks!

    opened by knarkzel 2
  • Compile error

    Compile error

    So the next crate does not compile anymore, because i can't find the id, as id is not a filed anymore. It can be fixed by changing line 316 in auth.rs from let user: User = self.users.get_by_id(session.id).await?; to let user: User = self.users.get_by_id(session.id()?).await?;. I tried to fix it myself, it works locally, but i can't update my fork

    opened by DerPizzaBoi 1
  • Trait FromRequest not implemented for User/Admin when trying to use guards

    Trait FromRequest not implemented for User/Admin when trying to use guards

    rust 1.60 stable rocket_auth = { version = "0.4.0", features = ["sqlx-postgres"] } rocket = { git = "https://github.com/SergioBenitez/Rocket/", features = ["json", "secrets", "uuid"]} rocket_dyn_templates = { git = "https://github.com/SergioBenitez/Rocket/", features = ["tera"]}

    I see the traits in the rocket_auth lib.......so, obviously, I'm doing something silly. Looking for input on what I'm doing wrong.

    use rocket_auth::{Users, Error, Auth, Signup, Login, User, AdminUser};

    #[get("/admin-panel")] fn admin_panel(user: AdminUser) -> String { format!("Hello {}.", user.email()) }

    error[E0277]: the trait bound rocket_auth::AdminUser: FromRequest<'_> is not satisfied --> src/admin/bp_user.rs:27:22 | 27 | fn admin_panel(user: AdminUser) -> String { | ^^^^^^^^^ the trait FromRequest<'_> is not implemented for rocket_auth::AdminUser

    opened by SpootDev 1
  • German language support

    German language support

    I saw that @julien-me added french language support, so i thought that i could add german language support. Hope it is ok for you. And if you don't like "de" for german, i can change it to "ger" or something like that

    Also, this is my first PR ever, so please tell me if i did something wrong

    opened by DerPizzaBoi 1
  • Email field of `User` should be public

    Email field of `User` should be public

    I'm using sailfish for templates, and I don't wanna serialize the type because of overhead, and I only need the email field of User. This should probably be public, unless there's a specific security reason for it not to be.

    opened by knarkzel 1
  • Fixed postgres database 'UPDATE_USER' string in sql.rs

    Fixed postgres database 'UPDATE_USER' string in sql.rs

    This is the same issue as with #47 and #45. This issue probably also exists for the other database implementations, as the have the same pattern. I did not change it for those, because i don't have them installed and can't test it. If you want i can change those as well.

    opened by DerPizzaBoi 0
  • Improve error messages

    Improve error messages

    Some error messages aren't very clear or are outright empty. Such as when you try to sign up, and the email is already taken, there is no helpful error message.

    opened by tvallotton 0
  • How to get the value chosen in an html form select, and having that value in a rust variable

    How to get the value chosen in an html form select, and having that value in a rust variable

    Hello i have a form with a dropdown select where i chose a date, when a date is chosen i would like to have the chosen value to a rust variable.

    main.rs:

    #![feature(proc_macro_hygiene, decl_macro)]
    #[macro_use] extern crate rocket;
    use rocket::{State, form::*, get, post, response::Redirect, routes, response::content};
    use rocket_auth::{prelude::Error, *};
    use rocket_dyn_templates::Template;
    use serde_json::json;
    use sqlx::*;
    mod api;
    use api::{call_api, db_fill, query_select, ResultsStruct};
    use std::result::Result;
    use std::*;
    use serde::{Deserialize, Serialize};
    use rocket::serde::json::Json;
    
    #[get("/style.css")]              // <- route attribute
    fn style() -> content::RawCss<&'static str> {
    content::RawCss(include_str!("style.css"))
    }
    
    #[get("/show_on_click")]              // <- route attribute
    fn show_on_click() -> content::RawJavaScript<&'static str> {
    content::RawJavaScript(include_str!("showOnClick.js"))
    }
    
    #[get("/login")]
    fn get_login() -> Template {
    Template::render("login", json!({}))
    }
    
    #[post("/login", data = "<form>")]
    async fn post_login(auth: Auth<'_>, form: Form<Login>) -> Result<Redirect, Error> {
    let result = auth.login(&form).await;
    println!("login attempt: {:?}", result);
    result?;
    Ok(Redirect::to("/"))
    }
    
    #[get("/signup")]
    async fn get_signup() -> Template {
    Template::render("signup", json!({}))
    }
    
    #[post("/signup", data = "<form>")]
    async fn post_signup(auth: Auth<'_>, form: Form<Signup>) -> Result<Redirect, Error> {
    auth.signup(&form).await?;
    auth.login(&form.into()).await?;
    
    Ok(Redirect::to("/"))
    }
    
    #[get("/")]
    async fn index(user: Option<User>) -> Template {
    let color = "";
    /*let c_api = thread::spawn( move || {
        let call_api = call_api().unwrap();
        call_api
    }).join().expect("Thread panicked");*/
    
    let query_result_fn = thread::spawn( move || {
        let query_result = query_select();
        query_result
    }).join().expect("Thread panicked");
    let query_result = query_result_fn.await.unwrap();
    
    Template::render("index", json!({ "user": user, /*"c_api": c_api,*/ "query_result": query_result }))
    }
    
    #[derive(FromForm, Deserialize, Serialize)]
    struct Time {
    commence_time: String
    }
    
    #[get("/time")]
    async fn time_get(user: Option<User>) -> Template {
    let date_chosen= ""; // here i would like to have the chosen date in string.
    
    let query_result_fn = thread::spawn( move || {
        let query_result = query_select();
        query_result
    }).join().expect("Thread panicked");
    let query_result = query_result_fn.await.unwrap();
    
    Template::render("time", json!({ "user": user, /*"c_api": c_api,*/ "query_result": query_result }))
    }
    
    #[post("/time", format="json", data="<user>")]
    fn post_time(user: Json<Time>) -> String {
    String::from(format!(
        "Created user: {}", user.commence_time))
    }
    
    
    #[get("/logout")]
    fn logout(auth: Auth<'_>) -> Result<Template, Error> {
    auth.logout()?;
    Ok(Template::render("logout", json!({})))
    }
    #[get("/delete")]
    async fn delete(auth: Auth<'_>) -> Result<Template, Error> {
    auth.delete().await?;
    Ok(Template::render("deleted", json!({})))
    }
    
    #[get("/show_all_users")]
    async fn show_all_users(conn: &State<SqlitePool>, user: Option<User>) -> Result<Template, Error> {
    let users: Vec<User> = query_as("select * from users;").fetch_all(&**conn).await?;
    println!("{:?}", users);
    Ok(Template::render(
        "users",
        json!({"users": users, "user": user}),
    ))
    }
    
    #[tokio::main]
    async fn main() -> Result<(), Error> {
    
    let conn = SqlitePool::connect("/home/erty/Programming/rocket_app/users.db").await?;
    let users: Users = conn.clone().into();
    users.create_table().await?;
    
    /*thread::spawn( move || {
        let db_fill = db_fill().unwrap();
        db_fill
    }).join().expect("Thread panicked");*/
    
    let _ = rocket::build()
        .mount(
            "/",
            routes![
                index,
                style,
                show_on_click,
                get_login,
                post_signup,
                get_signup,
                post_login,
                logout,
                delete,
                show_all_users,
                time_get
            ],
        )
        .manage(conn)
        .manage(users)
        .attach(Template::fairing())
        .launch()
        .await
        .unwrap();
    Ok(())
    }
    

    index.html.tera

    {% extends "base" %}
    
    {% block body %}
    
        {% if not user %}
    
            <h1 class="text">Please login!</h1>
            <h3 class="text">For the use of this site you need to create an account or login</h3>
            <br>
            <div class="container">
                <a href="/login"><button id="Cbutton" type="Button" class="Cbutton btn btn-primary w-100">Login</button></a>
            </div>
                
        {% endif %}
    
        {% if user %}
            <script src="/show_on_click"></script>
            <h1 class="text">Welcome to My Website Index! </h1>
            <br>
            <h3 class="text">You are logged in as {{ user.email }}</h3>
            <br>
                <form action="/time" method="post">
                    <select>
                        {% set vec_num = 0 -%}
                        {% for i in query_result %}
                            {% set_global commence_time = query_result[vec_num].commence_time %} 
                            <option value = {{ commence_time }} > {{ commence_time }} </option>
                            {% set_global vec_num = vec_num + 1 %}
                        {% endfor %}
                    </select>
                    <div class="mb-3 row">
                        <button id="Cbutton" type="Button submit" class="Cbutton btn btn-primary">Submit</button>
                    </div>
                </form>
        </div>
            
        {% endif %}
    
    {% endblock body %}
    

    if i submit i get this error

    No matching routes for POST /time application/x-www-form-urlencoded.

    opened by ErtyDess 0
  • Removed direct logging via println

    Removed direct logging via println

    Hi,

    while using the sqlite backend I noticed that there are direct messages that are bypassing my logging configuration. It seems some prinln statements were the cause of this, that are not found in other backends.

    And thank you for this wonderful crate, it works like a charm. Have a nice day!

    opened by Flyffies 0
  • Compilation failing due to SQLX features

    Compilation failing due to SQLX features

    Hi,

    I am attempting to use rocket_auth within a project already using Sqlx 0.6.1 and getting the following error:

    error: one of the features ['runtime-actix-native-tls', 'runtime-async-std-native-tls', 'runtime-tokio-native-tls', 'runtime-actix-rustls', 'runtime-async-std-rustls', 'runtime-tokio-rustls'] must be enabled
      --> /Users/george/.cargo/registry/src/github.com-1ecc6299db9ec823/sqlx-rt-0.5.13/src/lib.rs:9:1
       |
    9  | / compile_error!(
    10 | |     "one of the features ['runtime-actix-native-tls', 'runtime-async-std-native-tls', \
    11 | |      'runtime-tokio-native-tls', 'runtime-actix-rustls', 'runtime-async-std-rustls', \
    12 | |      'runtime-tokio-rustls'] must be enabled"
    13 | | );
       | |_^
    

    I was able to fix this error by locally cloning the rocket_auth repository and adding in the following to the SQLX dependency section

    [dependencies.sqlx]
    version = "0.6.0"
    optional = true
    features = ["runtime-tokio-rustls"]
    

    This is my dependencies (edited for brevity):

    [dependencies]
    rocket = {version = "0.5.0-rc.1", features = ["json"]}
    rocket_auth = {path = "../rocket_auth", features = ["sqlx-sqlite"]}
    serde = {version = "1.0", features = ["derive"]}
    serde_json = "1.0"
    tokio = {version = "1.17", features = ["full"]}
    sqlx = {version = "0.6", features = ["runtime-tokio-rustls", "sqlite"]}
    

    Just wondering if this is some perculiarity in the way my project is set up or is this a new bug?

    opened by modulated 2
  • Use

    Use "AUTOINCREMENT" for user table primary key

    I noticed in my app that after I delete a user, when I create a new user, the new user has the same id as the deleted user.

    This behavior is not expected. It can be avoided if this line is changed to include AUTOINCREMENT: https://github.com/tvallotton/rocket_auth/blob/ef445dfe22c6738e95c21f5710f85ab1136bdb06/src/db/sqlite/sql.rs#L3

    opened by yzernik 0
  • ValidationError leaks password if signup fails

    ValidationError leaks password if signup fails

    When singing up a user with a password, that does not meet the requirements, the error message leaks the password that was used in the signup. I don't know if this is intended. I narrowed it down to the ValidationError::new(...) function. The error message is inserted into the code and the actual message is left empty. Then in the Display implementation for ValidationError there is a check if the error message is empty, which is always the case. Then it skips printing the actual error message, and instead prints the code (which contains the error message, because it is set wrongly) and only shows "Validation error: " with the params (which is the password). I am showing these errors to my api response directly. This way the password pops up on screen, even though it is supposed to be hidden in the signup page. i know this is actually an issue of the validator page, but i have an idea to avoid this. You could instead of using the display method to show the message, print the actual "code" field of the error. If you don't have enough time to fix this, i could create a pr

    opened by DerPizzaBoi 4
Owner
Wtf is a monad anyway??
null
vault client using jwt authentication that define environment variables from vault secrets before executing into something else

envlt envlt, like env, allows you to define environment variables and then execute into something else, but instead of static values, it uses using si

Eric Burghard 6 Nov 13, 2022
Authenticate to Minecraft using the Microsoft Authentication Scheme from Rust.

Authenticating to Minecraft with the Microsoft Authentication Scheme from Rust This program showcases an implementation of the microsoft authenticatio

ALinuxPerson 17 Dec 22, 2022
Rust library for HTTP authentication. Parses challenge lists, responds to Basic and Digest challenges. Likely to be extended with server support and additional auth schemes.

Rust library for HTTP authentication. Parses challenge lists, responds to Basic and Digest challenges. Likely to be extended with server support and a

Scott Lamb 3 Jun 10, 2022
Fast, simple and REST compliant file-server with public/private key authentication written in Rust

stormi Stormi is a fast and simple file-server with public/private key authentication How does it work? Stormi accepts multipart/form-data form with m

Polygon 2 Dec 8, 2022
🔥 Firebase authentication for Rust 🦀

Fire Auth Rust wrapper for Firebase Authentication REST API Installation Add the following to Cargo.toml: fireauth = "0.1.5" How to use First you need

UwU 11 Nov 12, 2022
Xbox live authentication flow for Minecraft with Rust.

MC Auth Xbox live authentication flow for Minecraft in Rust. Why? In order to create tools for Minecraft based on rust that implement the user profile

Minecraft Rust 3 Jan 15, 2023
A HTTP Filter checking for OIDC Authentication, made for Envoy Plugins, written in Rust

WASM OIDC Plugin A plugin for Envoy written in Rust. It is a HTTP Filter, that implements the OIDC Authorization Code Flow. Requests sent to the filte

Anton Engelhardt 5 Jul 7, 2023
An auth system/library for Rust applications

Rust : Forbidden (WIP) An experimental auth library for Rust applications. Goals This crate is to define a common set of traits and idioms to provide

Mario Montoya 9 Nov 8, 2022
Securely generate API tokens for third-party access to your Tesla.

Tesla Auth Securely generate API tokens for third-party access to your Tesla. Multi-factor authentication (MFA) and Captcha are supported. Download Pr

Adrian Kumpf 136 Jan 7, 2023
An oauth2 client implementation providing the Device, Installed and Service Account flows.

yup-oauth2 is a utility library which implements several OAuth 2.0 flows. It's mainly used by google-apis-rs, to authenticate against Google services.

Lewin Bormann 174 Dec 30, 2022
A paseto implementation in rust.

Paseto Rust Paseto is everything you love about JOSE (JWT, JWE, JWS) without any of the many design deficits that plague the JOSE standards. This is d

Instructure, Inc. 145 Nov 7, 2022
An implementation of webauthn components for Rustlang servers

Webauthn-rs Webauthn is a modern approach to hardware based authentication, consisting of a user with an authenticator device, a browser or client tha

Kanidm 232 Jan 8, 2023
OpenSK is an open-source implementation for security keys written in Rust that supports both FIDO U2F and FIDO2 standards.

OpenSK This repository contains a Rust implementation of a FIDO2 authenticator. We developed this as a Tock OS application and it has been successfull

Google 2.4k Jan 2, 2023
A minimal jwt implementation for OIDC

Compact JWT Json Web Tokens (JWT) are a popular method for creating signed transparent tokens that can be verified by clients and servers. They are en

Kanidm 4 Dec 29, 2021
ROCCA cipher implementation for Rust.

ROCCA for Rust This is a Rust implementation of the ROCCA authenticated cipher, ported from the Zig implementation. ROCCA is key committing, has a 256

Frank Denis 6 Sep 30, 2022
RSA implementation in pure Rust

RSA A portable RSA implementation in pure Rust. ⚠️ WARNING: This crate has been audited by a 3rd party, but a full blog post with the results and the

Rust Crypto 346 Jan 4, 2023
WebCipher - JWT encryption/decryption algorithms + a JWK Store implementation

webcipher provides JWT authentication utilities and storage mechanism for caching keys and optimizing decryption/encryption processes.

Wavy 1 May 1, 2022
SD-JWT Rust Reference Implementation

SD-JWT Rust Reference Implementation This is the reference implementation of the IETF SD-JWT specification written in Rust. Supported version: 6. Note

OpenWallet Foundation Labs 4 Dec 19, 2023
Easy c̵̰͠r̵̛̠ö̴̪s̶̩̒s̵̭̀-t̶̲͝h̶̯̚r̵̺͐e̷̖̽ḁ̴̍d̶̖̔ ȓ̵͙ė̶͎ḟ̴͙e̸̖͛r̶̖͗ë̶̱́ṉ̵̒ĉ̷̥e̷͚̍ s̷̹͌h̷̲̉a̵̭͋r̷̫̊ḭ̵̊n̷̬͂g̵̦̃ f̶̻̊ơ̵̜ṟ̸̈́ R̵̞̋ù̵̺s̷̖̅ţ̸͗!̸̼͋

Rust S̵̓i̸̓n̵̉ I̴n̴f̶e̸r̵n̷a̴l mutability! Howdy, friendly Rust developer! Ever had a value get m̵̯̅ð̶͊v̴̮̾ê̴̼͘d away right under your nose just when

null 294 Dec 23, 2022
Rust Rocket MongoDB token-authorization REST API boilerplate

Rust Rocket MongoDB token-auth REST API boilerplate In this repository, you can find backend Rust rocket mongodb rest-api boilerplate with token autho

null 6 Dec 7, 2022