Making a custom Guard in Actix Web
Published: 2023-04-15 00:35:00

The premisse here is simple, I want to make a guard to wrap my scope/route/service, and in that guard I can put things like JWT token validation! So thats what this article is all about, organizing and guarding your routes.

## First step: Setup
=====

We start our project with the good old _cargo new _good_project_name_here_, then we go to our main.rs and paste the [basic example](https://actix.rs/docs/getting-started) of the docs:

```rust
#[actix_web::main]  
async fn main() -> std::io::Result<()> {  
	HttpServer::new(|| {  
		App::new()  
			.service(hello)  
			.service(echo)  
			.route("/hey", web::get().to(|| async {
				 HttpResponse::Ok().body("Hello") })))
			})  
	.bind(("127.0.0.1", 8080))?  
	.run()  
	.await  
}
```

We'll start by laying the groundwork for our next steps, our folder structure looks like this at the start, though most of it is empty:

.
|── Cargo.lock
|── Cargo.toml
|── src
	|── handlers
    |   ├── mod.rs
    |   ├── unauthorized_handler.rs
    |   └── user_handler.rs
    |── main.rs
    |── utils
        |── custom_guards.rs
        |── mod.rs

- I like to divide my handlers by the feature that they represent (like users or books) this results in more files but I think its worth the hassle in the long run.

- At this point, is a fair warning that I'm have followed the Getting Started in Acticx Web documentation, and know what a "mod.rs" file does, if you don't I STRONGLY recommend that you go check the Docs by clicking [Here](https://actix.rs/), its really good.

## User Handler

Here we're gonna do two things, first, create a simple get request that gives us a cute json response, the second, is create a function that lets us create a service, with the scope "/user" who will contain all requests related to a user.

First things first, that our get request:

```rust
// user_handler.rs

#[get("/")]
async fn get_all_users() -> HttpResponse {
	HttpResponse::Ok().json(json!({"test": "test"}))
}
```

Then, remember that we're not in _main.rs_, so we need to get this route there some way, and we also want it to work inside a scope and be protected by a guard, to do that, we need the following code:

```rust
// user_handler.rs

pub fn user_scoped_config(cfg: &mut web::ServiceConfig) {
	cfg.service(web::scope("/user")
		.guard(guard::fn_guard(|ctx| true)) // Will change this soon
		.service(get_all_users),
	);
}
```

The line _.guard(guard::fn_guard(|ctx| true))_ is where our custon guard will rest, its a fn_guard() as you can see, and it can take a function that get our context as parameter.

## Custom Guard

Now we'll create the function that will be our mighty guard! OUr objective here is to create a a funciton that recieves our context and returns a boolean, inside this function we can access our request data and decide if it can have access to our routes.
Here I'll call it _verify_token_ just as a reminder that it can be used for that, this function will only verify if the Authorization header exists, if not, access will be  denied,

```rust
//custom_guards.rs

pub fn verify_token(ctx: &GuardContext) -> bool {
	let auth_header = ctx.head().headers().get("authorization");
	if auth_header.is_none() {
		HttpResponse::Unauthorized().json(json!({"error" : "Acesso negado"}));
		return false;
	} else {
		return true;
	}
}
```

Now, we need to change our _fn_guard_ inside _user_handler.rs_, to look like this:

```rust
// user_handler.rs

pub fn user_scoped_config(cfg: &mut web::ServiceConfig) {
	cfg.service(web::scope("/user")
		.guard(guard::fn_guard(verify_token)
		.service(get_all_users),
	);
}
```

And done! Our routes are protected from requests without the Authorization Header!

## Bonus: Handle unauthorized requests

Well, now that we blocked the access, we migth want to send a pretty response explaining why said request was blocked, for that we can use  a _default_service_! Is quite simple, if a request fails a guard of a scope, it will try to match with other route in its father (in this case, our root app) so we can create a handler that will take care of that!

Inside _unauthorized_handler.rs_ insert the following code:

```rust
pub async fn handle_unauthorized() -> HttpResponse {
	HttpResponse::Unauthorized().json(json!({"error": "Unauthorized"}))
}
```
## Grand finale

Now we just need to use those beautiful thins inside our main function!
If you hook it all up in the right way you main should look like this:

```rust
mod handlers;
mod utils;

use actix_web::{
	web,
	App,
	HttpServer,
};

use handlers::{
	unauthorized_handler::handle_unauthorized,
	user_handler::user_scoped_config,
};

struct ApiGlobalState {}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
	let app_data = web::Data::new(ApiGlobalState {});
	HttpServer::new(move || {
		App::new()
			.app_data(app_data.clone())
			.configure(user_scoped_config)
			.default_service(web::route().to(handle_unauthorized))
	})
	.bind(("127.0.0.1", 8080))?
	.run()
	.await
}
```

And it's done! Hope that this was a good reading for you, and if you think I made some mistake, please reach out to me, I would love to get better with your help!
Thanks and have a nice time :)