Auth Web Microservice with rust using Actix-Web - Complete Tutorial Part 1
Tags: rust, tutorial, learning, webdev
Published: 2018-10-22 15:36:10

---
title: "Auth Web Microservice with rust using Actix-Web - Complete Tutorial Part 1"
published: true
description: "Rust utorial for creating an Auth micro service"
cover_image: https://hgill.io/images/rust-lang-ar21.png
tags: rust, tutorial, learning, webdev
series: rust-auth-server
---
This post is outdated now please read the updated version [Auth Web Microservice with rust using Actix-Web 1.0 - Complete Tutorial](https://gill.net.in/posts/auth-microservice-rust-actix-web1.0-diesel-complete-tutorial/) 
### What?
We are going to create a web-server in `rust` that only deals with user registration and authentication. I will be explaining the steps in each file as we go. The complete project code is here [repo](https://gitlab.com/mygnu/rust-auth-server/tree/part_one). Please take all this with a pinch of salt as I'm a still a noob to rust 😉.

##### Flow of the event would look like this:
 
- Registers with email address ➡ Receive an 📨 with a link to verify
- Follow the link ➡ register with same email and a password
- Login with email and password ➡ Get verified and receive jwt token

##### Crates we are going to use 

- [actix](https://crates.io/crates/actix) // Actix is a Rust actors framework.
- [actix-web](https://crates.io/crates/actix-web) // Actix web is a simple, pragmatic and extremely fast web framework for Rust.
- [brcypt](https://crates.io/crates/bcrypt) // Easily hash and verify passwords using bcrypt.
- [chrono](https://crates.io/crates/chrono) // Date and time library for Rust.
- [diesel](https://crates.io/crates/diesel) // A safe, extensible ORM and Query Builder for PostgreSQL, SQLite, and MySQL.
- [dotenv](https://crates.io/crates/dotenv) // A dotenv implementation for Rust.
- [env_logger](https://crates.io/crates/env_logger) // A logging implementation for log which is configured via an environment variable. 
- [failure](https://crates.io/crates/failure) // Experimental error handling abstraction.
- [jsonwebtoken](https://crates.io/crates/jsonwebtoken) //  Create and parse JWT in a strongly typed way.
- [futures](https://crates.io/crates/futures) // An implementation of futures and streams featuring zero allocations, composability, and iterator-like interfaces. 
- [r2d2](https://crates.io/crates/r2d2) // A generic connection pool.
- [serde](https://crates.io/crates/serde) // A generic serialization/deserialization framework.
- [serde_json](https://crates.io/crates/serde_json) // A JSON serialization file format.
- [serde_derive](https://crates.io/crates/serde_derive) // Macros 1.1 implementation of #[derive(Serialize, Deserialize)].
- [sparkpost](https://crates.io/crates/sparkpost) // Rust bindings for sparkpost email api v1.
- [uuid](https://crates.io/crates/uuid) // A library to generate and parse UUIDs.

I have provided a brief info about the crates in use from their official description. If you want to know more about any of these crates please click on the name to go to `crates.io`. __Shameless plug:__ `sparkpost` is my crate please leave feedback if you like/dislike it.

##### Prerequisite

I will assume here that you have some knowledge of programming, preferably some rust as well. A working setup of `rust` is required. Checkout <a href="https://rustup.rs/" target="_blank">https://rustup.rs</a> for esasy rust setup. To know more about rust checkout <a href="https://doc.rust-lang.org/stable/book/2018-edition/index.html" target="_blank"> 
The Book</a>.

We will be using diesel to create models and deal with database, queries and migrations. Pleas head over to <a href="http://diesel.rs/guides/getting-started/" target="_blank">http://diesel.rs/guides/getting-started/</a> to get started and setup `diesel_cli`. In this tutorial we will be using `postgresql` so follow the instructions to setup for postgres. You need to have a running postgres server and can create a database to follow this tutorial through. Another nice to have tool is <a href="https://github.com/passcod/cargo-watch" target="_blank">Cargo Watch</a> that lets you watch the file system and re-compile and re-run the app when you make any changes.

Install `Curl` if don't have it already on your system for testing the api locally.

##### Let's Begin

After checking your rust and cargo version and creating a new project with 
```bash
# at the time of writing this tutorial my setup is 
rustc --version && cargo --version
# rustc 1.29.1 (b801ae664 2018-09-20)
# cargo 1.29.0 (524a578d7 2018-08-05)

cargo new simple-auth-server
# Created binary (application) `simple-auth-server` project

cd simple-auth-server # and then 

# watch for changes re-compile and run
cargo watch -x run 
```
Fill in the cargo dependencies with the following, I will go through each of them as get used in the project. I am using explicit versions of the crates, as you know things get old and change.(in case you are reading this tutorial after a long time it was created). In part 1 of this tutorial we won't be using all of them but they will all become handy in the final app.

```toml
[dependencies]
actix = "0.7.4"
actix-web = "0.7.8"
bcrypt = "0.2.0"
chrono = { version = "0.4.6", features = ["serde"] }
diesel = { version = "1.3.3", features = ["postgres", "uuid", "r2d2", "chrono"] }
dotenv = "0.13.0"
env_logger = "0.5.13"
failure = "0.1.2"
frank_jwt = "3.0"
futures = "0.1"
r2d2 = "0.8.2"
serde_derive="1.0.79"
serde_json="1.0"
serde="1.0"
sparkpost = "0.4"
uuid = { version = "0.6.5", features = ["serde", "v4"] }
```

##### Setup The Base APP

Create new files `src/models.rs` `src/app.rs`.

```rust
// models.rs
use actix::{Actor, SyncContext};
use diesel::pg::PgConnection;
use diesel::r2d2::{ConnectionManager, Pool};

/// This is db executor actor. can be run in parallel
pub struct DbExecutor(pub Pool<ConnectionManager<PgConnection>>);


// Actors communicate exclusively by exchanging messages. 
// The sending actor can optionally wait for the response. 
// Actors are not referenced directly, but by means of addresses.
// Any rust type can be an actor, it only needs to implement the Actor trait.
impl Actor for DbExecutor {
    type Context = SyncContext<Self>;
}
```
To use this Actor we need to set up `actix-web` server. We have the following in `src/app.rs`. We are leaving the resource builders empty for now. This is where the meat of the routing is going to go.

```rust
// app.rs
use actix::prelude::*;
use actix_web::{http::Method, middleware, App};
use models::DbExecutor;

pub struct AppState {
    pub db: Addr<DbExecutor>,
}

// helper function to create and returns the app after mounting all routes/resources
pub fn create_app(db: Addr<DbExecutor>) -> App<AppState> {
    App::with_state(AppState { db })
        // setup builtin logger to get nice logging for each request
        .middleware(middleware::Logger::new("\"%r\" %s %b %Dms"))

         // routes for authentication
        .resource("/auth", |r| {
        })
        // routes to invitation
        .resource("/invitation/", |r| {
        })
        // routes to register as a user after the
        .resource("/register/", |r| {
        })
}
```

```rust
// main.rs
// to avoid the warning from diesel macros
#![allow(proc_macro_derive_resolution_fallback)]

extern crate actix;
extern crate actix_web;
extern crate serde;
extern crate chrono;
extern crate dotenv;
extern crate futures;
extern crate r2d2;
extern crate uuid;
#[macro_use] extern crate diesel;
#[macro_use] extern crate serde_derive;
#[macro_use] extern crate failure;

mod app;
mod models;
mod schema;
// mod errors;
// mod invitation_handler;
// mod invitation_routes;

use models::DbExecutor;
use actix::prelude::*;
use actix_web::server;
use diesel::{r2d2::ConnectionManager, PgConnection};
use dotenv::dotenv;
use std::env;


fn main() {
    dotenv().ok();
    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    let sys = actix::System::new("Actix_Tutorial");

    // create db connection pool
    let manager = ConnectionManager::<PgConnection>::new(database_url);
    let pool = r2d2::Pool::builder()
        .build(manager)
        .expect("Failed to create pool.");

    let address :Addr<DbExecutor>  = SyncArbiter::start(4, move || DbExecutor(pool.clone()));

    server::new(move || app::create_app(address.clone()))
        .bind("127.0.0.1:3000")
        .expect("Can not bind to '127.0.0.1:3000'")
        .start();

    sys.run();
}
```

At this stage your server should compile and run on `127.0.0.1:3000`. It doesn't do anything useful for now. Let's create some Models.

##### Setting up Diesel and creating our user Model

We start with creating a model for the user. Assuming from the previous steps you have `postgres` and `diesel-cli` installed and working. In your terminal `echo DATABASE_URL=postgres://username:password@localhost/database_name > .env` replace database_name, username and password as you have setup. Then we run `diesel setup` in the terminal. This will create our database if didn't exist and setup a migration directory etc.

Let's write some `SQL`, shall we. Create migrations by `diesel migration generate users` and invitation `diesel migration generate invitations`. Open the up.sql and down.sql files in migrations folder and add with following sql respectively.

```sql

--migrations/TIMESTAMP_users/up.sql
CREATE TABLE users (
  email VARCHAR(100) NOT NULL PRIMARY KEY,
  password VARCHAR(64) NOT NULL, --bcrypt hash
  created_at TIMESTAMP NOT NULL
);

--migrations/TIMESTAMP_users/down.sql
DROP TABLE users;

--migrations/TIMESTAMP_invitations/up.sql
CREATE TABLE invitations (
  id UUID NOT NULL PRIMARY KEY,
  email VARCHAR(100) NOT NULL,
  expires_at TIMESTAMP NOT NULL
);

--migrations/TIMESTAMP_invitations/down.sql
DROP TABLE invitations;
```
Command `diesel migration run` will create the table in the DB and a file `src/schema.rs`. This is the extent I will go about diesel-cli and migrations. Please read their documentation to learn more.

At this stage we have created the tables in the db, let's write some code to create a representation of user and invitation in rust. In `models.rs` we add the following.

```rust
// models.rs
...
// --- snip
use chrono::NaiveDateTime;
use uuid::Uuid;
use schema::{users,invitations};

#[derive(Debug, Serialize, Deserialize, Queryable, Insertable)]
#[table_name = "users"]
pub struct User {
    pub email: String,
    pub password: String,
    pub created_at: NaiveDateTime, // only NaiveDateTime works here due to diesel limitations
}

impl User {
    // this is just a helper function to remove password from user just before we return the value out later
    pub fn remove_pwd(mut self) -> Self {
        self.password = "".to_string();
        self
    }
}

#[derive(Debug, Serialize, Deserialize, Queryable, Insertable)]
#[table_name = "invitations"]
pub struct Invitation {
    pub id: Uuid,
    pub email: String,
    pub expires_at: NaiveDateTime,
}
```
Check your implementation is free from errors/warnings and keep an eye on `cargo watch -x run` command in the terminal.

### [read more...](https://hgill.io/posts/auth-microservice-rust-actix-web-diesel-complete-tutorial-part-1/#read-more)