Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JWT Validation #154

Open
monwolf opened this issue Apr 4, 2023 · 1 comment
Open

JWT Validation #154

monwolf opened this issue Apr 4, 2023 · 1 comment
Labels
enhancement New feature or request

Comments

@monwolf
Copy link
Contributor

monwolf commented Apr 4, 2023

Is your feature request related to a problem? Please describe.

It would be fine being capable to check if a request is correctly singed before serving it from cache.

Describe the solution you'd like

In the same way, you implemented the middleware for basic auth, it could be implemented a jwt checker.

Describe alternatives you've considered

I'm using this in our go microservices, maybe can help you:

import (
	"context"
	"encoding/json"
	"net/http"
	"time"

	"github.com/lestrrat-go/backoff/v2"
	"github.com/lestrrat-go/jwx/jwk"
	"github.com/lestrrat-go/jwx/jwt"
)

type JwtConfig struct {
	Context        context.Context
	Jwks_url       string
	Logger         *logger.Logger
	Allowed_scopes []string
	Excluded_paths []string
}

var co *JwtConfig
var jwtKeyFetcher *jwk.AutoRefresh

type JwtError struct {
	ErrorCode        string `json:"errorCode"`
	ErrorDescription string `json:"errorDescription"`
}

type ScpClaim struct {
	Scp []string
}

func InitJWT(conifg *JwtConfig) {

	if co == nil {
		co = conifg
		jwtKeyFetcher = jwk.NewAutoRefresh(co.Context)
		jwtKeyFetcher.Configure(co.Jwks_url,
			jwk.WithMinRefreshInterval(15*time.Minute),
			jwk.WithFetchBackoff(backoff.Constant(backoff.WithInterval(time.Minute))),
		)
	}

}

func errorJson(resp http.ResponseWriter, statuscode int, error *JwtError) {
	resp.WriteHeader(statuscode)
	resp.Header().Add("Content-Type", "application/json; charset=utf-8")
	json_error, _ := json.Marshal(error)
	resp.Write(json_error)
}

func validate_jwt(w http.ResponseWriter, r *http.Request) error {

	keyset, err := jwtKeyFetcher.Fetch(co.Context, co.Jwks_url)

	token, err := jwt.ParseRequest(r,
		jwt.WithKeySet(keyset),
		jwt.WithValidate(true),
		jwt.WithTypedClaim("scp", json.RawMessage{}),
	)
	if err != nil {
		co.Logger.Info("Error jwt:", err)
		errorJson(w, http.StatusUnauthorized, &JwtError{ErrorCode: "JsonWebTokenError", ErrorDescription: err.Error()})
		return http.ErrAbortHandler
	}

	if err := jwt.Validate(token); err != nil {
		co.Logger.Info("Error jwt:", err)
		errorJson(w, http.StatusUnauthorized, &JwtError{ErrorCode: "JsonWebTokenError", ErrorDescription: err.Error()})
		return http.ErrAbortHandler
	}

	scopes := getScopes(token)
	haveAllowedScope := haveAllowedScope(scopes, co.Allowed_scopes)
	if !haveAllowedScope {
		errorJson(w, http.StatusUnauthorized, &JwtError{ErrorCode: "InvalidScope", ErrorDescription: "Invalid Scope"})
		return http.ErrAbortHandler
	}
	return nil

}

func JwtHandler(next http.Handler) http.Handler {

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if isExcluded(co.Excluded_paths, r.URL.Path) {
			next.ServeHTTP(w, r)
			return
		}

		err := validate_jwt(w, r)
		if err != nil {
			return
		}

		next.ServeHTTP(w, r)

	})
}

func haveAllowedScope(scopes []string, allowed_scopes []string) bool {
	if allowed_scopes != nil {
		for _, s := range allowed_scopes {
			isAllowed := contains(scopes, s)
			if isAllowed {
				return true
			}
		}
	}
	return false
}

func getScopes(token jwt.Token) []string {
	scpInterface := token.PrivateClaims()["scp"]
	scpRaw, _ := scpInterface.(json.RawMessage)
	scopes := []string{}
	json.Unmarshal(scpRaw, &scopes)
	return scopes
}
@fabiocicerchia fabiocicerchia added the enhancement New feature or request label Apr 13, 2023
@monwolf
Copy link
Contributor Author

monwolf commented Jan 8, 2024

This is implemented in:

#171

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants