Auth Web Microservice with rust using Actix-Web - Complete Tutorial Part 2
Tags: rust, tutorial, learning, webdev
Published: 2018-10-30 09:21:27

---
title: "Auth Web Microservice with rust using Actix-Web - Complete Tutorial Part 2"
published: true
description: "Rust tutorial 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
---
###### Originally posted on [gill.net.in](https://gill.net.in)

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/) 

##### Welcome back to part 2 of the tutorial
In part one we have successfully setup bulk base of our Auth microservice. Congratulations if you have your app running and no errors so far. Checkout [Part One](https://hgill.io/posts/auth-microservice-rust-actix-web-diesel-complete-tutorial-part-1/) if you haven't already. Complete code for this tutorial is on [Gitlab](https://gitlab.com/mygnu/rust-auth-server/tree/master) master branch.


##### Verify User Email or not?
Picking up from part one, we now have as server that takes an email address from a request and spits out a JSON response with an invitation object. In part one I said that we will send an email to the user, after some thought and feedback, we will be skipping this part now (look out for part 3). The service that I use is sparkpost and you as the reader of this tutorial may not have an account with them(which is free for small usage). 

**WARNING: Do not use this workaround in any real app without proper email verification.** 

##### Workaround
For now we will use the http response from the server to verify the email so to speak. The simplest way of creating an email verification is to have our server to use some sort of secret sent via the email to the user email and have them click an link with the secret to verify. We could use the `UUID` from the invitation object as a secret. Let's say the client gets an invitation after entering their email with uuid `67a68837-a059-43e6-a0b8-6e57e6260f0d`.

We can send a request to register a new user with the above `UUID` in the url. Our server can take that id and find the Invitation object in the database and then compare the expiry date with current time. If all of these conditions are true we would let the user register, otherwise send an error response back. **For now we will return the Invitation Object to the client as a workaround.** Email support will get implemented next in **Part 3**.

##### Error handling and the `From` trait
Rust provides use with really powerful tools that we can use to convert one type of error to another. In this app we will be doing a few operations using different carets i.e. saving data with diesel, hashing password with bcrypt etc. These operations may return errors but we need to convert them to our custom error type. `std::convert::From` is a trait that allows us to convert that. Read more about the `From` trait [here](https://doc.rust-lang.org/std/convert/trait.From.html). By implementing the `From` trait we can use the `?` operator to [propagate errors](https://doc.rust-lang.org/book/second-edition/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator) of many different types that would get converted to our `ServiceError` type.

Our error is defined in the `errors.rs`, let's implement some `From` traits by adding impls for `From` uuid and diesel errors, we will also add a `Unauthorized` variant to our ServiceError enum. the file look like the following:

```rust
// errors.rs
use actix_web::{error::ResponseError, HttpResponse};
use std::convert::From;
use diesel::result::{DatabaseErrorKind, Error};
use uuid::ParseError;


#[derive(Fail, Debug)]
pub enum ServiceError {
    #[fail(display = "Internal Server Error")]
    InternalServerError,

    #[fail(display = "BadRequest: {}", _0)]
    BadRequest(String),

    #[fail(display = "Unauthorized")]
    Unauthorized,
}

// impl ResponseError trait allows to convert our errors into http responses with appropriate data
impl ResponseError for ServiceError {
    fn error_response(&self) -> HttpResponse {
        match *self {
            ServiceError::InternalServerError => HttpResponse::InternalServerError().json("Internal Server Error, Please try later"),
            ServiceError::BadRequest(ref message) => HttpResponse::BadRequest().json(message),
            ServiceError::Unauthorized => HttpResponse::Unauthorized().json("Unauthorized")
        }
    }
}

// we can return early in our handlers if UUID provided by the user is not valid
// and provide a custom message
impl From<ParseError> for ServiceError {
    fn from(_: ParseError) -> ServiceError {
        ServiceError::BadRequest("Invalid UUID".into())
    }
}

impl From<Error> for ServiceError {
    fn from(error: Error) -> ServiceError {
        // Right now we just care about UniqueViolation from diesel
        // But this would be helpful to easily map errors as our app grows
        match error {
            Error::DatabaseError(kind, info) => {
                if let DatabaseErrorKind::UniqueViolation = kind {
                    let message = info.details().unwrap_or_else(|| info.message()).to_string();
                    return ServiceError::BadRequest(message);
                }
                ServiceError::InternalServerError
            }
            _ => ServiceError::InternalServerError
        }
    }
}

```
Great! this will all become handy as we go.

##### Get some help
We all need some help at times. We will need to hash the password before we store it in the DB. There was a suggestion on Reddit rust community abut what algorithm to use. `argon2` was suggested [here](https://www.reddit.com/r/rust/comments/9qdz40/auth_web_microservice_with_rust_using_actixweb/e8bqfam). I agree with the suggestion but for the sake of simplicity I have decided to use `bcrypt`. BTW `bcrypt` algorithm is a widely used in production, and the bcrypt crate provides a really nice interface to hash and verify passwords.

To keep some concerns separate we create a new file `src/utils.rs` and define a helper hashing function as following.
```rust
//utils.rs
use bcrypt::{hash, DEFAULT_COST};
use errors::ServiceError;
use std::env;

pub fn hash_password(plain: &str) -> Result<String, ServiceError> {
    // get the hashing cost from the env variable or use default
    let hashing_cost: u32 = match env::var("HASH_ROUNDS") {
        Ok(cost) => cost.parse().unwrap_or(DEFAULT_COST),
        _ => DEFAULT_COST,
    };
    hash(plain, hashing_cost).map_err(|_| ServiceError::InternalServerError)
}
```
You may have noticed that we return a `Result` and use map_error() to return our custom error. This is to allow using the `?` operator later when we call this function (Another way to convert error is to implement `From` trait for the error returned by bcrypt function, instead). 

While we are at it, lets add a convenience method to our `User` struct defined in `models.rs` in the last tutorial. We also removing the **remove_pwd()** method, instead we will define another struct `SlimUser` that does not have the password field. We impl `From` trait to generate **SlimUser** from the user. It will all become clear as we get to use this in a while. 
```rust
use chrono::{NaiveDateTime, Local};
use std::convert::From;
//... snip
impl User {
    pub fn with_details(email: String, password: String) -> Self {
        User {
            email,
            password,
            created_at: Local::now().naive_local(),
        }
    }
}
//--snip
#[derive(Debug, Serialize, Deserialize)]
pub struct SlimUser {
    pub email: String,
}

impl From<User> for SlimUser {
    fn from(user: User) -> Self {
        SlimUser {
           email: user.email
        }
    }
}

``` 
Don't forget to add `extern crate bcrypt;` and `mod utils` in your `main.rs`.
Another neat thing I forgot in the part one was logging to console. To enable it add the following to the `main.rs`
```rust
extern crate env_logger;
// --snip

fn main(){
    dotenv().ok();
    std::env::set_var("RUST_LOG", "simple-auth-server=debug,actix_web=info");
    env_logger::init();
    //--snip
}


```

##### Registering User
If you remember from the previous tutorial, we created a handler for `Invitation` now let's create a handler for registering a user. We are going to create a struct `RegisterUser` with some data that allows us to verify the Invitation and then create and return a user from the database. 

Create a new file `src/register_handler.rs` and add `mod register_handler;` to you `main.rs`.

```rust
// register_handler.rs
use actix::{Handler, Message};
use chrono::Local;
use diesel::prelude::*;
use errors::ServiceError;
use models::{DbExecutor, Invitation, User, SlimUser};
use uuid::Uuid;
use utils::hash_password;

// UserData is used to extract data from a post request by the client
#[derive(Debug, Deserialize)]
pub struct UserData {
    pub password: String,
}

// to be used to send data via the Actix actor system
#[derive(Debug)]
pub struct RegisterUser {
    pub invitation_id: String,
    pub password: String,
}


impl Message for RegisterUser {
    type Result = Result<SlimUser, ServiceError>;
}


impl Handler<RegisterUser> for DbExecutor {
    type Result = Result<SlimUser, ServiceError>;
    fn handle(&mut self, msg: RegisterUser, _: &mut Self::Context) -> Self::Result {
        use schema::invitations::dsl::{invitations, id};
        use schema::users::dsl::users;
        let conn: &PgConnection = &self.0.get().unwrap();

        // try parsing the string provided by the user as url parameter
        // return early with error that will be converted to ServiceError
        let invitation_id = Uuid::parse_str(&msg.invitation_id)?;

        invitations.filter(id.eq(invitation_id))
            .load::<Invitation>(conn)
            .map_err(|_db_error| ServiceError::BadRequest("Invalid Invitation".into()))
            .and_then(|mut result| {
                if let Some(invitation) = result.pop() {
                    // if invitation is not expired
                    if invitation.expires_at > Local::now().naive_local() {
                        // try hashing the password, else return the error that will be converted to ServiceError
                        let password: String = hash_password(&msg.password)?;
                        let user = User::with_details(invitation.email, password);
                        let inserted_user: User = diesel::insert_into(users)
                            .values(&user)
                            .get_result(conn)?;

                        return Ok(inserted_user.into()); // convert User to SlimUser
                    }
                }
                Err(ServiceError::BadRequest("Invalid Invitation".into()))
            })
    }
}
```

##### Parsing url parameters
actix-web has many easy ways to extract data from a request.

One of the way is to use Path extractor. 

> [Path](https://actix.rs/actix-web/actix_web/struct.Path.html) provides information that can be extracted from the Request’s path. You can deserialize any variable segment from the path.

This will allow us to create a unique path for every invitation to be register as a user.

Let's modify our register route in the `app.rs` file, and add a handler function that we will implement later.

```rust
// app.rs
/// creates and returns the app after mounting all routes/resources
// add use statement at the top.
use register_routes::register_user;
//...snip
pub fn create_app(db: Addr<DbExecutor>) -> App<AppState> {
    App::with_state(AppState { db })
        //... snip
        // routes to register as a user
        .resource("/register/{invitation_id}", |r| {
           r.method(Method::POST).with(register_user);
        })

}
```

You may want to comment the changes out for now as things are not implemented and keep your app compiled and running. (I do that whenever possible, for continuous feedback).

All we need now is to implement that register_user() function that extracts the data from request sent by the client, invoke the handler by sending `RegisterUser` message to the Actor. Apart from the url parameter we also need to extract password from the client. We have already created a `UserData` struct in `register_handler.rs` for that purpose. We will ues the type `Json` to create UserData struct.

> Json allows to deserialize a request body into a struct. To extract typed information from a request’s body, the type T must implement the Deserialize trait from serde.

Create a new file `src/register_routes.rs` and add `mod register_routes;` to you `main.rs`.

```rust
use actix_web::{AsyncResponder, FutureResponse, HttpResponse, ResponseError, State, Json, Path};
use futures::future::Future;

use app::AppState;
use register_handler::{RegisterUser, UserData};


pub fn register_user((invitation_id, user_data, state): (Path<String>, Json<UserData>, State<AppState>))
                     -> FutureResponse<HttpResponse> {
    let msg = RegisterUser {
        // into_inner() returns the inner string value from Path
        invitation_id: invitation_id.into_inner(),
        password: user_data.password.clone(),
    };

    state.db.send(msg)
        .from_err()
        .and_then(|db_response| match db_response {
            Ok(slim_user) => Ok(HttpResponse::Ok().json(slim_user)),
            Err(service_error) => Ok(service_error.error_response()),
        }).responder()
}
```

##### Test your implementation
After taking care of the errors if you had any, lets give it a spin.
invoking
```bash
curl --request POST \
  --url http://localhost:3000/invitation \
  --header 'content-type: application/json' \
  --data '{
	"email":"name@domain.com"
}'
```
Should return something like 
```json
{
    "id": "f87910d7-0e33-4ded-a8d8-2264800d1783",
    "email": "name@domain.com",
    "expires_at": "2018-10-27T13:02:00.909757"
}
```
Imagine that we sent an email to the user by creating a link that takes to a form for the user to fill. From there we would have our client post a request to  http://localhost:3000/register/f87910d7-0e33-4ded-a8d8-2264800d1783. For the sake of this demo you can test your app with the following test command.

```bash
curl --request POST \
  --url http://localhost:3000/register/f87910d7-0e33-4ded-a8d8-2264800d1783 \
  --header 'content-type: application/json' \
  --data '{"password":"password"}'
```
Which should return something like
```json
{
    "email": "name@domain.com"
}
```
Running the command again would result with an error 
```json
 "Key (email)=(name@domain.com) already exists."
```
Congratulations now you have a web service that can invite, verify and create a user and even send you a semi useful error message. 🎉🎉

##### Let's do Auth
According to w3.org: 

> The general concept behind a token-based authentication system is simple. Allow users to enter their username and password in order to obtain a token which allows them to fetch a specific resource - without using their username and password. Once their token has been obtained, the user can offer the token - which offers access to a specific resource for a time period - to the remote site.

Now how you choose to exchange that token can have security implications. You will find many discussions/debates around the internet and many ways that people use. I am very wary of storing things on client side that can be accessed by the client side JavaScript. Unfortunately this approach is suggested in thousands of tutorial everywhere.  Here is a good read [Stop using JWT for sessions](http://cryto.net/%7Ejoepie91/blog/2016/06/13/stop-using-jwt-for-sessions/). 


I am not sure here, what to suggest you as the reader, apart from `don't follow online tutorials blindly and do your own research`. The purpose of this tutorial is to learn about Actix-web and rust not how to prevent your server from vulnerabilities. For the sake of this tutorial we will be using http only cookies to exchange tokens.

**PLEASE DO NOT USE IN PRODUCTION.** 

Now that is out of the way 😰, let's see what we can do here. actix-web provides us with a neat way as middleware of handling a session cookie documented here [actix_web::middleware::identity](https://actix.rs/actix-web/actix_web/middleware/identity/index.html). To enable this functionality we modify our `app.rs` file as following.
```rust
use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService};
use chrono::Duration;
//--snip
pub fn create_app(db: Addr<DbExecutor>) -> App<AppState> {
    // secret is a random 32 character long base 64 string
    let secret: String = std::env::var("SECRET_KEY").unwrap_or_else(|_| "0".repeat(32));
    let domain: String = std::env::var("DOMAIN").unwrap_or_else(|_| "localhost".to_string());

    App::with_state(AppState { db })
        .middleware(Logger::default())
        .middleware(IdentityService::new(
            CookieIdentityPolicy::new(secret.as_bytes())
                .name("auth")
                .path("/")
                .domain(domain.as_str())
                .max_age(Duration::days(1)) // just for testing
                .secure(false),
        ))
        //--snip
}
```
This gives us very convenient methods like `req.remember(data)`, `req.identity()` and `req.forget()` etc on `HttpRequest` prams in our route functions. That in turn will set and remove the auth cookie from client. 

##### JWT?
While writing this tutorial I ran into a few discussions about What JWT lib to use. From a simple search I found a few, and decided to go with [frank_jwt](https://crates.io/crates/frank_jwt) but then [Vincent Prouillet](https://github.com/Keats) [Pointed out the incompleteness](https://github.com/GildedHonour/frank_jwt/blob/master/src/lib.rs#L296-L323) and suggested to go with [jsonwebtoken](https://crates.io/crates/jsonwebtoken). After having trouble with using the lib I got a great response from them. Now the repo has working examples and I was able to implement the following default solution. Please note this is not the most secure implementation of JWT you might want to look up resources for making it better suit your needs.

Before we create auth handler and route function, let's add some helper functions to our `util.rs` for jwt encoding and decoding. Don't forget to add `extern crate jsonwebtoken as jwt;` in your `main.rs`. 

**I Will gladly accept a PR if someone has a better implementation.**

```rust
// utils.rs
use models::SlimUser;
use std::convert::From;
use jwt::{decode, encode, Header, Validation};
use chrono::{Local, Duration};
//--snip

#[derive(Debug, Serialize, Deserialize)]
struct Claims {
    // issuer
    iss: String,
    // subject
    sub: String,
    //issued at
    iat: i64,
    // expiry
    exp: i64,
    // user email
    email: String,
}

// struct to get converted to token and back
impl Claims {
    fn with_email(email: &str) -> Self {
        Claims {
            iss: "localhost".into(),
            sub: "auth".into(),
            email: email.to_owned(),
            iat: Local::now().timestamp(),
            exp: (Local::now() + Duration::hours(24)).timestamp(),
        }
    }
}

impl From<Claims> for SlimUser {
    fn from(claims: Claims) -> Self {
        SlimUser { email: claims.email }
    }
}

pub fn create_token(data: &SlimUser) -> Result<String, ServiceError> {
    let claims = Claims::with_email(data.email.as_str());
    encode(&Header::default(), &claims, get_secret().as_ref())
        .map_err(|_err| ServiceError::InternalServerError)
}

pub fn decode_token(token: &str) -> Result<SlimUser, ServiceError> {
    decode::<Claims>(token, get_secret().as_ref(), &Validation::default())
        .map(|data| Ok(data.claims.into()))
        .map_err(|_err| ServiceError::Unauthorized)?
}

// take a string from env variable
fn get_secret() -> String {
    env::var("JWT_SECRET").unwrap_or("my secret".into())
}
```
##### Auth Handling
You know the drill now 😉, lets create a new file `src/auth_handler.rs` and add `mod auth_handler;` to you `main.rs`.

```rust
//auth_handler.rs
use actix::{Handler, Message};
use diesel::prelude::*;
use errors::ServiceError;
use models::{DbExecutor, User, SlimUser};
use bcrypt::verify;
use actix_web::{FromRequest, HttpRequest, middleware::identity::RequestIdentity};

#[derive(Debug, Deserialize)]
pub struct AuthData {
    pub email: String,
    pub password: String,
}

impl Message for AuthData {
    type Result = Result<SlimUser, ServiceError>;
}


impl Handler<AuthData> for DbExecutor {
    type Result = Result<SlimUser, ServiceError>;
    fn handle(&mut self, msg: AuthData, _: &mut Self::Context) -> Self::Result {
        use schema::users::dsl::{users, email};
        let conn: &PgConnection = &self.0.get().unwrap();
        let mismatch_error = Err(ServiceError::BadRequest("Username and Password don't match".into()));

        let mut items = users
            .filter(email.eq(&msg.email))
            .load::<User>(conn)?;

        if let Some(user) = items.pop() {
            match verify(&msg.password, &user.password) {
                Ok(matching) => {
                    if matching { return Ok(user.into()); } else { return mismatch_error; }
                }
                Err(_) => { return mismatch_error; }
            }
        }
        mismatch_error
    }
}
```
Handler above takes the `AuthData` struct that contains email and password sent by the client. We use the email to extract user from the db and use bcrypt verify function to match the password. If all goes well we return the user or we return `BadRequest` error.

Now let's also create `src/auth_routes.rs` with the following content:

```rust
// auth_routes.rs
use actix_web::{AsyncResponder, FutureResponse, HttpResponse, HttpRequest, ResponseError, Json};
use actix_web::middleware::identity::RequestIdentity;
use futures::future::Future;
use utils::create_token;

use app::AppState;
use auth_handler::AuthData;

pub fn login((auth_data, req): (Json<AuthData>, HttpRequest<AppState>))
             -> FutureResponse<HttpResponse> {
    req.state()
        .db
        .send(auth_data.into_inner())
        .from_err()
        .and_then(move |res| match res {
            Ok(slim_user) => {
                let token = create_token(&slim_user)?;
                req.remember(token);
                Ok(HttpResponse::Ok().into())
            }
            Err(err) => Ok(err.error_response()),
        }).responder()
}

pub fn logout(req: HttpRequest<AppState>) -> HttpResponse {
    req.forget();
    HttpResponse::Ok().into()
}
```
Our login method extracts the `AuthData` from request and sends a message to `DbEexcutor` Actor handler which we implemented in auth_handler.rs. Here if all is good we get a user returned to us, We use our helper function that we defined earlier in utils.rs to create a token and call `req.remember(token)`. This in turn sets a cookie header with token for the client to save.

Last thing we need to do now is use login/logout function in our `app.rs`. Change the `.rsource("/auth")` closure to following:
```rust
.resource("/auth", |r| {
            r.method(Method::POST).with(login);
            r.method(Method::DELETE).with(logout);
        })
```
Don't forget to add `use auth_routes::{login, logout};` at the top of the file though.

##### Test run Auth
If you have been following the tutorial, you have already created a user with email and password. Use the following curl command to test our server.
```bash
curl -i --request POST \
  --url http://localhost:3000/auth \
  --header 'content-type: application/json' \
  --data '{
        "email": "name@domain.com",
        "password":"password"
}'

## response
HTTP/1.1 200 OK
set-cookie: auth=iqsB4KUUjXUjnNRl1dVx9lKiRfH24itiNdJjTAJsU4CcaetPpaSWfrNq6IIoVR5+qKPEVTrUeg==; HttpOnly; Path=/; Domain=localhost; Max-Age=86400
content-length: 0
date: Sun, 28 Oct 2018 12:36:43 GMT
```
If you received a 200 response like above with a set-cookie header, Congratulations you have successfully logged in.

To test the logout we send a DELETE request to the `/auth`, make sure you get set-cookie header with empty data and immediate expiry date.
```bash
curl -i --request DELETE \
  --url http://localhost:3000/auth

## response
HTTP/1.1 200 OK
set-cookie: auth=; HttpOnly; Path=/; Domain=localhost; Max-Age=0; Expires=Fri, 27 Oct 2017 13:01:52 GMT
content-length: 0
date: Sat, 27 Oct 2018 13:01:52 GMT
```

##### Implementing a protected route
The whole point of having Auth is to have way to verify a request is coming from a authenticated client. Actix-web has a trait `FromRequest` that we can implement on any type and then use that to extract data from the request. See documentation [here](https://actix.rs/actix-web/actix_web/trait.FromRequest.html). We will add the following at the bottom of `auth_handler.rs`.

```rust
//auth_handler.rs
//--snip
use actix_web::FromRequest;
use utils::decode_token;
//--snip

// we need the same data as SlimUser
// simple aliasing makes the intentions clear and its more readable
pub type LoggedUser = SlimUser;

impl<S> FromRequest<S> for LoggedUser {
    type Config = ();
    type Result = Result<LoggedUser, ServiceError>;
    fn from_request(req: &HttpRequest<S>, _: &Self::Config) -> Self::Result {
        if let Some(identity) = req.identity() {
            let user: SlimUser = decode_token(&identity)?;
            return Ok(user as LoggedUser);
        }
        Err(ServiceError::Unauthorized)
    }
}
```
We chose to use a type alias rather that creating a whole new type. When we extract `LoggedUser` from a request reader would know that its is the authenticated user. FromRequest trait simply tries to Deserialize the string from the cookie into our struct and if that fails it simply returns Unauthorized error back. To test this we need to add an actual route to or app. We simply add another function to `auth_routes.rs`
```rust
//auth_routes.rs
//--snip

pub fn get_me(logged_user: LoggedUser) -> HttpResponse {
    HttpResponse::Ok().json(logged_user)
}
```
To call this we register this method in `app.rs` resources. It would look like following.
```rust
//app.rs
use auth_routes::{login, logout, get_me};
//--snip

.resource("/auth", |r| {
    r.method(Method::POST).with(login);
    r.method(Method::DELETE).with(logout);
    r.method(Method::GET).with(get_me);
})
//--snip
```
##### Testing logged in user
Try the following Curl command in the terminal.
```bash
curl -i --request POST \
  --url http://localhost:3000/auth \
  --header 'content-type: application/json' \
  --data '{
        "email": "name@domain.com",
        "password":"password"
}'
# result would be something like
HTTP/1.1 200 OK
set-cookie: auth=HdS0iPKTBL/4MpTmoUKQ5H7wft5kP7OjP6vbyd05Ex5flLvAkKd+P2GchG1jpvV6p9GQtzPEcg==; HttpOnly; Path=/; Domain=localhost; Max-Age=86400
content-length: 0
date: Sun, 28 Oct 2018 19:16:12 GMT

## and then pass the cookie back for a get request
curl -i --request GET \
  --url http://localhost:3000/auth \
  --cookie auth=HdS0iPKTBL/4MpTmoUKQ5H7wft5kP7OjP6vbyd05Ex5flLvAkKd+P2GchG1jpvV6p9GQtzPEcg==
## result
HTTP/1.1 200 OK
content-length: 27
content-type: application/json
date: Sun, 28 Oct 2018 19:21:04 GMT

{"email":"name@domain.com"}

```
It should successfully return your email as json back. Only logged in users or requests with a valid auth cookie and token will pass through the routes you extract the `LoggedUser`.

##### What's next?
In Part 3 of this tutorial we will create the **email verification** and a **frontend** for this app. I wold like to use some sort of rust html templating system for that. At the same time I'm learning Angular so I might slap a small angular app at the front of it.
Get in touch with me on [twitter](https://twitter.com/mygnu_) if you have a question or suggestion about any of it.