¿Cómo gestionan las personas la autenticación en Go? [cerrado]

187

Para aquellos que crean API RESTful y aplicaciones front-end JS en Go, ¿cómo administran la autenticación? ¿Está utilizando alguna biblioteca o técnica en particular?

Me sorprende encontrar tan poca discusión sobre esto. Tengo en cuenta respuestas como las siguientes, y estoy tratando de evitar desarrollar mi propia implementación:

Formulario de autenticación en ASP.Net

¿Están todos codificando su propia solución, por separado?

SexxLuthor
fuente
55
La autenticación depende en gran medida del tipo de aplicación que busca. No existe una solución única para todos. Además, es un problema difícil de resolver. Esta es probablemente la razón por la que no encontrará ninguna documentación concluyente.
jimt
21
Hola, gracias por la rápida respuesta. Entendido, pero la mayoría de los lenguajes y marcos han presentado soluciones de autenticación que cubren los requisitos de autenticación más comunes compartidos por la mayoría de las aplicaciones, y tienen una amplia participación y apoyo de la comunidad. Estoy de acuerdo en que es un problema difícil. ¿No se benefician más del esfuerzo cooperativo? (Esto no es una queja, porque es de código abierto, sino más bien una observación de que todos estamos reinventando la rueda. :)
SexxLuthor
13
@jimt El hecho de que es un problema difícil hace que sea aún más importante proporcionarnos a los mortales una solución conónica que no nos podemos equivocar.
tymtam
Estoy votando para cerrar esta pregunta como fuera de tema porque es una pregunta de encuesta.
Flimzy

Respuestas:

115

Esta pregunta tiene un montón de puntos de vista, y tiene una insignia de Pregunta Popular, por lo que sé que hay mucho interés latente en este tema, y ​​muchas personas están preguntando exactamente lo mismo y no encuentran respuestas en Interwebs.

La mayor parte de la información disponible da como resultado el equivalente textual de la cosa ondulada de la mano, que se deja como un "ejercicio para el lector". ;)

Sin embargo, finalmente he encontrado un ejemplo concreto, (generosamente) proporcionado por un miembro de la lista de correo de golang-nuts:

https://groups.google.com/forum/#!msg/golang-nuts/GE7a_5C5kbA/fdSnH41pOPYJ

Esto proporciona un esquema sugerido y una implementación del lado del servidor como base para la autenticación personalizada. El código del lado del cliente todavía depende de usted.

(Espero que el autor de la publicación vea esto: ¡Gracias!)

Extraído (y reformateado):


"Sugeriría algo como el siguiente diseño:

create table User (
 ID int primary key identity(1,1),
 Username text,
 FullName text,
 PasswordHash text,
 PasswordSalt text,
 IsDisabled bool
)

create table UserSession (
 SessionKey text primary key,
 UserID int not null, -- Could have a hard "references User"
 LoginTime <time type> not null,
 LastSeenTime <time type> not null
)
  • Cuando un usuario inicia sesión en su sitio a través de un POST bajo TLS, determine si la contraseña es válida.
  • Luego, emita una clave de sesión aleatoria, digamos 50 o más caracteres crypto rand y cosas en una cookie segura.
  • Agregue esa clave de sesión a la tabla UserSession.
  • Luego, cuando vuelva a ver a ese usuario, primero presione la tabla UserSession para ver si SessionKey está allí con un LoginTime válido y LastSeenTime y el Usuario no se ha eliminado. Puede diseñarlo para que un temporizador borre automáticamente las filas antiguas en UserSession ".
SexxLuthor
fuente
8
Tendemos a preferir un sitio autónomo aquí en SO, entonces, ¿le importaría publicar la solución aquí también? En caso de que el enlace cambie a su debido tiempo (rotura del enlace y qué más ...) Los visitantes futuros podrían estar contentos con esto.
topskip
Esa es una pregunta justa, respetuosamente formulada. Gracias. He incluido la solución; ¿Crees que también se debe incluir el nombre del autor? (Es público, pero me pregunto sobre la etiqueta de cualquiera de las opciones).
SexxLuthor
Creo que es bueno como es. No pretendes ser el "propietario" de este fragmento, y no puedo ver que el autor original de este fragmento requiera que cada copia necesite una atribución. (Solo mis dos centavos).
topskip
35
No debe haber un campo "PasswordSalt" en su base de datos, porque debe usar bcrypt como algoritmo de hash, que crea automáticamente una sal y la incluye en el hash devuelto. Utilice también una función de comparación de tiempo constante.
0xdabbad00
44
+1 para bcrypt. Además, las sesiones de gorila con sus claves de 'cifrado' y 'autenticación' le permitirían almacenar de forma segura la información de la sesión sin usar una tabla de base de datos.
crantok
14

Usaría middleware para hacer la autenticación.

Puede probar go-http-auth para la autenticación básica y de resumen y gomniauth para OAuth2.

Pero cómo autenticarse realmente depende de su aplicación.

La autenticación introduce estado / contexto en sus http.Handlers y últimamente se ha debatido al respecto.

Las soluciones bien conocidas para el problema de contexto son gorilla / context y google context que se describen aquí .

Hice una solución más general sin la necesidad de un estado global en go-on / wrap que se pueda usar juntos o sin los otros dos y se integre muy bien con el middleware sin contexto.

wraphttpauth proporciona integración de go-http-auth con go-on / wrap.

metakeule
fuente
Hay muchas cosas nuevas con los principiantes. Me pregunto qué tipo de cosas debería comenzar un principiante. go-http-autho gomniautho ambos?
Casper
¿Alguien aquí implementó OAuth 1.0 en golang? ConsumerKey y autenticación basada en secreto?
user2888996
¿Cómo puedo implementar oAuth 1.0? ¿Usando Consumer Key y secret? Por favor ayuda. No obtengo ninguna biblioteca para lo mismo.
user2888996
9

Respondiendo esto en 2018. Sugiero usar JWT (JSON Web Token). La respuesta que marcó resuelta tiene un inconveniente, que es el viaje que realizó al frente (usuario) y atrás (servidor / base de datos). Lo que es peor si el usuario hizo una solicitud frecuente que necesita autenticación, dará como resultado una solicitud hinchada desde / hacia el servidor y la base de datos. Para resolver este problema, use JWT, que almacena el token en el extremo del usuario, que el usuario puede usar en cualquier momento que necesite acceso / solicitud. No es necesario ir al procesamiento de la base de datos y del servidor para verificar la validez del token.

mfathirirhas
fuente
6

Otro paquete de código abierto para manejar la autenticación con cookies es httpauth .

(escrito por mí, por cierto)

Cameron Little
fuente
2

Honestamente, hay muchos métodos y técnicas de autenticación que puede montar en su aplicación y que dependen de la lógica y los requisitos comerciales de las aplicaciones.
Por ejemplo, Oauth2, LDAP, autenticación local, etc.
Mi respuesta asume que está buscando autenticación local, lo que significa que administra las identidades del usuario en su aplicación. El servidor debe exponer un conjunto de API externas que permiten a los usuarios y administradores Administrar las cuentas y cómo desean identificarse en el Servidor para lograr una comunicación confiable. terminarás creando una tabla DB que contiene la información del usuario. donde la contraseña se codifica por motivos de seguridad Consulte Cómo almacenar la contraseña en la base de datos

supongamos los requisitos de la aplicación para autenticar a los usuarios según uno de los siguientes métodos:

  • Autenticación básica (nombre de usuario, contraseña):
    este método de autenticación depende de las credenciales de usuario establecidas en el encabezado de autorización codificado en base64 y definido en rfc7617 , básicamente cuando la aplicación recibe al usuario solicita que decodifique la autorización y vuelva a codificar la contraseña para compararla dentro de DB hash si coincide, el usuario autenticado devuelve el código de estado 401 al usuario.

  • autenticación basada en certificados:
    este método de autenticación depende de un certificado digital para identificar a un usuario, y se conoce como autenticación x509, por lo que cuando la aplicación recibe las solicitudes del usuario, lee el certificado del cliente y verifica que coincida con el certificado raíz de CA proporcionado a la aplicación.

  • token de portador:
    este método de autenticación depende de tokens de acceso de corta duración. El token de portador es una cadena críptica, generalmente generada por el servidor en respuesta a una solicitud de inicio de sesión. entonces, cuando la aplicación recibe las solicitudes del usuario, lee la autorización y valida el token para autenticar al usuario.

Sin embargo, recomendaría go-guardian para la biblioteca de autenticación, que lo hace a través de un conjunto extensible de métodos de autenticación conocidos como estrategias. Básicamente, Go-Guardian no monta rutas ni asume ningún esquema de base de datos en particular, lo que maximiza la flexibilidad y permite que el desarrollador tome decisiones.

Configurar un autenticador de guardián es sencillo.

Aquí el ejemplo completo de los métodos anteriores.

package main

import (
    "context"
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "sync"

    "github.com/golang/groupcache/lru"
    "github.com/gorilla/mux"
    "github.com/shaj13/go-guardian/auth"
    "github.com/shaj13/go-guardian/auth/strategies/basic"
    "github.com/shaj13/go-guardian/auth/strategies/bearer"
    gx509 "github.com/shaj13/go-guardian/auth/strategies/x509"
    "github.com/shaj13/go-guardian/store"
)

var authenticator auth.Authenticator
var cache store.Cache

func middleware(next http.Handler) http.HandlerFunc {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Println("Executing Auth Middleware")
        user, err := authenticator.Authenticate(r)
        if err != nil {
            code := http.StatusUnauthorized
            http.Error(w, http.StatusText(code), code)
            return
        }
        log.Printf("User %s Authenticated\n", user.UserName())
        next.ServeHTTP(w, r)
    })
}

func Resource(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Resource!!\n"))
}

func Login(w http.ResponseWriter, r *http.Request) {
    token := "90d64460d14870c08c81352a05dedd3465940a7"
    user := auth.NewDefaultUser("admin", "1", nil, nil)
    cache.Store(token, user, r)
    body := fmt.Sprintf("token: %s \n", token)
    w.Write([]byte(body))
}

func main() {
    opts := x509.VerifyOptions{}
    opts.KeyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
    opts.Roots = x509.NewCertPool()
    // Read Root Ca Certificate
    opts.Roots.AddCert(readCertificate("<root-ca>"))

    cache = &store.LRU{
        lru.New(100),
        &sync.Mutex{},
    }

    // create strategies
    x509Strategy := gx509.New(opts)
    basicStrategy := basic.New(validateUser, cache)
    tokenStrategy := bearer.New(bearer.NoOpAuthenticate, cache)

    authenticator = auth.New()
    authenticator.EnableStrategy(gx509.StrategyKey, x509Strategy)
    authenticator.EnableStrategy(basic.StrategyKey, basicStrategy)
    authenticator.EnableStrategy(bearer.CachedStrategyKey, tokenStrategy)

    r := mux.NewRouter()
    r.HandleFunc("/resource", middleware(http.HandlerFunc(Resource)))
    r.HandleFunc("/login", middleware(http.HandlerFunc(Login)))

    log.Fatal(http.ListenAndServeTLS(":8080", "<server-cert>", "<server-key>", r))
}

func validateUser(ctx context.Context, r *http.Request, userName, password string) (auth.Info, error) {
    // here connect to db or any other service to fetch user and validate it.
    if userName == "stackoverflow" && password == "stackoverflow" {
        return auth.NewDefaultUser("stackoverflow", "10", nil, nil), nil
    }

    return nil, fmt.Errorf("Invalid credentials")
}

func readCertificate(file string) *x509.Certificate {
    data, err := ioutil.ReadFile(file)

    if err != nil {
        log.Fatalf("error reading %s: %v", file, err)
    }

    p, _ := pem.Decode(data)
    cert, err := x509.ParseCertificate(p.Bytes)
    if err != nil {
        log.Fatalf("error parseing certificate %s: %v", file, err)
    }

    return cert
}

Uso:

  • Obtener ficha:
curl  -k https://127.0.0.1:8080/login -u stackoverflow:stackoverflow
token: 90d64460d14870c08c81352a05dedd3465940a7

  • Autenticar con un token:
curl  -k https://127.0.0.1:8080/resource -H "Authorization: Bearer 90d64460d14870c08c81352a05dedd3465940a7"

Resource!!
  • Autenticar con una credencial de usuario:
curl  -k https://127.0.0.1:8080/resource -u stackoverflow:stackoverflow

Resource!!
  • Autenticar con un certificado de usuario:
curl --cert client.pem --key client-key.pem --cacert ca.pem https://127.0.0.1:8080/resource

Resource!!

Puede habilitar varios métodos de autenticación a la vez. Por lo general, debe usar al menos dos métodos

shaj13
fuente
1

Eche un vistazo a Labstack Echo : envuelve la autenticación para API RESTful y aplicaciones frontend en middleware que puede usar para proteger rutas API específicas.

Configurar la autenticación básica, por ejemplo, es tan sencillo como crear un nuevo subruter para la /adminruta:

e.Group("/admin").Use(middleware.BasicAuth(func(username, password string, c echo.Context) (bool, error) {
    if username == "joe" && password == "secret" {
        return true, nil
    }
    return false, nil
}))

Vea todas las opciones de autenticación de middleware de Labstack aquí.

Adil B
fuente