diff --git a/api/auth0jwt/auth0jwt.go b/api/auth0jwt/auth0jwt.go deleted file mode 100644 index 941abf5..0000000 --- a/api/auth0jwt/auth0jwt.go +++ /dev/null @@ -1,139 +0,0 @@ -// Package auth0jwt is a modification of the Auth0 provided Go tutorial: -// https://auth0.com/docs/quickstart/backend/golang -// As you may guess that has several issues, but a lot of what's here has been taken verbatim. -package auth0jwt - -import ( - "crypto/subtle" - "encoding/json" - "errors" - "github.com/auth0/go-jwt-middleware" - "github.com/dgrijalva/jwt-go" - "github.com/labstack/echo/v4" - "net/http" -) - -type Jwks struct { - Keys []JSONWebKeys `json:"keys"` -} - -type JSONWebKeys struct { - Kty string `json:"kty"` - Kid string `json:"kid"` - Use string `json:"use"` - N string `json:"n"` - E string `json:"e"` - X5c []string `json:"x5c"` -} - -type Config struct { - // Aud is used to veirfy the 'aud' claim. It's the identifier of the API in Auth0. - Aud string - // Iss is used to verify the 'iss' claim. - Iss string - // JwksPath is the path to the file like "https://my-application.auth0.com/.well-known/jwks.json". - // See https://auth0.com/docs/tokens/concepts/jwks - JwksPath string -} - -func NewMiddleware(cfg Config) echo.MiddlewareFunc { - mw := jwtmiddleware.New(jwtmiddleware.Options{ - ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) { - checkAud := verifyArrayAudience(token.Claims.(jwt.MapClaims), cfg.Aud, true) - if !checkAud { - return token, echo.NewHTTPError(401, "invalid audience") - } - checkIss := token.Claims.(jwt.MapClaims).VerifyIssuer(cfg.Iss, true) - if !checkIss { - return token, echo.NewHTTPError(401, "invalid issuer") - } - - cert, err := getPemCert(cfg.JwksPath, token) - if err != nil { - return nil, err - } - - result, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(cert)) - return result, nil - }, - UserProperty: "user", - CredentialsOptional: false, - Debug: false, - }) - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - req := c.Request() - // req gets stomped by CheckJWT, to have a new context - err := mw.CheckJWT(c.Response().Writer, req) - if err != nil { - return err - } - user := req.Context().Value(mw.Options.UserProperty) - if user == nil { - panic("why is 'user' nil in jwt context!?") - } - c.Set(mw.Options.UserProperty, user) - return next(c) - } - } -} - -func getPemCert(jwksPath string, token *jwt.Token) (string, error) { - cert := "" - resp, err := http.Get(jwksPath) - - if err != nil { - return cert, err - } - defer resp.Body.Close() - - var jwks = Jwks{} - err = json.NewDecoder(resp.Body).Decode(&jwks) - - if err != nil { - return cert, err - } - - for k := range jwks.Keys { - if token.Header["kid"] == jwks.Keys[k].Kid { - cert = "-----BEGIN CERTIFICATE-----\n" + jwks.Keys[k].X5c[0] + "\n-----END CERTIFICATE-----" - } - } - - if cert == "" { - err := errors.New("unable to find appropriate key") - return cert, err - } - - return cert, nil -} - -// Seehttps://github.com/dgrijalva/jwt-go/pull/308 -// These two methods are straight copy paste -func verifyArrayAudience(m jwt.MapClaims, cmp string, req bool) bool { - switch m["aud"].(type) { - case string: - aud := m["aud"].(string) - return verifyAudHelper(aud, cmp, req) - default: - auds := m["aud"].([]interface{}) - for _, aud := range auds { - if verifyAudHelper(aud.(string), cmp, req) { - return true - } - } - return false - } -} - -func verifyAudHelper(aud string, cmp string, required bool) bool { - if aud == "" { - return !required - } - if subtle.ConstantTimeCompare([]byte(aud), []byte(cmp)) != 0 { - return true - } else { - return false - } -} diff --git a/api/auth0jwt/auth0jwt_test.go b/api/auth0jwt/auth0jwt_test.go deleted file mode 100644 index d5eca98..0000000 --- a/api/auth0jwt/auth0jwt_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package auth0jwt_test - -import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "testing" -) - -func TestAuth0Jwt(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "API Suite") -} - -var _ = Describe("auth0jwt", func() { - //var e *echo.Echo - - BeforeEach(func() { - //e = echo.New() - }) - - It("validates against iss", func() { - }) - It("validates against aud string", func() { - }) - It("validates against aud array", func() { - }) - It("adds the user to the echo context", func() { - }) - It("validates against the PEM cert", func() {}) -}) diff --git a/go.mod b/go.mod index e0d7eae..1bb5eb7 100644 --- a/go.mod +++ b/go.mod @@ -3,24 +3,25 @@ module github.com/lithictech/go-aperitif go 1.22 require ( - github.com/auth0/go-jwt-middleware v0.0.0-20200507191422-d30d7b9ece63 - github.com/dgrijalva/jwt-go v3.2.0+incompatible - github.com/google/uuid v1.1.1 - github.com/hashicorp/go-multierror v1.1.0 - github.com/jmoiron/sqlx v1.2.0 + github.com/google/uuid v1.6.0 + github.com/hashicorp/go-multierror v1.1.1 + github.com/jmoiron/sqlx v1.4.0 github.com/labstack/echo/v4 v4.12.0 - github.com/onsi/ginkgo/v2 v2.3.1 - github.com/onsi/gomega v1.22.0 + github.com/onsi/ginkgo/v2 v2.19.1 + github.com/onsi/gomega v1.34.1 github.com/pkg/errors v0.9.1 - github.com/rgalanakis/golangal v1.1.0 + github.com/rgalanakis/golangal v1.2.0 github.com/rgalanakis/validator v0.0.0-20180731224108-4a34a8927f7c - github.com/sirupsen/logrus v1.6.0 - golang.org/x/crypto v0.22.0 + github.com/sirupsen/logrus v1.9.3 + golang.org/x/crypto v0.25.0 ) require ( + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/google/go-cmp v0.5.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect github.com/labstack/gommon v0.4.2 // indirect @@ -28,11 +29,13 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/term v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/term v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.23.0 // indirect google.golang.org/appengine v1.6.6 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index b998998..55cffea 100644 --- a/go.sum +++ b/go.sum @@ -1,35 +1,41 @@ -github.com/auth0/go-jwt-middleware v0.0.0-20200507191422-d30d7b9ece63 h1:LY/kRH+fCqA090FsM2VfZ+oocD99ogm3HrT1r0WDnCk= -github.com/auth0/go-jwt-middleware v0.0.0-20200507191422-d30d7b9ece63/go.mod h1:mF0ip7kTEFtnhBJbd/gJe62US3jykNN+dcZoZakJCCA= -github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0= -github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= +github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= @@ -38,6 +44,7 @@ github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0 github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -45,62 +52,80 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/onsi/ginkgo/v2 v2.3.1 h1:8SbseP7qM32WcvE6VaN6vfXxv698izmsJ1UQX9ve7T8= github.com/onsi/ginkgo/v2 v2.3.1/go.mod h1:Sv4yQXwG5VmF7tm3Q5Z+RWUpPo24LF1mpnz2crUb8Ys= +github.com/onsi/ginkgo/v2 v2.19.1 h1:QXgq3Z8Crl5EL1WBAC98A5sEBHARrAJNzAmMxzLcRF0= +github.com/onsi/ginkgo/v2 v2.19.1/go.mod h1:O3DtEWQkPa/F7fBMgmZQKKsluAy8pd3rEQdrjkPb9zA= github.com/onsi/gomega v1.22.0 h1:AIg2/OntwkBiCg5Tt1ayyiF1ArFrWFoCSMtMi/wdApk= github.com/onsi/gomega v1.22.0/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= +github.com/onsi/gomega v1.34.0 h1:eSSPsPNp6ZpsG8X1OVmOTxig+CblTc4AxpPBykhe2Os= +github.com/onsi/gomega v1.34.0/go.mod h1:MIKI8c+f+QLWk+hxbePD4i0LMJSExPaZOVfkoex4cAo= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rgalanakis/golangal v1.1.0 h1:JMR5gBHMmcWQ86hYM8mRXp99AC1AeEdWKc4sNfjjA3A= github.com/rgalanakis/golangal v1.1.0/go.mod h1:DT35MZom81QtvdrIerWZ0T3moT/npNBiJmW9cXcufMM= +github.com/rgalanakis/golangal v1.2.0 h1:dDrD6sJR2JbnNl0aDNvBHjzWN6r9lDTfv3XqKTNTMmg= +github.com/rgalanakis/golangal v1.2.0/go.mod h1:DT35MZom81QtvdrIerWZ0T3moT/npNBiJmW9cXcufMM= github.com/rgalanakis/validator v0.0.0-20180731224108-4a34a8927f7c h1:z1J+SUwpje5zEyPfHBIv6N++OxjETBV5wGBwkmO/Wzk= github.com/rgalanakis/validator v0.0.0-20180731224108-4a34a8927f7c/go.mod h1:qIw2dAd/4wMHHnINySB5+y9HPKQXUKmga0oZHSluPKE= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0= -github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= -github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/jwtee/jwtee.go b/jwtee/jwtee.go deleted file mode 100644 index 025454d..0000000 --- a/jwtee/jwtee.go +++ /dev/null @@ -1,149 +0,0 @@ -// Package jwtee wraps github.com/dgrijalva/jwt-go -// with some tooling that makes it easier to use -// in most practical usage. -package jwtee - -import ( - "crypto/subtle" - "errors" - "github.com/dgrijalva/jwt-go" - "time" -) - -type Error struct { - msg string -} - -func (e Error) Error() string { - return e.msg -} - -type Jwtee struct { - Secret []byte - Aud string - Iss string - Alg jwt.SigningMethod -} - -type Input struct { - Secret string - Aud string - Iss string - Alg string -} - -func New(input Input) (Jwtee, error) { - j := Jwtee{ - Secret: []byte(input.Secret), - Aud: input.Aud, - Iss: input.Iss, - Alg: jwt.GetSigningMethod(input.Alg), - } - if len(j.Secret) == 0 { - return j, errors.New("secret is required") - } - if j.Aud == "" { - return j, errors.New("aud is required") - } - if j.Iss == "" { - return j, errors.New("iss is required") - } - if j.Alg == nil { - return j, errors.New("valid alg is required") - } - return j, nil -} - -func (j Jwtee) Parse(tokenString string) (*jwt.Token, error) { - return jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { - if token.Method != j.Alg { - return token, Error{msg: "invalid alg"} - } - checkAud := verifyArrayAudience(token.Claims.(jwt.MapClaims), j.Aud, true) - if !checkAud { - return token, Error{msg: "invalid aud"} - } - checkIss := token.Claims.(jwt.MapClaims).VerifyIssuer(j.Iss, true) - if !checkIss { - return token, Error{msg: "invalid iss"} - } - return j.Secret, nil - }) -} - -func (j Jwtee) ParseMapClaims(tokenString string) (jwt.MapClaims, error) { - tok, err := j.Parse(tokenString) - if tok == nil { - panic("token should never be nil") - } - return tok.Claims.(jwt.MapClaims), err -} - -func (j Jwtee) BuildTtl(ttl time.Duration, moreClaims map[string]interface{}) (string, error) { - tok := jwt.New(j.Alg) - mc := tok.Claims.(jwt.MapClaims) - mc["iss"] = j.Iss - mc["aud"] = j.Aud - mc["exp"] = jwt.TimeFunc().Add(ttl).Unix() - for k, v := range moreClaims { - mc[k] = v - } - return tok.SignedString(j.Secret) -} - -func (j Jwtee) Dup(input Input) Jwtee { - if len(input.Secret) > 0 { - j.Secret = []byte(input.Secret) - } - if input.Aud != "" { - j.Aud = input.Aud - } - if input.Iss != "" { - j.Iss = input.Iss - } - if input.Alg != "" { - j.Alg = jwt.GetSigningMethod(input.Alg) - } - return j -} - -// See https://github.com/dgrijalva/jwt-go/pull/308 -// These two methods are straight copy paste -func verifyArrayAudience(m jwt.MapClaims, cmp string, req bool) bool { - switch m["aud"].(type) { - case string: - aud := m["aud"].(string) - return verifyAudHelper(aud, cmp, req) - default: - auds := m["aud"].([]interface{}) - for _, aud := range auds { - if verifyAudHelper(aud.(string), cmp, req) { - return true - } - } - return false - } -} - -func verifyAudHelper(aud string, cmp string, required bool) bool { - if aud == "" { - return !required - } - if subtle.ConstantTimeCompare([]byte(aud), []byte(cmp)) != 0 { - return true - } else { - return false - } -} - -func StringClaim(claims jwt.MapClaims, key string) (string, bool) { - v, ok := claims[key] - if !ok { - return "", false - } - s, ok := v.(string) - if !ok { - return "", false - } - return s, len(s) > 0 -} diff --git a/jwtee/jwtee_test.go b/jwtee/jwtee_test.go deleted file mode 100644 index 3455b93..0000000 --- a/jwtee/jwtee_test.go +++ /dev/null @@ -1,197 +0,0 @@ -package jwtee_test - -import ( - "github.com/dgrijalva/jwt-go" - "github.com/lithictech/go-aperitif/jwtee" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - . "github.com/rgalanakis/golangal" - "testing" - "time" -) - -func TestJwtee(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "jwtee package Suite") -} - -var _ = Describe("jwtee", func() { - secret := "xyz" - aud := "hi" - iss := "there" - alg := "HS256" - - validInput := func() jwtee.Input { - return jwtee.Input{ - Secret: secret, - Aud: aud, - Iss: iss, - Alg: alg, - } - } - - newJwtee := func() jwtee.Jwtee { - j, err := jwtee.New(validInput()) - Expect(err).ToNot(HaveOccurred()) - return j - } - - It("requires secret, aud, iss, and alg", func() { - var err error - var jw jwtee.Jwtee - jw, err = jwtee.New(validInput()) - Expect(err).ToNot(HaveOccurred()) - Expect(jw).To(And( - MatchField("Aud", aud), - MatchField("Iss", iss), - MatchField("Alg", jwt.SigningMethodHS256), - )) - _, err = jwtee.New(jwtee.Input{ - Secret: "", - Aud: aud, - Iss: iss, - Alg: alg, - }) - Expect(err).To(MatchError(ContainSubstring("secret is required"))) - _, err = jwtee.New(jwtee.Input{ - Secret: secret, - Aud: "", - Iss: iss, - Alg: alg, - }) - Expect(err).To(MatchError(ContainSubstring("aud is required"))) - _, err = jwtee.New(jwtee.Input{ - Secret: secret, - Aud: aud, - Iss: "", - Alg: alg, - }) - Expect(err).To(MatchError(ContainSubstring("iss is required"))) - _, err = jwtee.New(jwtee.Input{ - Secret: secret, - Aud: aud, - Iss: iss, - Alg: "", - }) - Expect(err).To(MatchError(ContainSubstring("alg is required"))) - }) - It("can dup itself with non-empty input values", func() { - jw := newJwtee() - jw2 := jw.Dup(jwtee.Input{Aud: "another"}) - Expect(jw.Aud).To(Equal(aud)) - Expect(jw.Iss).To(Equal(iss)) - Expect(jw2.Aud).To(Equal("another")) - Expect(jw2.Iss).To(Equal(iss)) - }) - Describe("parsing", func() { - It("can verify with a string aud claim", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJoaSIsImlzcyI6InRoZXJlIiwiaWF0IjoxNTE2MjM5MDIyfQ.kTgZa43Zq9LrjDAEerD8feT2_TrIhzCPO1UC4bBXzgQ` - cl, err := jw.ParseMapClaims(s) - Expect(err).ToNot(HaveOccurred()) - Expect(cl["aud"]).To(Equal("hi")) - }) - - It("can fail with an invalid aud claim", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ5byIsImlzcyI6InRoZXJlIiwiaWF0IjoxNTE2MjM5MDIyfQ.BG7D0kCIcdgTfhOFNxArgubEL_2_WQmxE4vpnOv_AlU` - cl, err := jw.ParseMapClaims(s) - Expect(err).To(MatchError("invalid aud")) - Expect(cl["aud"]).To(Equal("yo")) - }) - It("can verify with an array aud claim", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiaGkiLCJoZWxsbyJdLCJpc3MiOiJ0aGVyZSIsImlhdCI6MTUxNjIzOTAyMn0.37-1H6f20flFs2vjJ6u2nzh7BQ51kyQyELEX0y_xE3c` - cl, err := jw.ParseMapClaims(s) - Expect(err).ToNot(HaveOccurred()) - Expect(cl["aud"]).To(BeEquivalentTo([]interface{}{"hi", "hello"})) - }) - It("can fail if aud not in array aud claim", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsieW8iXSwiaXNzIjoidGhlcmUiLCJpYXQiOjE1MTYyMzkwMjJ9.u-WkwjTF4kxdGB2wtinAtC1usOnIqeTPnDKg2HQ2gJw` - cl, err := jw.ParseMapClaims(s) - Expect(err).To(MatchError("invalid aud")) - Expect(cl["aud"]).To(BeEquivalentTo([]interface{}{"yo"})) - }) - It("can verify against an issuer", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJoaSIsImlzcyI6InRoZXJlIiwiaWF0IjoxNTE2MjM5MDIyfQ.kTgZa43Zq9LrjDAEerD8feT2_TrIhzCPO1UC4bBXzgQ` - cl, err := jw.ParseMapClaims(s) - Expect(err).ToNot(HaveOccurred()) - Expect(cl["iss"]).To(Equal("there")) - }) - It("can fail with an invalid issuer", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJoaSIsImlzcyI6InlvbmRlciIsImlhdCI6MTUxNjIzOTAyMn0.Wo0zf5P9H4HAnOWTgdUKNN0W-jTTJot0lEl5kE1r3YY` - cl, err := jw.ParseMapClaims(s) - Expect(err).To(MatchError("invalid iss")) - Expect(cl["iss"]).To(Equal("yonder")) - }) - It("validates against the signing method", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJoaSIsImlzcyI6InRoZXJlIiwiaWF0IjoxNTE2MjM5MDIyfQ.kTgZa43Zq9LrjDAEerD8feT2_TrIhzCPO1UC4bBXzgQ` - tok, err := jw.Parse(s) - Expect(err).ToNot(HaveOccurred()) - Expect(tok.Header["alg"]).To(Equal("HS256")) - }) - It("can fail with an unexpected signing method", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJoaSIsImlzcyI6InlvbmRlciIsImlhdCI6MTUxNjIzOTAyMn0.7q_DMegJbTO9uxWPy7n2mfDrBgAO3xBSpVmGjqHG6-ubve8QH2Y1d2noYWMk-wjSwkfbVB1K98FCfVDvZxhfGA` - tok, err := jw.Parse(s) - Expect(err).To(MatchError("invalid alg")) - Expect(tok.Header["alg"]).To(Equal("HS512")) - }) - It("can verify an unexpired exp", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJoaSIsImlzcyI6InRoZXJlIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1OTc5ODkyMzM1MTR9.bpLset-GCKbOlish900xGamrRCqQzmX06A2e2BtAdJE` - cl, err := jw.ParseMapClaims(s) - Expect(err).ToNot(HaveOccurred()) - Expect(cl["exp"]).To(BeEquivalentTo(1597989233514)) - }) - It("fails expired exp", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJoaSIsImlzcyI6InRoZXJlIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjV9.XsqW1BORkEZBwYXzDVgPJmSV-6wDzkFaZ7NacIfDjNY` - cl, err := jw.ParseMapClaims(s) - Expect(err).To(MatchError("Token is expired")) - Expect(cl["exp"]).To(BeEquivalentTo(5)) - }) - }) - Describe("building", func() { - origTime := jwt.TimeFunc - AfterEach(func() { - jwt.TimeFunc = origTime - }) - It("builds a token with the default fields and additional fields", func() { - jwt.TimeFunc = func() time.Time { - return time.Unix(10000, 0) - } - jw := newJwtee() - js, err := jw.BuildTtl(654*time.Second, map[string]interface{}{"sub": 1234}) - Expect(err).ToNot(HaveOccurred()) - expected := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJoaSIsImV4cCI6MTA2NTQsImlzcyI6InRoZXJlIiwic3ViIjoxMjM0fQ.OgPwnSrNaEpCgSMcILAdATor2NGlupnt7ggbqr32NL0` - Expect(js).To(Equal(expected)) - }) - }) - Describe("StringClaim", func() { - It("extracts a non-empty string claim", func() { - c := jwt.MapClaims{"s": "", "s2": "a", "i": 1} - var s string - var ok bool - s, ok = jwtee.StringClaim(c, "s2") - Expect(ok).To(BeTrue()) - Expect(s).To(Equal("a")) - - s, ok = jwtee.StringClaim(c, "s") - Expect(ok).To(BeFalse()) - Expect(s).To(BeEmpty()) - - s, ok = jwtee.StringClaim(c, "x") - Expect(ok).To(BeFalse()) - Expect(s).To(BeEmpty()) - - s, ok = jwtee.StringClaim(c, "i") - Expect(ok).To(BeFalse()) - Expect(s).To(BeEmpty()) - }) - }) -})