changeset 119:29e56c342c9f

Added first middleware for JWT token extraction. TODO: Add second one to check against logged in users.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Tue, 26 Jun 2018 18:29:26 +0200
parents dad6cf39691e
children be2631b0ce7e
files auth/middleware.go auth/token.go config/config.go
diffstat 3 files changed, 98 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/auth/middleware.go	Tue Jun 26 18:29:26 2018 +0200
@@ -0,0 +1,54 @@
+package auth
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"regexp"
+)
+
+var extractToken = regexp.MustCompile(`\s*Bearer\s+(\S+)`)
+
+type contextType int
+
+const (
+	claimsKey contextType = iota
+	tokenKey
+)
+
+func GetClaims(req *http.Request) (*Claims, bool) {
+	claims, ok := req.Context().Value(claimsKey).(*Claims)
+	return claims, ok
+}
+
+func GetToken(req *http.Request) (string, bool) {
+	token, ok := req.Context().Value(tokenKey).(string)
+	return token, ok
+}
+
+func JWTMiddleware(next http.Handler) http.Handler {
+
+	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+
+		auth := req.Header.Get("Authorization")
+
+		token := extractToken.FindStringSubmatch(auth)
+		if len(token) != 2 {
+			http.Error(rw, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
+			return
+		}
+
+		claims, err := TokenToClaims(token[1])
+		if err != nil {
+			http.Error(rw, fmt.Sprintf("error: %v", err), http.StatusUnauthorized)
+			return
+		}
+
+		ctx := req.Context()
+		ctx = context.WithValue(ctx, claimsKey, claims)
+		ctx = context.WithValue(ctx, tokenKey, token[1])
+		req = req.WithContext(ctx)
+
+		next.ServeHTTP(rw, req)
+	})
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/auth/token.go	Tue Jun 26 18:29:26 2018 +0200
@@ -0,0 +1,40 @@
+package auth
+
+import (
+	"time"
+
+	"gemma.intevation.de/gemma/config"
+
+	jwt "github.com/dgrijalva/jwt-go"
+)
+
+type Claims struct {
+	jwt.StandardClaims
+
+	User  string   `json:"user"`
+	Roles []string `json:"roles"`
+}
+
+const maxTokenValid = time.Hour * 3
+
+func NewToken(user string, roles []string) (string, error) {
+
+	// Create the Claims
+	claims := &Claims{
+		StandardClaims: jwt.StandardClaims{
+			ExpiresAt: jwt.TimeFunc().Add(maxTokenValid).Unix(),
+		},
+		User:  user,
+		Roles: roles,
+	}
+
+	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
+	return token.SignedString(config.Config.JWTSignKey)
+}
+
+func TokenToClaims(token string) (*Claims, error) {
+	claims := &Claims{}
+	_, err := jwt.ParseWithClaims(token, claims,
+		func(*jwt.Token) (interface{}, error) { return config.Config.JWTSignKey, nil })
+	return claims, err
+}
--- a/config/config.go	Tue Jun 26 16:40:44 2018 +0200
+++ b/config/config.go	Tue Jun 26 18:29:26 2018 +0200
@@ -7,6 +7,8 @@
 	DBPort    uint
 	DBName    string
 	DBSSLMode string
+
+	JWTSignKey []byte
 }
 
 func NewConfiguration() *Configuration {
@@ -16,5 +18,7 @@
 		DBPort:    5432,
 		DBName:    "gemma",
 		DBSSLMode: "require",
+
+		JWTSignKey: []byte("very, very secret!"),
 	}
 }