diff --git a/.golangci.yml b/.golangci.yml index 37c6ddc1..2f081a67 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -33,6 +33,7 @@ issues: - gocritic - gosec - goconst + - revive - path: _test\.go linters: - revive diff --git a/go.mod b/go.mod index 97ed3de7..41e0bea8 100644 --- a/go.mod +++ b/go.mod @@ -14,14 +14,14 @@ require ( github.com/fsnotify/fsnotify v1.7.0 github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 github.com/go-jose/go-jose/v3 v3.0.1 - github.com/golang-jwt/jwt v3.2.2+incompatible + github.com/golang-jwt/jwt/v5 v5.2.0 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 github.com/justinas/alice v1.2.0 github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa github.com/mitchellh/mapstructure v1.5.0 - github.com/oauth2-proxy/mockoidc v0.0.0-20240213094324-6186c302dcc8 + github.com/oauth2-proxy/mockoidc v0.0.0-20240214162133-caebfff84d25 github.com/oauth2-proxy/tools/reference-gen v0.0.0-20210118095127-56ffd7384404 github.com/ohler55/ojg v1.21.1 github.com/onsi/ginkgo v1.16.5 diff --git a/go.sum b/go.sum index 6a815958..a9d7c4e3 100644 --- a/go.sum +++ b/go.sum @@ -79,6 +79,8 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 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.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= +github.com/golang-jwt/jwt/v5 v5.2.0/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-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= @@ -157,6 +159,8 @@ github.com/oauth2-proxy/mockoidc v0.0.0-20220308204021-b9169deeb282 h1:TQMyrpijt github.com/oauth2-proxy/mockoidc v0.0.0-20220308204021-b9169deeb282/go.mod h1:rW25Kyd08Wdn3UVn0YBsDTSvReu0jqpmJKzxITPSjks= github.com/oauth2-proxy/mockoidc v0.0.0-20240213094324-6186c302dcc8 h1:DGMYYU5GYoPTxE2ZfBcAD/8j6/QZXi9iX4Vp2ojnGOc= github.com/oauth2-proxy/mockoidc v0.0.0-20240213094324-6186c302dcc8/go.mod h1:oHHSFtBUrYeEgVbIyOGqMP65t2ezJCcxeKhXvGpAcKc= +github.com/oauth2-proxy/mockoidc v0.0.0-20240214162133-caebfff84d25 h1:9bCMuD3TcnjeqjPT2gSlha4asp8NvgcFRYExCaikCxk= +github.com/oauth2-proxy/mockoidc v0.0.0-20240214162133-caebfff84d25/go.mod h1:eDjgYHYDJbPLBLsyZ6qRaugP0mX8vePOhZ5id1fdzJw= github.com/oauth2-proxy/tools/reference-gen v0.0.0-20210118095127-56ffd7384404 h1:ZpzR4Ou1nhldBG/vEzauoqyaUlofaUcLkv1C/gBK8ls= github.com/oauth2-proxy/tools/reference-gen v0.0.0-20210118095127-56ffd7384404/go.mod h1:YpORG8zs14vNlpXvuHYnnDvWazIRaDk02MaY8lafqdI= github.com/ohler55/ojg v1.21.0 h1:niqSS6yl3PQZJrqh7pKs/zinl4HebGe8urXEfpvlpYY= @@ -270,6 +274,8 @@ golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg= +golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= diff --git a/oauthproxy.go b/oauthproxy.go index 982ed7d8..df646f72 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -991,7 +991,7 @@ func (p *OAuthProxy) AuthOnly(rw http.ResponseWriter, req *http.Request) { // we are authenticated p.addHeadersForProxying(rw, session) - p.headersChain.Then(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + p.headersChain.Then(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { rw.WriteHeader(http.StatusAccepted) })).ServeHTTP(rw, req) } diff --git a/pkg/app/pagewriter/error_page.go b/pkg/app/pagewriter/error_page.go index 1a7a8312..e62a9a52 100644 --- a/pkg/app/pagewriter/error_page.go +++ b/pkg/app/pagewriter/error_page.go @@ -58,8 +58,6 @@ type ErrorPageOpts struct { func (e *errorPageWriter) WriteErrorPage(rw http.ResponseWriter, opts ErrorPageOpts) { rw.WriteHeader(opts.Status) - // We allow unescaped template.HTML since it is user configured options - /* #nosec G203 */ data := struct { Title string Message string @@ -76,7 +74,7 @@ func (e *errorPageWriter) WriteErrorPage(rw http.ResponseWriter, opts ErrorPageO StatusCode: opts.Status, Redirect: opts.RedirectURL, RequestID: opts.RequestID, - Footer: template.HTML(e.footer), + Footer: template.HTML(e.footer), // #nosec G203 -- We allow unescaped template.HTML since it is user configured options Version: e.version, } diff --git a/pkg/app/pagewriter/sign_in_page.go b/pkg/app/pagewriter/sign_in_page.go index ad2cdf15..bb34bb49 100644 --- a/pkg/app/pagewriter/sign_in_page.go +++ b/pkg/app/pagewriter/sign_in_page.go @@ -55,8 +55,6 @@ type signInPageWriter struct { // WriteSignInPage writes the sign-in page to the given response writer. // It uses the redirectURL to be able to set the final destination for the user post login. func (s *signInPageWriter) WriteSignInPage(rw http.ResponseWriter, req *http.Request, redirectURL string, statusCode int) { - // We allow unescaped template.HTML since it is user configured options - /* #nosec G203 */ t := struct { ProviderName string SignInMessage template.HTML @@ -69,14 +67,14 @@ func (s *signInPageWriter) WriteSignInPage(rw http.ResponseWriter, req *http.Req LogoData template.HTML }{ ProviderName: s.providerName, - SignInMessage: template.HTML(s.signInMessage), + SignInMessage: template.HTML(s.signInMessage), // #nosec G203 -- We allow unescaped template.HTML since it is user configured options StatusCode: statusCode, CustomLogin: s.displayLoginForm, Redirect: redirectURL, Version: s.version, ProxyPrefix: s.proxyPrefix, - Footer: template.HTML(s.footer), - LogoData: template.HTML(s.logoData), + Footer: template.HTML(s.footer), // #nosec G203 -- We allow unescaped template.HTML since it is user configured options + LogoData: template.HTML(s.logoData), // #nosec G203 -- We allow unescaped template.HTML since it is user configured options } err := s.template.Execute(rw, t) diff --git a/pkg/header/injector.go b/pkg/header/injector.go index 0d17dca1..aa6f675a 100644 --- a/pkg/header/injector.go +++ b/pkg/header/injector.go @@ -72,7 +72,7 @@ func newSecretInjector(name string, source *options.SecretSource) (valueInjector return nil, fmt.Errorf("error getting secret value: %v", err) } - return newInjectorFunc(func(header http.Header, session *sessionsapi.SessionState) { + return newInjectorFunc(func(header http.Header, _ *sessionsapi.SessionState) { header.Add(name, string(value)) }), nil } diff --git a/pkg/middleware/jwt_session.go b/pkg/middleware/jwt_session.go index 78ef5400..90f43d81 100644 --- a/pkg/middleware/jwt_session.go +++ b/pkg/middleware/jwt_session.go @@ -114,9 +114,8 @@ func (j *jwtSessionLoader) getBasicToken(token string) (string, error) { // check user, user+password, or just password for a token if j.jwtRegex.MatchString(user) { - // Support blank passwords or magic `x-oauth-basic` passwords - nothing else - /* #nosec G101 */ - if password == "" || password == "x-oauth-basic" { + if password == "x-oauth-basic" || // #nosec G101 -- Support blank passwords or magic `x-oauth-basic` passwords, nothing else + password == "" { return user, nil } } else if j.jwtRegex.MatchString(password) { diff --git a/pkg/middleware/jwt_session_test.go b/pkg/middleware/jwt_session_test.go index 2d056a78..b6597f8e 100644 --- a/pkg/middleware/jwt_session_test.go +++ b/pkg/middleware/jwt_session_test.go @@ -14,7 +14,7 @@ import ( "time" "github.com/coreos/go-oidc/v3/oidc" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v5" middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware" sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" . "github.com/onsi/ginkgo" @@ -401,7 +401,7 @@ Nnc3a3lGVWFCNUMxQnNJcnJMTWxka1dFaHluYmI4Ongtb2F1dGgtYmFzaWM=` type idTokenClaims struct { Email string `json:"email,omitempty"` Verified *bool `json:"email_verified,omitempty"` - jwt.StandardClaims + jwt.RegisteredClaims } type tokenToSessionTableInput struct { @@ -451,13 +451,12 @@ Nnc3a3lGVWFCNUMxQnNJcnJMTWxka1dFaHluYmI4Ongtb2F1dGgtYmFzaWM=` }, Entry("with no email", tokenToSessionTableInput{ idToken: idTokenClaims{ - StandardClaims: jwt.StandardClaims{ - Audience: "asdf1234", - ExpiresAt: expiresFuture.Unix(), - Id: "id-some-id", - IssuedAt: time.Now().Unix(), + RegisteredClaims: jwt.RegisteredClaims{ + Audience: jwt.ClaimStrings{"asdf1234"}, + ExpiresAt: jwt.NewNumericDate(expiresFuture), + IssuedAt: jwt.NewNumericDate(time.Now()), Issuer: "https://issuer.example.com", - NotBefore: 0, + NotBefore: jwt.NewNumericDate(time.Time{}), Subject: "123456789", }, }, @@ -468,13 +467,12 @@ Nnc3a3lGVWFCNUMxQnNJcnJMTWxka1dFaHluYmI4Ongtb2F1dGgtYmFzaWM=` }), Entry("with a verified email", tokenToSessionTableInput{ idToken: idTokenClaims{ - StandardClaims: jwt.StandardClaims{ - Audience: "asdf1234", - ExpiresAt: expiresFuture.Unix(), - Id: "id-some-id", - IssuedAt: time.Now().Unix(), + RegisteredClaims: jwt.RegisteredClaims{ + Audience: jwt.ClaimStrings{"asdf1234"}, + ExpiresAt: jwt.NewNumericDate(expiresFuture), + IssuedAt: jwt.NewNumericDate(time.Now()), Issuer: "https://issuer.example.com", - NotBefore: 0, + NotBefore: jwt.NewNumericDate(time.Time{}), Subject: "123456789", }, Email: "foo@example.com", @@ -487,13 +485,12 @@ Nnc3a3lGVWFCNUMxQnNJcnJMTWxka1dFaHluYmI4Ongtb2F1dGgtYmFzaWM=` }), Entry("with a non-verified email", tokenToSessionTableInput{ idToken: idTokenClaims{ - StandardClaims: jwt.StandardClaims{ - Audience: "asdf1234", - ExpiresAt: expiresFuture.Unix(), - Id: "id-some-id", - IssuedAt: time.Now().Unix(), + RegisteredClaims: jwt.RegisteredClaims{ + Audience: jwt.ClaimStrings{"asdf1234"}, + ExpiresAt: jwt.NewNumericDate(expiresFuture), + IssuedAt: jwt.NewNumericDate(time.Now()), Issuer: "https://issuer.example.com", - NotBefore: 0, + NotBefore: jwt.NewNumericDate(time.Time{}), Subject: "123456789", }, Email: "foo@example.com", diff --git a/pkg/providers/oidc/provider_verifier_test.go b/pkg/providers/oidc/provider_verifier_test.go index 1b0c06e7..b95ad551 100644 --- a/pkg/providers/oidc/provider_verifier_test.go +++ b/pkg/providers/oidc/provider_verifier_test.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v5" "github.com/oauth2-proxy/mockoidc" . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" @@ -90,7 +90,7 @@ var _ = Describe("ProviderVerifier", func() { type verifierTableInput struct { modifyOpts func(*ProviderVerifierOptions) - modifyClaims func(*jwt.StandardClaims) + modifyClaims func(claims *jwt.RegisteredClaims) expectedError string } @@ -109,11 +109,11 @@ var _ = Describe("ProviderVerifier", func() { Expect(err).ToNot(HaveOccurred()) now := time.Now() - claims := jwt.StandardClaims{ - Audience: m.Config().ClientID, + claims := jwt.RegisteredClaims{ + Audience: jwt.ClaimStrings{m.Config().ClientID}, Issuer: m.Issuer(), - ExpiresAt: now.Add(1 * time.Hour).Unix(), - IssuedAt: now.Unix(), + ExpiresAt: jwt.NewNumericDate(now.Add(1 * time.Hour)), + IssuedAt: jwt.NewNumericDate(now), Subject: "user", } if in.modifyClaims != nil { @@ -136,8 +136,8 @@ var _ = Describe("ProviderVerifier", func() { }, Entry("with the default opts and claims", &verifierTableInput{}), Entry("when the audience is mismatched", &verifierTableInput{ - modifyClaims: func(j *jwt.StandardClaims) { - j.Audience = "OtherClient" + modifyClaims: func(j *jwt.RegisteredClaims) { + j.Audience = jwt.ClaimStrings{"OtherClient"} }, expectedError: "audience from claim aud with value [OtherClient] does not match with any of allowed audiences", }), @@ -145,12 +145,12 @@ var _ = Describe("ProviderVerifier", func() { modifyOpts: func(p *ProviderVerifierOptions) { p.ExtraAudiences = []string{"ExtraIssuer"} }, - modifyClaims: func(j *jwt.StandardClaims) { - j.Audience = "ExtraIssuer" + modifyClaims: func(j *jwt.RegisteredClaims) { + j.Audience = jwt.ClaimStrings{"ExtraIssuer"} }, }), Entry("when the issuer is mismatched", &verifierTableInput{ - modifyClaims: func(j *jwt.StandardClaims) { + modifyClaims: func(j *jwt.RegisteredClaims) { j.Issuer = "OtherIssuer" }, expectedError: "failed to verify token: oidc: id token issued by a different provider", @@ -159,13 +159,13 @@ var _ = Describe("ProviderVerifier", func() { modifyOpts: func(p *ProviderVerifierOptions) { p.SkipIssuerVerification = true }, - modifyClaims: func(j *jwt.StandardClaims) { + modifyClaims: func(j *jwt.RegisteredClaims) { j.Issuer = "OtherIssuer" }, }), Entry("when the token has expired", &verifierTableInput{ - modifyClaims: func(j *jwt.StandardClaims) { - j.ExpiresAt = time.Now().Add(-1 * time.Hour).Unix() + modifyClaims: func(j *jwt.RegisteredClaims) { + j.ExpiresAt = jwt.NewNumericDate(time.Now().Add(-1 * time.Hour)) }, expectedError: "failed to verify token: oidc: token is expired", }), diff --git a/pkg/upstream/proxy.go b/pkg/upstream/proxy.go index 0f3f5740..8eaefc1a 100644 --- a/pkg/upstream/proxy.go +++ b/pkg/upstream/proxy.go @@ -122,7 +122,7 @@ func (m *multiUpstreamProxy) registerRewriteHandler(upstream options.Upstream, h rewrite := newRewritePath(rewriteRegExp, upstream.RewriteTarget, writer) h := alice.New(rewrite).Then(handler) - m.serveMux.MatcherFunc(func(req *http.Request, match *mux.RouteMatch) bool { + m.serveMux.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool { return rewriteRegExp.MatchString(req.URL.Path) }).Handler(h) diff --git a/pkg/validation/options.go b/pkg/validation/options.go index 8c804829..b14439a7 100644 --- a/pkg/validation/options.go +++ b/pkg/validation/options.go @@ -30,10 +30,8 @@ func Validate(o *options.Options) error { msgs = parseSignatureKey(o, msgs) if o.SSLInsecureSkipVerify { - // InsecureSkipVerify is a configurable option we allow - /* #nosec G402 */ insecureTransport := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // #nosec G402 -- InsecureSkipVerify is a configurable option we allow } http.DefaultClient = &http.Client{Transport: insecureTransport} } else if len(o.Providers[0].CAFiles) > 0 { diff --git a/providers/adfs_test.go b/providers/adfs_test.go index 92b1ac0d..6ffe97f2 100755 --- a/providers/adfs_test.go +++ b/providers/adfs_test.go @@ -12,7 +12,7 @@ import ( "strings" "github.com/coreos/go-oidc/v3/oidc" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v5" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc" diff --git a/providers/azure_test.go b/providers/azure_test.go index 27d70b88..46742042 100644 --- a/providers/azure_test.go +++ b/providers/azure_test.go @@ -14,7 +14,7 @@ import ( "time" "github.com/coreos/go-oidc/v3/oidc" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v5" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc" @@ -311,9 +311,9 @@ func TestAzureProviderRedeem(t *testing.T) { if testCase.EmailFromIDToken != "" { var err error token := idTokenClaims{ - StandardClaims: jwt.StandardClaims{Audience: "cd6d4fae-f6a6-4a34-8454-2c6b598e9532"}, - Email: testCase.EmailFromIDToken, - Groups: []string{"aa", "bb"}, + RegisteredClaims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"cd6d4fae-f6a6-4a34-8454-2c6b598e9532"}}, + Email: testCase.EmailFromIDToken, + Groups: []string{"aa", "bb"}, } idTokenString, err = newSignedTestIDToken(token) assert.NoError(t, err) @@ -321,9 +321,9 @@ func TestAzureProviderRedeem(t *testing.T) { if testCase.EmailFromAccessToken != "" { var err error token := idTokenClaims{ - StandardClaims: jwt.StandardClaims{Audience: "cd6d4fae-f6a6-4a34-8454-2c6b598e9532"}, - Email: testCase.EmailFromAccessToken, - Groups: []string{"aa", "bb"}, + RegisteredClaims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"cd6d4fae-f6a6-4a34-8454-2c6b598e9532"}}, + Email: testCase.EmailFromAccessToken, + Groups: []string{"aa", "bb"}, } accessTokenString, err = newSignedTestIDToken(token) assert.NoError(t, err) @@ -390,8 +390,8 @@ func TestAzureProviderRefresh(t *testing.T) { subject := "foo" idToken := idTokenClaims{ Email: email, - StandardClaims: jwt.StandardClaims{ - Audience: "cd6d4fae-f6a6-4a34-8454-2c6b598e9532", + RegisteredClaims: jwt.RegisteredClaims{ + Audience: jwt.ClaimStrings{"cd6d4fae-f6a6-4a34-8454-2c6b598e9532"}, Subject: subject, }, } diff --git a/providers/logingov.go b/providers/logingov.go index e4f09218..30b144a0 100644 --- a/providers/logingov.go +++ b/providers/logingov.go @@ -13,7 +13,7 @@ import ( "time" "github.com/go-jose/go-jose/v3" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v5" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" @@ -146,12 +146,12 @@ type loginGovCustomClaims struct { Birthdate string `json:"birthdate"` AtHash string `json:"at_hash"` CHash string `json:"c_hash"` - jwt.StandardClaims + jwt.RegisteredClaims } // checkNonce checks the nonce in the id_token func checkNonce(idToken string, p *LoginGovProvider) (err error) { - token, err := jwt.ParseWithClaims(idToken, &loginGovCustomClaims{}, func(token *jwt.Token) (interface{}, error) { + token, err := jwt.ParseWithClaims(idToken, &loginGovCustomClaims{}, func(_ *jwt.Token) (interface{}, error) { var pubkeys jose.JSONWebKeySet rerr := requests.New(p.PubJWKURL.String()).Do().UnmarshalInto(&pubkeys) if rerr != nil { @@ -207,12 +207,11 @@ func (p *LoginGovProvider) Redeem(ctx context.Context, _, code, codeVerifier str return nil, ErrMissingCode } - claims := &jwt.StandardClaims{ + claims := &jwt.RegisteredClaims{ Issuer: p.ClientID, Subject: p.ClientID, - Audience: p.RedeemURL.String(), - ExpiresAt: time.Now().Add(5 * time.Minute).Unix(), - Id: randSeq(32), + Audience: jwt.ClaimStrings{p.RedeemURL.String()}, + ExpiresAt: jwt.NewNumericDate(time.Now().Add(5 * time.Minute)), } token := jwt.NewWithClaims(jwt.GetSigningMethod("RS256"), claims) ss, err := token.SignedString(p.JWTKey) diff --git a/providers/logingov_test.go b/providers/logingov_test.go index a8853490..570ab65b 100644 --- a/providers/logingov_test.go +++ b/providers/logingov_test.go @@ -16,7 +16,7 @@ import ( "time" "github.com/go-jose/go-jose/v3" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v5" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" . "github.com/onsi/gomega" "github.com/stretchr/testify/assert" @@ -174,7 +174,7 @@ func TestLoginGovProviderSessionData(t *testing.T) { Birthdate string `json:"birthdate"` AtHash string `json:"at_hash"` CHash string `json:"c_hash"` - jwt.StandardClaims + jwt.RegisteredClaims } claims := MyCustomClaims{ "http://idmanagement.gov/ns/assurance/loa/1", @@ -186,13 +186,12 @@ func TestLoginGovProviderSessionData(t *testing.T) { "", "", "", - jwt.StandardClaims{ - Audience: "Audience", - ExpiresAt: time.Now().Unix() + expiresIn, - Id: "foo", - IssuedAt: time.Now().Unix(), + jwt.RegisteredClaims{ + Audience: jwt.ClaimStrings{"Audience"}, + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expiresIn) * time.Second)), + IssuedAt: jwt.NewNumericDate(time.Now()), Issuer: "https://idp.int.login.gov", - NotBefore: time.Now().Unix() - 1, + NotBefore: jwt.NewNumericDate(time.Now().Add(-1 * time.Second)), Subject: "b2d2d115-1d7e-4579-b9d6-f8e84f4f56ca", }, } @@ -268,7 +267,7 @@ func TestLoginGovProviderBadNonce(t *testing.T) { Birthdate string `json:"birthdate"` AtHash string `json:"at_hash"` CHash string `json:"c_hash"` - jwt.StandardClaims + jwt.RegisteredClaims } claims := MyCustomClaims{ "http://idmanagement.gov/ns/assurance/loa/1", @@ -280,13 +279,12 @@ func TestLoginGovProviderBadNonce(t *testing.T) { "", "", "", - jwt.StandardClaims{ - Audience: "Audience", - ExpiresAt: time.Now().Unix() + expiresIn, - Id: "foo", - IssuedAt: time.Now().Unix(), + jwt.RegisteredClaims{ + Audience: jwt.ClaimStrings{"Audience"}, + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expiresIn) * time.Second)), + IssuedAt: jwt.NewNumericDate(time.Now()), Issuer: "https://idp.int.login.gov", - NotBefore: time.Now().Unix() - 1, + NotBefore: jwt.NewNumericDate(time.Now().Add(-1 * time.Second)), Subject: "b2d2d115-1d7e-4579-b9d6-f8e84f4f56ca", }, } diff --git a/providers/provider_data_test.go b/providers/provider_data_test.go index 6bdcf5cc..044a77b1 100644 --- a/providers/provider_data_test.go +++ b/providers/provider_data_test.go @@ -19,7 +19,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/coreos/go-oidc/v3/oidc" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v5" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption" internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc" @@ -37,45 +37,44 @@ const ( oidcSecret = "SuperSecret123456789" oidcNonce = "abcde12345edcba09876abcde12345ff" - failureTokenID = "this-id-fails-verification" + failureIssuer = "this-id-fails-verification" ) var ( verified = true unverified = false - standardClaims = jwt.StandardClaims{ - Audience: oidcClientID, - ExpiresAt: time.Now().Add(time.Duration(5) * time.Minute).Unix(), - Id: "id-some-id", - IssuedAt: time.Now().Unix(), + registeredClaims = jwt.RegisteredClaims{ + Audience: jwt.ClaimStrings{oidcClientID}, + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(5) * time.Minute)), + IssuedAt: jwt.NewNumericDate(time.Now()), Issuer: oidcIssuer, - NotBefore: 0, + NotBefore: jwt.NewNumericDate(time.Time{}), Subject: "123456789", } defaultIDToken = idTokenClaims{ - Name: "Jane Dobbs", - Email: "janed@me.com", - Phone: "+4798765432", - Picture: "http://mugbook.com/janed/me.jpg", - Groups: []string{"test:a", "test:b"}, - Roles: []string{"test:c", "test:d"}, - Verified: &verified, - Nonce: encryption.HashNonce([]byte(oidcNonce)), - StandardClaims: standardClaims, + Name: "Jane Dobbs", + Email: "janed@me.com", + Phone: "+4798765432", + Picture: "http://mugbook.com/janed/me.jpg", + Groups: []string{"test:a", "test:b"}, + Roles: []string{"test:c", "test:d"}, + Verified: &verified, + Nonce: encryption.HashNonce([]byte(oidcNonce)), + RegisteredClaims: registeredClaims, } numericGroupsIDToken = idTokenClaims{ - Name: "Jane Dobbs", - Email: "janed@me.com", - Phone: "+4798765432", - Picture: "http://mugbook.com/janed/me.jpg", - Groups: []interface{}{1, 2, 3}, - Roles: []string{"test:c", "test:d"}, - Verified: &verified, - Nonce: encryption.HashNonce([]byte(oidcNonce)), - StandardClaims: standardClaims, + Name: "Jane Dobbs", + Email: "janed@me.com", + Phone: "+4798765432", + Picture: "http://mugbook.com/janed/me.jpg", + Groups: []interface{}{1, 2, 3}, + Roles: []string{"test:c", "test:d"}, + Verified: &verified, + Nonce: encryption.HashNonce([]byte(oidcNonce)), + RegisteredClaims: registeredClaims, } complexGroupsIDToken = idTokenClaims{ @@ -91,24 +90,24 @@ var ( 12345, "Just::A::String", }, - Roles: []string{"test:simple", "test:roles"}, - Verified: &verified, - StandardClaims: standardClaims, + Roles: []string{"test:simple", "test:roles"}, + Verified: &verified, + RegisteredClaims: registeredClaims, } unverifiedIDToken = idTokenClaims{ - Name: "Mystery Man", - Email: "unverified@email.com", - Phone: "+4025205729", - Picture: "http://mugbook.com/unverified/email.jpg", - Groups: []string{"test:a", "test:b"}, - Roles: []string{"test:c", "test:d"}, - Verified: &unverified, - StandardClaims: standardClaims, + Name: "Mystery Man", + Email: "unverified@email.com", + Phone: "+4025205729", + Picture: "http://mugbook.com/unverified/email.jpg", + Groups: []string{"test:a", "test:b"}, + Roles: []string{"test:c", "test:d"}, + Verified: &unverified, + RegisteredClaims: registeredClaims, } minimalIDToken = idTokenClaims{ - StandardClaims: standardClaims, + RegisteredClaims: registeredClaims, } ) @@ -121,7 +120,7 @@ type idTokenClaims struct { Roles interface{} `json:"roles,omitempty"` Verified *bool `json:"email_verified,omitempty"` Nonce string `json:"nonce,omitempty"` - jwt.StandardClaims + jwt.RegisteredClaims } type mockJWKS struct{} @@ -134,7 +133,7 @@ func (mockJWKS) VerifySignature(_ context.Context, jwt string) ([]byte, error) { tokenClaims := &idTokenClaims{} err = json.Unmarshal(decoded, tokenClaims) - if err != nil || tokenClaims.Id == failureTokenID { + if err != nil || tokenClaims.Issuer == failureIssuer { return nil, fmt.Errorf("the validation failed for subject [%v]", tokenClaims.Subject) } @@ -158,7 +157,7 @@ func newTestOauth2Token() *oauth2.Token { func TestProviderData_verifyIDToken(t *testing.T) { failureIDToken := defaultIDToken - failureIDToken.Id = failureTokenID + failureIDToken.Issuer = failureIssuer testCases := map[string]struct { IDToken *idTokenClaims @@ -172,13 +171,6 @@ func TestProviderData_verifyIDToken(t *testing.T) { ExpectIDToken: true, ExpectedError: nil, }, - "Invalid ID Token": { - IDToken: &failureIDToken, - Verifier: true, - ExpectIDToken: false, - ExpectedError: errors.New("failed to verify token: failed to verify signature: " + - "the validation failed for subject [123456789]"), - }, "Missing ID Token": { IDToken: nil, Verifier: true,