diff --git a/go.mod b/go.mod index 3bfa1ce..8644062 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,18 @@ module github.com/coreos/go-oidc/v3 -go 1.19 +go 1.21 + +toolchain go1.22.0 require ( - github.com/go-jose/go-jose/v3 v3.0.1 + github.com/go-jose/go-jose/v4 v4.0.1 golang.org/x/net v0.17.0 golang.org/x/oauth2 v0.13.0 ) require ( github.com/golang/protobuf v1.5.3 // indirect - github.com/stretchr/testify v1.7.0 // indirect - golang.org/x/crypto v0.14.0 // indirect + golang.org/x/crypto v0.19.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/protobuf v1.31.0 // indirect ) diff --git a/go.sum b/go.sum index 36cfc3a..5d382b8 100644 --- a/go.sum +++ b/go.sum @@ -1,28 +1,24 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= -github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +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/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= +github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -33,7 +29,6 @@ golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -55,6 +50,5 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -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/oidc/jose.go b/oidc/jose.go index b7bd092..f42d37d 100644 --- a/oidc/jose.go +++ b/oidc/jose.go @@ -1,5 +1,7 @@ package oidc +import jose "github.com/go-jose/go-jose/v4" + // JOSE asymmetric signing algorithm values as defined by RFC 7518 // // see: https://tools.ietf.org/html/rfc7518#section-3.1 @@ -15,3 +17,16 @@ const ( PS512 = "PS512" // RSASSA-PSS using SHA512 and MGF1-SHA512 EdDSA = "EdDSA" // Ed25519 using SHA-512 ) + +var allAlgs = []jose.SignatureAlgorithm{ + jose.RS256, + jose.RS384, + jose.RS512, + jose.ES256, + jose.ES384, + jose.ES512, + jose.PS256, + jose.PS384, + jose.PS512, + jose.EdDSA, +} diff --git a/oidc/jwks.go b/oidc/jwks.go index b1e3f7e..9a70c14 100644 --- a/oidc/jwks.go +++ b/oidc/jwks.go @@ -13,7 +13,7 @@ import ( "sync" "time" - jose "github.com/go-jose/go-jose/v3" + jose "github.com/go-jose/go-jose/v4" ) // StaticKeySet is a verifier that validates JWT against a static set of public keys. @@ -25,7 +25,9 @@ type StaticKeySet struct { // VerifySignature compares the signature against a static set of public keys. func (s *StaticKeySet) VerifySignature(ctx context.Context, jwt string) ([]byte, error) { - jws, err := jose.ParseSigned(jwt) + // Algorithms are already checked by Verifier, so this parse method accepts + // any algorithm. + jws, err := jose.ParseSigned(jwt, allAlgs) if err != nil { return nil, fmt.Errorf("parsing jwt: %v", err) } @@ -127,8 +129,13 @@ var parsedJWTKey contextKey func (r *RemoteKeySet) VerifySignature(ctx context.Context, jwt string) ([]byte, error) { jws, ok := ctx.Value(parsedJWTKey).(*jose.JSONWebSignature) if !ok { + // The algorithm values are already enforced by the Validator, which also sets + // the context value above to pre-parsed signature. + // + // Practically, this codepath isn't called in normal use of this package, but + // if it is, the algorithms have already been checked. var err error - jws, err = jose.ParseSigned(jwt) + jws, err = jose.ParseSigned(jwt, allAlgs) if err != nil { return nil, fmt.Errorf("oidc: malformed jwt: %v", err) } diff --git a/oidc/jwks_test.go b/oidc/jwks_test.go index b9545ea..2cbf38b 100644 --- a/oidc/jwks_test.go +++ b/oidc/jwks_test.go @@ -17,7 +17,7 @@ import ( "testing" "time" - jose "github.com/go-jose/go-jose/v3" + jose "github.com/go-jose/go-jose/v4" ) type keyServer struct { @@ -146,7 +146,7 @@ func TestKeyVerifyContextCanceled(t *testing.T) { payload := []byte("a secret") good := newECDSAKey(t) - jws, err := jose.ParseSigned(good.sign(t, payload)) + jws, err := jose.ParseSigned(good.sign(t, payload), allAlgs) if err != nil { t.Fatal(err) } @@ -185,11 +185,11 @@ func testKeyVerify(t *testing.T, good, bad *signingKey, verification ...*signing payload := []byte("a secret") - jws, err := jose.ParseSigned(good.sign(t, payload)) + jws, err := jose.ParseSigned(good.sign(t, payload), allAlgs) if err != nil { t.Fatal(err) } - badJWS, err := jose.ParseSigned(bad.sign(t, payload)) + badJWS, err := jose.ParseSigned(bad.sign(t, payload), allAlgs) if err != nil { t.Fatal(err) } @@ -234,11 +234,11 @@ func TestRotation(t *testing.T) { key2.keyID = "key2" payload := []byte("a secret") - jws1, err := jose.ParseSigned(key1.sign(t, payload)) + jws1, err := jose.ParseSigned(key1.sign(t, payload), allAlgs) if err != nil { t.Fatal(err) } - jws2, err := jose.ParseSigned(key2.sign(t, payload)) + jws2, err := jose.ParseSigned(key2.sign(t, payload), allAlgs) if err != nil { t.Fatal(err) } diff --git a/oidc/verify.go b/oidc/verify.go index 0bca49a..0ac58d2 100644 --- a/oidc/verify.go +++ b/oidc/verify.go @@ -12,7 +12,7 @@ import ( "strings" "time" - jose "github.com/go-jose/go-jose/v3" + jose "github.com/go-jose/go-jose/v4" "golang.org/x/oauth2" ) @@ -310,7 +310,16 @@ func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDTok return t, nil } - jws, err := jose.ParseSigned(rawIDToken) + var supportedSigAlgs []jose.SignatureAlgorithm + for _, alg := range v.config.SupportedSigningAlgs { + supportedSigAlgs = append(supportedSigAlgs, jose.SignatureAlgorithm(alg)) + } + if len(supportedSigAlgs) == 0 { + // If no algorithms were specified by both the config and discovery, default + // to the one mandatory algorithm "RS256". + supportedSigAlgs = []jose.SignatureAlgorithm{jose.RS256} + } + jws, err := jose.ParseSigned(rawIDToken, supportedSigAlgs) if err != nil { return nil, fmt.Errorf("oidc: malformed jwt: %v", err) } @@ -322,17 +331,7 @@ func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDTok default: return nil, fmt.Errorf("oidc: multiple signatures on id token not supported") } - sig := jws.Signatures[0] - supportedSigAlgs := v.config.SupportedSigningAlgs - if len(supportedSigAlgs) == 0 { - supportedSigAlgs = []string{RS256} - } - - if !contains(supportedSigAlgs, sig.Header.Algorithm) { - return nil, fmt.Errorf("oidc: id token signed with unsupported algorithm, expected %q got %q", supportedSigAlgs, sig.Header.Algorithm) - } - t.sigAlgorithm = sig.Header.Algorithm ctx = context.WithValue(ctx, parsedJWTKey, jws)