Auth reverse proxy in Golang
Tags: go, tutorial, programming, webdev
Published: 2021-05-11 23:20:39

![yesss](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z35kma3hmmrhoq52f4bt.jpg)
Let’s suppose that we have a monolith that needs to do lot of things, like process data, read and write to the DB, spawn an HTTP server for an API and also authenticate it’s incoming request.

There are scenarios where it would be nice to decouple some responsibilities. This could be because we have a team that can put all it’s effort into maintain that part of the system, or maybe there is a need to migrate the back end logic from one framework to another, etc.

In this case, this article proposes an example on how to make an HTTP server that stands in front the main server, authorize the incoming requests and then it forwards them to the back end or returns a response with a 401 status code.

----

## Making our target server


{% gist 
https://gist.github.com/LautaroJayat/c6bffed5975ab81e8e10e0c92fa1a1fb %}

In our target we have only two main blogs: our `main` function and a `welcome` handler that will be used for all of the incoming request. The first one just makes a new Mux for an HTTP server and then starts listening in port 8080.
The handler is also simple:

First, it checks if the incoming request comes from certain port (like a white list). If not, it gives a 401 response and return.

Then, if we have have an exported environment variable named SECRET, it checks if the incoming request has any header with that secret, and if it matches the one we have exported. If not, it respond with another 401 status code and return.

If everything is OK, it adds 3 headers and returns a body that says `“hi from target server”.`


## Making our proxy server


![Our project structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rmjcjdcslw92iso3k22u.png)

We want something simple that could be extensible, so we will be having a main function that will instantiate our reverse proxy, the main server and it’s handlers:

{% gist https://gist.github.com/LautaroJayat/1446bdd97b91016f942226b694c54942 %}

In our reverse proxy we call a function that will return a function with the following signature: `func (*http.Request)`, this will be our director function.

The Director in the `httputil.ReverseProxy` struct is just a function that can access the request and make modifications like adding headers, change the URL, host, cookies depending on our forwarding logic.

For example, in `director/director.go` we have this:

{% gist https://gist.github.com/LautaroJayat/d8b8b7be70f64d372d2bd4767c0672e3 %}

Basically, we have a function that access two environment variables and then returns a function that modifies the request to froward to the new host.

In our case, the HOST variable will be the host of our target server. This means that any incoming request served by the reverse proxy will be mapped to the target server.

For example, in our case `http://localhost:8081/hi` will be passed to `http://localhost:8080/hi`.

Now lets see what is happening in our router package.

In line 25 of our main.go file we had `r := rt.NewRouter(&rp)`. So there is a function that receives a pointer to the reverse proxy and then it returns a router from the [github.com/julienschmidt/httprouter](https://github.com/julienschmidt/httprouter) package.

If we go to our router/router.go file we have the following:

{% gist https://gist.github.com/LautaroJayat/4a4d0f5593851208f354ebeb7fd67c1f %}

There we instantiate a router and assign some handlers for each desired method.

At first view it appears that we check for an authorization header in the most common HTTP verbs and we just forwards the `OPTIONS` verb.

Although it appears simple, this design allow us to plug and play different auth strategies depending on the structure and path of the URL, making it extensible and easy to maintain.
Now is time for the strategies.

Our strategies must be of the type `httprouter.Handler` because is what our router uses. This is just a function with the following signature:

```go
func (http.ResponseWriter, *http.Request, httprouter.Params)
```

In our example, the `FwdOptionsReq` function is the most simple case.

{% gist https://gist.github.com/LautaroJayat/e9ac9171f5f41a14768af82c5fcbc95c %}

As we can see, each strategy is a a function that receives the reverse proxy and then returns a function that can access the response writer, the request, the params, etc. In this case, the returned handler just serve the HTTP request using the reverse proxy and then pass the response from the target to the client.

Is interesting to notice that we are using high order functions but we could be using other things like structs that had a pointer to the reverse proxy and a methods which represents the handlers. This is just one implementation.

Let’s see now how the `strategy/checkAuthHeader.go` file looks like:

{% gist https://gist.github.com/LautaroJayat/3251c7be1efb6825a3e2cd51c0017fd6 %}

In this case, we first load two environment variables so we can use it in the handler. Then, in the returned function, we check if there is any secret. If there is one, we enrich the headers with it.

Then we get the “Auth” header and compare it with the value we got from the “AUTH” environment variable.

If both do not match, we use the provided ResponseWriter and make an early response. Only if everything is OK we continue and forward the request to the target server.

----

## A tiny demo

In order to follow this demo you can get the code cloning this github repo:

{% github LautaroJayat/go_auth_proxy_example %}

First let’s run the target server opening a terminal and running:

```bash
go run target/main.go
# outputs: Starting target server
```

Then we need to open another terminal and let’s export some variables:

```bash
export SCHEME="http"
export HOST="localhost:8080"
export AUTH="123456"
```

And then let’s just start the reverse proxy running the following command:

```bash
go run proxy/main.go
# outputs:
#
# SCHEME http
# HOST localhost:8080
# Auth 123456
# Starting proxy server
# Routes Registered
```

Now let’s try to CURL our target server:

```bash
curl -X GET -i  http://localhost:8081
# outputs: 
# 
# HTTP/1.1 403 Forbidden
# Date: Mon, 10 May 2021 17:16:57 GMT
# Content-Length: 12
# Content-Type: text/plain; charset=utf-8
#
# Auth needed
```

But, if we provide that header:

```bash
curl -X GET -H "Auth: 123456" -i  http://localhost:8081
# outputs: 
# 
# HTTP/1.1 200 OK
# Content-Length: 23
# Content-Type: text/plain; charset=utf-8
# Date: Mon, 10 May 2021 17:17:56 GMT
# X-Host: localhost:8082
# X-Method: GET
# X-Url: /
#
# hi from target server!
```

----

Thanks for reading. This was just a little example on how to use the provided reverse proxy provided the standard Go library.

If you have some improvements or corrections, please feel free to make a comment (or a pull request to the repo).

If you liked this post, please share it :)

The code for this example can be found here: [https://github.com/LautaroJayat/go_auth_proxy_example](https://github.com/LautaroJayat/go_auth_proxy_example)