Go: RESTful - API-KEY

De Aulas



Afluentes: Sistemas Distribuídos e Mobile

Exemplo usando API KEY

Agora vamos ver um exemplo usando autenticação via api key.

package main

import (
	"bufio"
	"fmt"
	"net/http"
	"os"

	"github.com/rs/cors"
)

func main() {
	router := http.NewServeMux()

	router.HandleFunc("GET /auth", func(w http.ResponseWriter, r *http.Request) {
		key := r.Header.Get("X-API-KEY") // Pegamos a informação X-API-KEY do cliente
		if key != getAPIKey() {          // e testamos com a informação do arquivo
			http.Error(w, "Forbidden", http.StatusForbidden)
			return
		}
		fmt.Fprintf(w, "Autentication Ok!")
	})

	// Liberamos o CORS
	c := cors.AllowAll()
	r := c.Handler(router)

	err := http.ListenAndServe("localhost:8080", r)
	if err != nil {
		fmt.Println(err.Error())
	}
}

// Pegamos a informação do arquivo apikey.txt que é nossa chave
// propriamente dita. Se não existir o arquivo ou der algum erro,
// retorna uma string vazia.
func getAPIKey() string {
	file, err := os.Open("apikey.txt")
	if err != nil {
		return ""
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	if scanner.Scan() {
		return scanner.Text()
	}
	return ""
}

Observe que no código acima, estamos usando uma função chamada getAPIKey que busca uma informação única de um arquivo chamado apikey.txt. Caso dê algum erro, retorna uma string vazia. Então usamos essa informação para comparar com uma informação recebida da requisição, no cabeçalho, chamada X-API-KEY. Se forem iguais, autentica, senão fecha a aplicação e retorna um código de erro. Podemos usar o curl para testar:

curl -H "X-API-KEY:01234" http://localhost:8080/auth

Veja que adicionamos um cabeçalho agora. Para validar, temos que ter o arquivo apikey.txt com a informação 01234 dentro.

Uma maneira mais simples é colocar uma constante global e pegar a informação dessa constante. Contudo, a maneira apresentada acima permite que você possa alterar a apikey sem a necessidade de reiniciar a api.

API KEY por Usuário

Outra forma interessante é gerenciar as API KEYs por usuário, permitindo que cada usuário tenha sua própria apikey. Pode-se armazenar em um banco de dados ou arquivo json. Em banco de dados é mais recomendável, pois permite que cada usuário gerencie sua própria apikey, alterando ela quando necessário e aumentando o grau de segurança.

Então agora, nossa apikey seria um par de informações, sendo o usuário e a chave:

// Podemos ler as informações de um arquivo JSON e jogar em um MAP
apiKeys := getAPIKeys("apikeys.json")

// Pegamos as informações vindas da requisição
user := r.Header.Get("X-User")
key := r.Header.Get("X-API-KEY")

// Depois validamos
if apiKeys[user] != key {
	http.Error(w, "Forbidden", http.StatusForbidden)
	return
}

Nesse caso, o arquivo apikeys.json ficaria mais ou menos assim:

{
    "user1": "apikey1",
    "user2": "apikey2",
    "user3": "apikey3"
}

E pra testar com curl:

curl -H "X-User: user1" -H "X-API-KEY: apikey1" http://localhost:8080/auth

Outra questão é que o CORS pode barrar a gente, então temos que liberar antes de chamar o ListenAndServe

c := cors.AllowAll()
r := c.Handler(router)

Outra forma é especificar o que se quer liberar:

c := cors.New(cors.Options{
    AllowedOrigins:   []string{"*"},
    AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
    AllowedHeaders:   []string{"Content-Type", "X-User", "X-API-KEY"},
    AllowCredentials: true,
})
handler := c.Handler(router)

Atividades

  1. Implemente api de acesso com autenticação de API-KEY por usuário com arquivo JSON citado acima.
  2. Implemente agora uma api de acesso de com autenticação de API-KEY por usuário usando uma tabela em um banco de dados com pelo menos o par de X-User e X-API-KEY. Veja que para testar, basta fazer uma pesquisa no banco de dados com as informações do usuário e chave e caso retorne um registro, o usuário está autorizado.