API Rest con Go (Golang) y PostgreSQL Parte 5
Tags: opensource, go, api, rest
Published: 2020-08-24 15:19:56

## Requisitos:

* Conocimientos básicos sobre el lenguaje [go](https://golang.org/).
* Conocimiento sobre HTTP y REST recomendable.
* [Go](https://golang.org/dl/) en su versión 1.11 o superior.
* [PostgreSQL](https://www.postgresql.org/download/) en su versión 9.6 o superior.
* Conocimientos básicos de [SQL](https://en.wikipedia.org/wiki/SQL) deseables.
* Ganas de aprender.

## Que vamos a desarrollar

Crearemos un API Rest para un microblog, estilo [twitter](https://twitter.com/notifications) en su versión mas básica. Con un modelo de usuarios y uno de publicaciones. Con un [CRUD](https://en.wikipedia.org/wiki/Create%2C_read%2C_update_and_delete) completo en cada caso, autenticación con [JSON Web Tokens](https://jwt.io/), creación, uso de middlewares y asegurar contraseña por con [bcrypt](https://en.wikipedia.org/wiki/Bcrypt). ¿Suena interesante? ¡Entonces vamos!

![Meme](https://media.giphy.com/media/Z6HwUgq2o5aKs/giphy.gif)

### Indice

* [Parte 1 - Preparación](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-m0o)
* [Parte 2 - Modelos](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-parte-2-137l)
* [Parte 3 - Base de datos](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-parte-3-29eb)
* [Parte 4 - Manejo de peticiones](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-parte-4-4i31)
* **Parte 5 - Middlewares y JWT**

[Código fuente](https://github.com/orlmonteverde/go-postgres-microblog/tree/v1.3.0) hasta el momento.

## Activando el API

Lo primero que debemos hacer es asegurarnos de que el **API** esta corriendo.

```bash
go build ./cmd/microblog && ./microblog
```

## Realizando peticiones

Cuando desarrollamos un **API REST** nuestro interés, por lo general, es que sea consumida por algún cliente, bien sea una aplicación móvil, web, otro servidor, entre otros. Pero no podemos esperar hasta conectar el cliente para saber si todo funciona como esperamos, para eso existen las pruebas. Hay diferentes tipos de pruebas, y es un tema que excede el propósito de este articulo, en esta ocasión probaremos cada *endpoint* manualmente con un par de herramientas populares para dicho fin: [curl](https://curl.haxx.se/) y [postman](https://www.postman.com/).

La intención es la siguiente: muestro como realizar la petición con [postman](https://www.postman.com/) por medio de un [GIF](https://es.wikipedia.org/wiki/Graphics_Interchange_Format) y posteriormente les comparto como hacer la misma petición desde la terminal usando [curl](https://curl.haxx.se/), no es necesario que utilicen ambos, el uso de estos programas no sera cubierto a detalle pero si lo suficiente para las necesidades de nuestro proyecto. Dicho esto, manos a la obra.

![Keyboard](https://media.giphy.com/media/fsoCk5kgOcYMM/giphy.gif)

### Creando un usuario

Si vamos a utilizar [postman](https://www.postman.com/) necesitamos tenerlo instalado en nuestro ordenador, si no es así podemos descargarlo desde [aquí](https://www.postman.com/downloads/).

Lo primero que haremos sera crear una colección en [postman](https://www.postman.com/), este paso no es obligatorio pero nos va a facilita reutilizar las peticiones luego.

![new collection](https://dev-to-uploads.s3.amazonaws.com/i/3zakslleks1wxzciump1.gif)

Ahora podemos proceder a crear nuestro usuario, simplemente realizando una petición de tipo **POST** a la dirección *http://localhost:9000/api/v1/users/* con el contenido correspondiente en formato [JSON](https://json.org/json-es.html).

![create user](https://dev-to-uploads.s3.amazonaws.com/i/5ow94huzkrkpbrrafvu1.gif)

O por medio de [curl](https://curl.haxx.se/).

```bash
curl -X POST -H 'Content-Type: application/json' \
  -d '{"first_name": "orlando", "last_name": "monteverde", "username": "orlmonteverde", "email": "orlmonteverde@gmail.com", "password": "123456"}' \
  http://localhost:9000/api/v1/users/
```

En cualquier caso habremos registrado un nuevo usuario. Ahora que ya sabemos como se hace, los invito a insertar un par mas, como practica y para poder usarlos en las siguientes pruebas.

### Obtener todos los usuarios

De manera similar a la caso anterior vamos a realizar una petición a la dirección *http://localhost:9000/api/v1/users/* pero esta vez sera de tipo **GET** y no requiere contenido.

![get users](https://dev-to-uploads.s3.amazonaws.com/i/jb4fykfq07ujv9a1byhl.gif)

O con [curl](https://curl.haxx.se/).

```bash
curl http://localhost:9000/api/v1/users/
```

### Obtener un usuario

En el caso anterior pudimos obtener todos los usuarios, pero esta vez queremos obtener uno en especifico, esto lo logramos repitiendo la petición pero esta vez agregando el **id** del usuario al final de la dirección  *http://localhost:9000/api/v1/users/{id}*.

![get user](https://dev-to-uploads.s3.amazonaws.com/i/4uehq79lxp5lfu2w0ulg.gif)

O con [curl](https://curl.haxx.se/).

```bash
curl http://localhost:9000/api/v1/users/11
```

### Actualizar usuario

Ahora que ya podemos crear y obtener usuarios, veamos como actualizar un usuario existente. Esta vez realizamos una petición **PUT** a la dirección *http://localhost:9000/api/v1/users/{id}*, donde el **id** debe corresponder con el del usuario que deseamos actualizar. Y, finalmente, el contenido que reemplazara al anterior en formato [JSON](https://json.org/json-es.html).

![update user](https://dev-to-uploads.s3.amazonaws.com/i/8wpj927kpvx9s21g72rf.gif)

O con [curl](https://curl.haxx.se/).

```bash
curl -X PUT -H 'Content-Type: application/json' \
  -d '{"email": "orlmonteverde@gmail.com", "first_name": "carlos", "last_name": "monteverde"}' \
  http://localhost:9000/api/v1/users/11
```

### Eliminar usuario

Para completa nuestro [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) solo faltaria la operación de borrar. Realizamos una petición **DELETE** a la dirección *http://localhost:9000/api/v1/users/{id}*, donde el **id** debe corresponder con el del usuario que deseamos borrar y con eso es suficiente.

![delete user](https://dev-to-uploads.s3.amazonaws.com/i/6gh792z1yl1noz5ezbwy.gif)

O con [curl](https://curl.haxx.se/).

```bash
curl -X DELETE http://localhost:9000/api/v1/users/11
```

Llegados a este punto, ya tenemos experiencia realizando peticiones a nuestra **API** así que, como es costumbre, para el caso de las **publicaciones** avanzaremos mas rápido ya que las operaciones son bastante parecidas.

### Crear publicación

```bash
curl -X POST -H 'Content-Type: application/json' \
  -d '{"body": "my first post", "user_id": 14}' \
  http://localhost:9000/api/v1/posts/
```

Es importante recordar que el **user_id** debe coincidir con el **id** de un usuario existente u obtendremos un error debido a la restricción que agregamos al momento de crear la tabla en la [parte 3](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-parte-3-29eb).

### Obtener todas las publicaciones

```bash
curl http://localhost:9000/api/v1/posts/
```

### Obtener una publicación

```bash
curl http://localhost:9000/api/v1/posts/11
```

### Actualizar publicación

```bash
curl -X PUT -H 'Content-Type: application/json' \
  -d '{"body": "my first post updated"}' \
  http://localhost:9000/api/v1/posts/1
```

### Borrar una publicación

```bash
curl -X DELETE http://localhost:9000/api/v1/posts/10
```

### Obtener publicaciones por usuario

```bash
curl -X GET http://localhost:9000/api/v1/posts/user/14
```

### Middleware

Sin complicarlo demasiado, un *middleware* en nuestro caso es solo una función, esta ejecutara alguna acción antes (o después) de que se ejecute una función *handler*, esto nos permite reutilizar código de una manera bastante practica, por ejemplo, obtener *logs* por consola en cada petición, recuperar el programa después de que ocurra un *panic* para evitar que nuestro servidor caiga y, prácticamente, cualquier cosa que se nos ocurra. Conociendo la firma que deben tener estas funciones, el proceso que tenga lugar en el cuerpo de la función lo definimos nosotros.

Los *middlewares* son tan usados que el paquete [chi](https://github.com/go-chi/chi) (y similares) que estamos ocupando para manejar las rutas incluye varios métodos para trabajar con ellos, ademas de incluir sus propios *middlewares*, vamos a utilizar dos que son bastante utilizados.

#### Middlewares con Chi

Regresamos al paquete **server** y realizamos unos cambios en el archivo **server.go**.

```go
// internal/server/server.go
package server

import (
	"log"
	"net/http"
	"time"

	"github.com/go-chi/chi"
	"github.com/go-chi/chi/middleware"
	v1 "github.com/orlmonteverde/go-postgres-microblog/internal/server/v1"
)

// [...]
```

Importando el paquete **middleware**, que es un subpaquete de [chi](https://github.com/go-chi/chi) y procedemos a usarlo.

```go
// internal/server/server.go
// [...]
func New(port string) (*Server, error) {
	r := chi.NewRouter()

	r.Use(middleware.Logger)
	r.Use(middleware.Recoverer)

	// [...]
}

// [...]
```

Ahora, gracias al método **Use** del puntero a **chi.Mux**, podemos agregar **middlewares** a las rutas *hijas* de **r**, en este caso **Logger**, que nos muestra *logs* por consola de las peticiones a nuestra **API**, los invito a realizar y petición y observar la terminal donde tienen corriendo el **API**, por ejemplo, esta:

```bash
curl http://localhost:9000/api/v1/users/
```

De igual forma, agregamos el *middleware* **Recoverer**, que  permite a nuestro servidor recuperarse de posibles **panics**. El paquete [chi](https://github.com/go-chi/chi) nos permite agregar **middlewares** a rutas especificas por medio del método **With** del puntero a **chi.Mux** de a siguiente manera:

```go
r.With(middleware).Get("/", handler)
```

Esto lo vamos a utilizar mas adelante cuando desarrollemos nuestro *middleware* para autorización con [JWT](https://jwt.io/).

## JSON Web Token

Hasta este punto tenemos un **API** funcional con las operaciones mas comunes, pero se encuentra totalmente expuesta, normalmente deseamos que solo un grupo determinado pueda acceder a cierta información y aun mas si hablamos de editar o borrar.

Para solucionar esto normalmente recurrimos a la [autorización](https://medium.com/unagi/autenticaci%C3%B3n-vs-autorizaci%C3%B3n-entendiendo-los-conceptos-b%C3%A1sicos-8a872385e321), donde tenemos varias opciones. Una bastante utilizada con las **API REST** es [JWT]( https://jwt.io/), les aseguro que la menciono por eso y no porque tengo como cuatro artículos diciendo que lo usaríamos 😅.

> JWT es un estándar abierto basado en JSON propuesto por IETF (RFC 7519) para la creación de tokens de acceso que permiten la propagación de identidad y privilegios o *claims* en inglés. Por ejemplo, un servidor podría generar un **token** indicando que el usuario tiene privilegios de administrador y proporcionarlo a un cliente. El cliente entonces podría utilizar el **token** para probar que está actuando como un administrador en el cliente o en otro sistema. El **token** está firmado por la clave del servidor, así que el cliente y el servidor son ambos capaz de verificar que el **token** es legítimo.
[wikipedia](https://es.wikipedia.org/wiki/JSON_Web_Token)

Lo primero que necesitamos es descargar un paquete de terceros que nos facilitara el trabajo con [JWT](https://jwt.io/), existen varios, pero personalmente me gusta [jwt-go](github.com/dgrijalva/jwt-go), así que procederemos con la descarga.

```bash
go get github.com/dgrijalva/jwt-go
```

### Paquete Claim

La información contenida dentro de nuestro **token** es lo que llamamos *claims*, aquí podemos agregar información útil, algunas estandarizadas como la fecha de expiración del **token** y también datos personalizados que nos interese incluir, por ejemplo si tiene permisos de administrador. No debemos agregar aquí información sensible/secreta como contraseñas o números de tarjeta de crédito porque esta información sera compartida en **base64**, por lo que es fácilmente decodificable, en un momento lo veremos.

Vamos a colocar toda la lógica de los *claims* en un paquete para facilitar el trabajo con los mismos.

```go
// pkg/claim/claim.go
package claim

import jwt "github.com/dgrijalva/jwt-go"

type Claim struct {
	jwt.StandardClaims
	ID string `json:"id"`
}
```

Creamos un estructura a la que llamamos **Claim** y le agregamos el tipo **StandardClaims** que nos proporciona el paquete [jwt-go](https://github.com/dgrijalva/jwt-go), este tipo incluye los campos mas comunes para el token, adicionalmente incluimos el campo **ID** que utilizaremos para agregar el **id** del usuario, aunque podemos agregar todos los campos que necesitemos es mejor no excederse ya que mientras mas contenido incluyamos mayor sera el tamaño del token.

Ahora agreguemos un método al nuestro tipo **Claim** que nos permita generar un **token** a partir de este.

```go
// pkg/claim/claim.go
func (c *Claim) GetToken(signingString string) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
	return token.SignedString([]byte(signingString))
}
```

Esta es una tarea bastante sencilla gracias al paquete [jwt-go](https://github.com/dgrijalva/jwt-go), utilizando su función **NewWithClaims** podemos obtener un **token** que incluya un *claim*, ademas debemos pasar el método que queremos utilizar para firmar el **token**, en este caso **HS256** que es bastante utilizado pero existen mas, los invito a revisar la documentación del paquete [jwt-go](https://github.com/dgrijalva/jwt-go) y la de [JWT](https://jwt.io/).

Finalmente firmamos el **token** con el método **SignedString** del puntero a **jwt.Token**, este método requiere la clave que utilizaremos para firmar el **token** y, aunque el tipo esperado por esta es una interfaz vacía (*interface{}*) no podemos utilizar una clave de tipo *string* directamente, sino que debemos pasarlo como un *slice* de *bytes*.

Agreguemos una función mas, que nos permita hacer la operación inversa, recibir un **token**, validarlo y retornar un **Claim** a partir de este.

```go
// pkg/claim/claim.go
func GetFromToken(tokenString, signingString string) (*Claim, error) {
	token, err := jwt.Parse(tokenString, func(*jwt.Token) (interface{}, error) {
		return []byte(signingString), nil
	})
	if err != nil {
		return nil, err
	}

	if !token.Valid {
		return nil, errors.New("invalid token")
	}

	claim, ok := token.Claims.(jwt.MapClaims)
	if !ok {
		return nil, errors.New("invalid claim")
	}

	iID, ok := claim["id"]
	if !ok {
		return nil, errors.New("user id not found")
	}

        d, ok := iID.(float64)
	if !ok {
		return nil, errors.New("invalid user id")
	}

	return &Claim{ID: int(id)}, nil
}
```

La función **GetFromToken** es un poco extensa pero no muy compleja, lo mas importante es el comienzo, donde utilizamos la función **Parse** del paquete [jwt-go](https://github.com/dgrijalva/jwt-go) para validar el **token** y retornar un puntero a **jwt.Token** por medio de una función de tipo **jwt.Keyfunc** con la firma `func(*jwt.Token) (interface{}, error)` que debe retornar la clave para descifrar el **token** que, en este caso, debe ser la misma que usamos para firmarlo anteriormente porque estamos utilizando [criptografía simétrica](https://es.wikipedia.org/wiki/Criptograf%C3%ADa_sim%C3%A9trica).

Hecho esto, realizamos algunas validaciones: si el método **Parse** retorno un error, si el token obtenido es valido, realizamos un **type assertion** al **Claim** dentro del **token** para convertirlo en un **jwt.MapClaims** que internamente es solo un `map[string]interface{}`, confirmamos si contiene la clave *id*, correspondiente al campo *ID* en nuestro tipo **Claim**, realizamos un **type assertion** para obtener el **id** como un **float64** y retornar un puntero al tipo **Claim** con el **id** obtenido.

### Middleware de autorización

Con esto terminamos el paquete **claim**, es momento de crear nuestro **middleware**. Comencemos por crear un nuevo directorio al que llamaremos *middleware* dentro del directorio **internal**.

```bash
mkdir internal/middleware
```

Dentro de este directorio crearemos un archivo al que llamaremos **auth.go** (o el nombre que prefieran) y agregamos el siguiente contenido.

```go
// internal/middleware/auth.go
package middleware

import (
	"errors"
	"strings"
)

func tokenFromAuthorization(authorization string) (string, error) {
	if authorization == "" {
		return "", errors.New("autorization is required")
	}

	if !strings.HasPrefix(authorization, "Bearer") {
		return "", errors.New("invalid autorization format")
	}

	l := strings.Split(authorization, " ")
	if len(l) != 2 {
		return "", errors.New("invalid autorization format")
	}

	return l[1], nil
}
```

La función **tokenFromAuthorization** no es obligatoria, pero nos permite extraer parte de la lógica del **middleware**, que crearemos a continuación, dejando el código mas limpio. Esta función espera recibir una cadena de texto a la que llamamos **authorization** con una forma similar a esta `Bearer <TOKEN>`, validamos que el formato sea correcto: que no sea una cadena de texto vaciá, que contenga el prefijo *Bearer* gracias a la función **HasPrefix** del paquete **strings**, separamos la cadena de texto con la función **Split** también del paquete **strings** utilizando como separador el espacio que divide el prefijo **Bearer** del **token**, confirmamos que el **slice** obtenido tenga exactamente dos elementos y, si todo marcha como esperamos, retornamos el segundo elemento del **slice**, el cual debería corresponder al **token**.

Suficiente preparación, es momento crear nuestro **middleware** de autorización.

```go
// internal/middleware/auth.go
package middleware

import (
	"context"
	"errors"
	"net/http"
	"os"
	"strings"

	"github.com/orlmonteverde/go-postgres-microblog/pkg/claim"
	"github.com/orlmonteverde/go-postgres-microblog/pkg/response"
)

func Authorizator(next http.Handler) http.Handler {
	signingString := os.Getenv("SIGNING_STRING")
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		authorization := r.Header.Get("Authorization")
		tokenString, err := tokenFromAuthorization(authorization)
		if err != nil {
			response.HTTPError(w, r, http.StatusUnauthorized, err.Error())
			return
		}

		c, err := claim.GetFromToken(tokenString, signingString)
		if err != nil {
			response.HTTPError(w, r, http.StatusUnauthorized, err.Error())
			return
		}

		ctx := r.Context()
		ctx = context.WithValue(ctx, "id", c.ID)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

// [...]
```

Vamos a recorrer la función paso a paso. Al inicio estamos obteniendo el valor de la variable de entorno *SIGNING_STRING* por medio de la función **Getenv** del paquete **os** y posteriormente estamos retornando una función, esta función tiene la misma estructura de las funciones **handler** que hemos utilizado anteriormente, pero no podemos retornarla como una función literal, sino que la convertimos al tipo **HandlerFunc** del paquete **http**, el cual satisface la interface **Handler**.

Dentro de esta función realizamos todo el proceso de validación del **token**, obtenemos el contenido del **Header** **Authorization**, que es la manera en la que esperamos recibir el **token**, y utilizamos la función **tokenFromAuthorization** que creamos recientemente para extraer el **token**, comprobamos si existe un error y utilizamos la función  **HTTPError** del paquete **response** para responder con un mensaje de error de igual forma a como lo hacemos en las funciones **handler** seguido de un **return** para que la función no se continúe ejecutando.

Ahora utilizamos la función **GetFromToken** que agregamos antes al paquete **claim**, y obtenemos un nuevo **Claim** a partir del **token**, confirmamos si existe un error de manera similar al caso anterior. Superado este punto podemos asumir que todo ha marchado bien y el **token** es valido, así que permitiremos que la solicitud pase al **handler** correspondiente, representado en el **middleware** como el argumento **next**, llamando a su método **ServeHTTP** y pasando el **http.ResponseWriter** y el puntero a **http.Request**.

Pero si son observadores notaran que estamos haciendo un par de cosas antes, a partir del contexto del puntero a **http.Request**, creamos un nuevo contexto con la función **WithValue** del paquete **context** al que pasamos el **ID** del usuario utilizando como clave la cadena de texto *id* y pasamos al método **ServeHTTP** el puntero a **http.Request** con este nuevo contexto. Esto ultimo no es obligatorio y no usaremos ese valor en este caso, es principalmente para mostrar que pueden pasar valores al **handler** utilizando el contexto, cosa que puede resultar bastante útil.

### Utilizando el Middleware de autorización

Una vez que tenemos listo nuestro **middleware** de autorización es momento de utilizarlo, así que vamos a regresar al archivo **user_router.go** para hacer algunos cambios.

```go
// internal/server/v1/user_router.go
package v1

import (
	// [...]
	"github.com/orlmonteverde/go-postgres-microblog/internal/middleware"
	// [...]
)

// […]

func (ur *UserRouter) Routes() http.Handler {
	r := chi.NewRouter()

	r.
		With(middleware.Authorizator).
		Get("/", ur.GetAllHandler)

	// [...]

	return r
}
```

Utilizamos el método **With** del tipo puntero a **chi.Mux** para pasar un **middleware** en linea a una ruta especifica y es suficiente, nuestro **endpoint** para obtener usuarios se encuentra protegida, vamos a probarlo.


```bash
curl http://localhost:9000/api/v1/users/
```

Si todo ha salido bien, deberíamos obtener una respuesta como la siguiente.


```json
{
  "message": "autorization is required"
}
```

Perfecto, ahora nuestra ruta esta protegida con [JWT](https://jwt.io/) y podemos agregar nuestro nuevo **middleware** a todas las rutas que nos interese proteger utiliozando cualquiera de las opciones que nos ofrece [chi](https://github.com/go-chi/chi) para manejar **middlewares**.

### Login

Hasta este punto nuestra API funciona bastante bien, pero no tenemos forma de acceder a las rutas protegidas por el **middleware** **Authorizator**, necesitamos una manera de confirmar la identidad del usuario y darle permiso de acceder a esa información, así que vamos a crear un nuevo endpoint, para permitir al usuario hacer **login**.

Regresaremos al archivo **user_router.go** para realizar algunas modificaciones y agregar nuestro nuevo **handler**.

```go
// internal/server/v1/user_router.go
package v1

// [...]
func (ur *UserRouter) LoginHandler(w http.ResponseWriter, r *http.Request) {
	var u user.User
	err := json.NewDecoder(r.Body).Decode(&u)
	if err != nil {
		response.HTTPError(w, r, http.StatusBadRequest, err.Error())
		return
	}

	defer r.Body.Close()

	ctx := r.Context()
	storedUser, err := ur.Repository.GetByUsername(ctx, u.Username)
	if err != nil {
		response.HTTPError(w, r, http.StatusNotFound, err.Error())
		return
	}

	if !storedUser.PasswordMatch(u.Password) {
		response.HTTPError(w, r, http.StatusBadRequest, "password don't match")
		return
	}

	c := claim.Claim{ID: int(storedUser.ID)}
	token, err := c.GetToken(os.Getenv("SIGNING_STRING"))
	if err != nil {
		response.HTTPError(w, r, http.StatusInternalServerError, err.Error())
		return
	}

	response.JSON(w, r, http.StatusOK, response.Map{"token": token})
}

// […]
```

Como en los pasos anteriores hemos preparado el terreno para este **endpoint** el resultado es bastante sencillo y no creo que requiera demasiada explicación, pero igual le daremos un repaso rápido.

Obtenemos el usuario que debe ser enviando en el cuerpo de la petición, el cual debe contener el **username** y la **password**, buscamos un usuario en nuestra base de datos con el **username** enviado, utilizando el método **GetByUsername** del **Repository**.

Si el usuario existe procedemos a confirmar que la contraseña enviada coincida con la que tenemos almacenada en nuestra base de datos gracias al método **PasswordMatch** que agregamos a nuestro tipo **User**.

Si la contraseña coincide generamos un nuevo **Claim**, obtenemos un nuevo **token** a partir de este, utilizando su método **GetToken**, con el misma firma que utilizamos para el desencriptado del **token** en el **middleware**.

Si todo ha sucedido correctamente retornamos el nuevo **token** y podemos usar este para acceder a la información protegida por el **middleware** **Authorizator**. Vamos a probar el nuevo **endpoint**.

Ahora debemos agregar nuestro nuevo **handler** a las rutas para poder utilizarlo.

```go
// internal/server/v1/user_router.go
package v1

// […]

func (ur *UserRouter) Routes() http.Handler {
	r := chi.NewRouter()

	// [...]

	r.Post("/login/", ur.LoginHandler)

	return r
}
```

Y ahora podemos realizar peticiones para probar nuestro **handler**.

```bash
curl -X POST http://localhost:9000/api/v1/users/login/ \
    -H 'Content-Type: application/json' \
    -d '{"username": "orlmonteverde", "password": "123456"}'
```

Con esto obtendremos un nuevo **token** y podremos usarlo en las peticiones protegidas. Por cierto, no se lo digan a nadie, pero esas son mis credenciales en [facebook](https://facebook.com) 😏.

```json
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTV9.Dd0Ej44-4P2xwZAf9sXKUUInyRJ71QNVYRJaHCxj1FE"
}
```

Ahora tenemos nuestro **token**, como mencione antes, la información contenida en este es fácilmente decodificable, podemos comprobar esto si vamos a [jwt.io](https://jwt.io/) y pegamos nuestro **token**.

![JWT](https://dev-to-uploads.s3.amazonaws.com/i/9j8tyh5p2udtzt175wwp.png)

Para finalizar, vamos a probar nuestro **token** contra el **endpoint** protegido con el **middleware**.

```bash
curl http://localhost:9000/api/v1/users/ \
    -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTV9.Dd0Ej44-4P2xwZAf9sXKUUInyRJ71QNVYRJaHCxj1FE'
```

Ahora deberíamos poder acceder a la información como antes de agregar el **middleware**. Si tienes algun problema o deseas ver el codigo completo, se encuentra disponible en [este repositorio](https://github.com/orlmonteverde/go-postgres-microblog/tree/v1.4.0).

Gracias por acompañarme en este tutorial y por la paciencia, me tarde un montón, lo siento por eso.

![Congratulations](https://media1.tenor.com/images/d2f47c433f56bed36755300a7313d0b6/tenor.gif)

Realmente espero que el contenido les sea de utilidad, y cualquier duda o sugerencia, por favor, déjenla en los comentarios y les responderé tan pronto como me resulte posible. Saludos y gracias por leer 😏.

![konosuba](https://media.giphy.com/media/9VSxGCayIge1a/giphy.gif)