diff --git a/.github/composites/jfrog/action.yaml b/.github/composites/jfrog/action.yaml index f9cce77..50b6df8 100644 --- a/.github/composites/jfrog/action.yaml +++ b/.github/composites/jfrog/action.yaml @@ -58,4 +58,4 @@ runs: if: success() && ${{ inputs.version }} != '' shell: bash run: | - jf rt bdi c --max-builds=5 \ No newline at end of file + jf rt bdi c --max-builds=5 diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 7368964..3be290b 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -26,7 +26,7 @@ jobs: - name: Setup go uses: actions/setup-go@v5 with: - go-version: 1.21 + go-version: 1.24 - name: Setup deps shell: bash diff --git a/Dockerfile b/Dockerfile index 4e94510..d542163 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.22 as build +FROM golang:1.24 as build WORKDIR /go/src/ diff --git a/client/jwt/jwt.go b/client/jwt/jwt.go index cf094c1..ed5dec6 100644 --- a/client/jwt/jwt.go +++ b/client/jwt/jwt.go @@ -21,7 +21,7 @@ import ( "fmt" "strings" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v5" ) // Base64Encode encodes a byte array to a base 64 string. @@ -44,7 +44,7 @@ func CryptoHash(base string) string { // DecodeToken decodes the non-secret part of the jwt token and extracts the associated claims. // Note: this method does not verify the tokens validity, for this the corresponding endpoint of the iam service should be used. -func DecodeToken(token string) (*jwt.StandardClaims, error) { +func DecodeToken(token string) (*jwt.RegisteredClaims, error) { s := strings.Split(token, ".") if len(s) < 2 { return nil, fmt.Errorf("token format is wrong") @@ -53,7 +53,7 @@ func DecodeToken(token string) (*jwt.StandardClaims, error) { if err != nil { return nil, fmt.Errorf("could not decode token part") } - m := new(jwt.StandardClaims) + m := new(jwt.RegisteredClaims) err = json.Unmarshal(b, &m) if err != nil { return nil, fmt.Errorf("could not unmarshal claims") diff --git a/go.mod b/go.mod index 3d8b04d..013e0e0 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,12 @@ module github.com/ingka-group/iam-proxy -go 1.21 +go 1.24.1 require ( contrib.go.opencensus.io/exporter/ocagent v0.7.0 github.com/blendle/zapdriver v1.3.1 github.com/gin-contrib/zap v1.1.0 github.com/gin-gonic/gin v1.10.0 - github.com/golang-jwt/jwt v3.2.2+incompatible github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 @@ -42,6 +41,7 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.20.0 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/golang-jwt/jwt/v5 v5.2.2 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect @@ -59,11 +59,11 @@ require ( go.opentelemetry.io/proto/otlp v1.1.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.8.0 // indirect - golang.org/x/crypto v0.23.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/crypto v0.37.0 // indirect + golang.org/x/net v0.39.0 // indirect + golang.org/x/sync v0.13.0 // indirect + golang.org/x/sys v0.32.0 // indirect + golang.org/x/text v0.24.0 // indirect google.golang.org/api v0.158.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect diff --git a/go.sum b/go.sum index 3dbf447..d9ee147 100644 --- a/go.sum +++ b/go.sum @@ -82,8 +82,8 @@ github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBEx github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -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-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -240,8 +240,8 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -293,8 +293,8 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -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.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= +golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -308,8 +308,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -336,16 +336,16 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/internal/api/http_test.go b/internal/api/http_test.go index d4908eb..97f7c4a 100644 --- a/internal/api/http_test.go +++ b/internal/api/http_test.go @@ -118,7 +118,7 @@ func TestHealth(t *testing.T) { } if diff := cmp.Diff(h, tt.want); diff != "" { - t.Errorf(diff) + t.Error(diff) } }) } diff --git a/internal/api/iam_end2end_test.go b/internal/api/iam_end2end_test.go index 52da6e6..07db09b 100644 --- a/internal/api/iam_end2end_test.go +++ b/internal/api/iam_end2end_test.go @@ -94,7 +94,7 @@ func TestClient_GenerateToken(t *testing.T) { t.Run(tt.name, func(t *testing.T) { srv, err := service.New(defaultConfig(tt.iam)) if err != nil { - t.Errorf(fmt.Sprintf("could not create service instance: %s", err.Error())) + t.Errorf("could not create service instance: %s", err.Error()) } tt.args.cfg.Service = srv @@ -193,7 +193,7 @@ func TestClient_ParseToken(t *testing.T) { t.Run(tt.name, func(t *testing.T) { srv, err := service.New(defaultConfig(tt.iam)) if err != nil { - t.Errorf(fmt.Sprintf("could not create service instance: %s", err.Error())) + t.Errorf("could not create service instance: %s", err.Error()) } tt.args.cfg.Service = srv @@ -306,7 +306,7 @@ func TestClient_IdentityToken(t *testing.T) { t.Run(tt.name, func(t *testing.T) { srv, err := service.New(defaultConfig(tt.iam)) if err != nil { - t.Errorf(fmt.Sprintf("could not create service instance: %s", err.Error())) + t.Errorf("could not create service instance: %s", err.Error()) } tt.args.cfg.Service = srv @@ -355,7 +355,7 @@ func TestClient_End2End(t *testing.T) { srv, err := service.New(defaultConfig(cfg)) if err != nil { - t.Errorf(fmt.Sprintf("could not create service instance: %s", err.Error())) + t.Errorf("could not create service instance: %s", err.Error()) } c, err := New(Config{ diff --git a/internal/service/jwt.go b/internal/service/jwt.go index ca1b820..ac61c98 100644 --- a/internal/service/jwt.go +++ b/internal/service/jwt.go @@ -16,10 +16,11 @@ package service import ( "context" + "errors" "fmt" "time" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" "github.com/ingka-group/iam-proxy/internal/models" @@ -35,7 +36,7 @@ const ( // Claims defines the token claims. type Claims struct { - jwt.StandardClaims + jwt.RegisteredClaims } // verifyUser checks the iam privileges for the given client id and secret. @@ -65,18 +66,18 @@ func (s *Service) GenerateToken(_ context.Context, clientID, clientSecret string return "", "", 0, fmt.Errorf("user not authorized to use iam service: %w", err) } - expiration := time.Now().Add(expirationInterval).Unix() + expiration := time.Now().Add(expirationInterval) - accessToken, err := s.createToken(&jwt.StandardClaims{ - Id: uuid.New().String(), - ExpiresAt: expiration, + accessToken, err := s.createToken(&jwt.RegisteredClaims{ + ID: uuid.New().String(), + ExpiresAt: jwt.NewNumericDate(expiration), Issuer: issuer, }) if err != nil { return "", "", 0, fmt.Errorf("could not generate access token for %s: %w", appName, err) } - identityToken, err := s.createToken(&jwt.StandardClaims{ + identityToken, err := s.createToken(&jwt.RegisteredClaims{ Issuer: issuer, Subject: appName, }) @@ -87,7 +88,7 @@ func (s *Service) GenerateToken(_ context.Context, clientID, clientSecret string return accessToken, identityToken, int64(expirationInterval.Seconds()), nil } -func (s *Service) createToken(claims *jwt.StandardClaims) (string, error) { +func (s *Service) createToken(claims *jwt.RegisteredClaims) (string, error) { token := jwt.NewWithClaims(jwt.SigningMethodHS512, claims) tokenString, err := token.SignedString(s.secret) if err != nil { @@ -98,7 +99,7 @@ func (s *Service) createToken(claims *jwt.StandardClaims) (string, error) { // ParseToken parses the token and confirms its validity. func (s *Service) ParseToken(tokenString string) (string, error) { - token, err := jwt.ParseWithClaims(tokenString, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) { + token, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } @@ -109,12 +110,12 @@ func (s *Service) ParseToken(tokenString string) (string, error) { return "", fmt.Errorf("%s: %w", parseTokenError, err) } - if claims, ok := token.Claims.(*jwt.StandardClaims); ok && token.Valid { + if claims, ok := token.Claims.(*jwt.RegisteredClaims); ok && token.Valid { if claims.Issuer != issuer { - return "", fmt.Errorf(invalidIssuer) + return "", errors.New(invalidIssuer) } return claims.Subject, nil } - return "", fmt.Errorf(invalidTokenError) + return "", errors.New(invalidTokenError) } diff --git a/internal/service/jwt_test.go b/internal/service/jwt_test.go index 92deae2..a2bef2f 100644 --- a/internal/service/jwt_test.go +++ b/internal/service/jwt_test.go @@ -20,7 +20,7 @@ import ( "testing" "time" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/assert" jwtmodule "github.com/ingka-group/iam-proxy/client/jwt" @@ -88,8 +88,8 @@ func TestService_ParseToken_ParseError(t *testing.T) { func TestService_ParseToken_Expired(t *testing.T) { srv := newTestService() - claims := &jwt.StandardClaims{ - ExpiresAt: time.Now().Add(-1 * time.Second).Unix(), + claims := &jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(-1 * time.Second)), Issuer: issuer, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) @@ -98,15 +98,15 @@ func TestService_ParseToken_Expired(t *testing.T) { _, err = srv.ParseToken(tokenString) assert.Error(t, err) - assert.True(t, strings.Contains(err.Error(), "token is expired by 1s")) + assert.True(t, strings.Contains(err.Error(), "token is expired")) assert.True(t, strings.Contains(err.Error(), parseTokenError)) } func TestService_ParseToken_IssuerError(t *testing.T) { srv := newTestService() - claims := &jwt.StandardClaims{ - ExpiresAt: time.Now().Unix(), + claims := &jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)), Issuer: "other-issuer", } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)