Disallow replay of `private_key_jwt` by blacklisting JTIs in Hydra
Description
In Hydra (an OAuth2 Server and OpenID Certified™ OpenID Connect Provider written in Go), before version 1.4.0+oryOS.17, when using client authentication method 'private_key_jwt' [1], OpenId specification says the following about assertion jti: "A unique identifier for the token, which can be used to prevent reuse of the token. These tokens MUST only be used once, unless conditions for reuse were negotiated between the parties". Hydra does not check the uniqueness of this jti value. Exploiting this vulnerability is somewhat difficult because: - TLS protects against MITM which makes it difficult to intercept valid tokens for replay attacks - The expiry time of the JWT gives only a short window of opportunity where it could be replayed This has been patched in version v1.4.0+oryOS.17
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/ory/hydraGo | < 1.4.0 | 1.4.0 |
Affected products
1Patches
1700d17d3b7d5Merge pull request from GHSA-3p3g-vpw6-4w66
17 files changed · +464 −47
client/manager.go+1 −1 modified@@ -35,7 +35,7 @@ type Manager interface { } type Storage interface { - fosite.Storage + GetClient(ctx context.Context, id string) (fosite.Client, error) CreateClient(ctx context.Context, c *Client) error
driver/configuration/provider_viper_test.go+4 −3 modified@@ -8,13 +8,14 @@ import ( "strings" "testing" - "github.com/ory/hydra/x" - "github.com/ory/viper" - "github.com/ory/x/logrusx" "github.com/rs/cors" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/ory/hydra/x" + "github.com/ory/viper" + "github.com/ory/x/logrusx" ) func setupEnv(env map[string]string) func(t *testing.T) (func(), func()) {
go.mod+3 −3 modified@@ -14,7 +14,7 @@ require ( github.com/go-swagger/go-swagger v0.22.1-0.20200306221957-4aad3a5f78b8 github.com/gobuffalo/packr v1.24.0 github.com/gobwas/glob v0.2.3 - github.com/golang/mock v1.3.1 + github.com/golang/mock v1.4.3 github.com/google/uuid v1.1.1 github.com/gorilla/sessions v1.1.4-0.20181208214519-12bd4761fc66 github.com/gtank/cryptopasta v0.0.0-20170601214702-1f550f6f2f69 @@ -26,7 +26,7 @@ require ( github.com/oleiade/reflections v1.0.0 github.com/olekukonko/tablewriter v0.0.1 github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e - github.com/ory/fosite v0.30.6 + github.com/ory/fosite v0.31.0 github.com/ory/go-acc v0.2.1 github.com/ory/graceful v0.1.1 github.com/ory/herodot v0.7.0 @@ -53,7 +53,7 @@ require ( github.com/uber/jaeger-client-go v2.22.1+incompatible github.com/urfave/negroni v1.0.0 go.opentelemetry.io/otel v0.2.1 - golang.org/x/crypto v0.0.0-20200320181102-891825fb96df + golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d golang.org/x/tools v0.0.0-20200313205530-4303120df7d8
go.sum+9 −0 modified@@ -452,6 +452,8 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -737,6 +739,8 @@ github.com/ory/dockertest/v3 v3.5.4/go.mod h1:J8ZUbNB2FOhm1cFZW9xBpDsODqsSWcyYgt github.com/ory/fosite v0.29.0/go.mod h1:0atSZmXO7CAcs6NPMI/Qtot8tmZYj04Nddoold4S2h0= github.com/ory/fosite v0.30.6 h1:t1EQHkGv3gVODC9oBvoEi3nIyZ4kvC/ayCsvyswDvis= github.com/ory/fosite v0.30.6/go.mod h1:Lq9qQ9Sl6mcea2Tt8J7PU+wUeFYPZ+vg7N3zPVKGbN8= +github.com/ory/fosite v0.31.0 h1:NZ0FA4ywPEYrCGLNVBAz2dq8vTacLDbbO4Iiy68WCKQ= +github.com/ory/fosite v0.31.0/go.mod h1:lSSqjo8Kr/U1P3kJWxsNGHmq7TnH/7pS1ijvQRT7G+g= github.com/ory/go-acc v0.0.0-20181118080137-ddc355013f90 h1:Bpk3eqc3rbJT2mE+uS9ETzmi2cEL4RuIKz2iUeteh04= github.com/ory/go-acc v0.0.0-20181118080137-ddc355013f90/go.mod h1:sxnvPCxChFuSmTJGj8FdMupeq1BezCiEpDjTUXQ4hf4= github.com/ory/go-acc v0.2.1 h1:Pwcmwd/cSnwJsYN76+w3HU7oXeWFTkwj/KUj1qGDrVw= @@ -1025,6 +1029,8 @@ golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAak golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200320181102-891825fb96df h1:lDWgvUvNnaTnNBc/dwOty86cFeKoKWbwy2wQj0gIxbU= golang.org/x/crypto v0.0.0-20200320181102-891825fb96df/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1153,6 +1159,7 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepx golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= @@ -1324,5 +1331,7 @@ mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZI mvdan.cc/unparam v0.0.0-20190917161559-b83a221c10a2/go.mod h1:rCqoQrfAmpTX/h2APczwM7UymU/uvaOluiVPIYCSY/k= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= sourcegraph.com/sqs/pbtypes v1.0.0/go.mod h1:3AciMUv4qUuRHRHhOG4TZOB+72GdPVz5k+c648qsFS4=
oauth2/fosite_store_helpers.go+116 −0 modified@@ -22,11 +22,14 @@ package oauth2 import ( "context" + "crypto/sha256" "fmt" "net/url" "testing" "time" + "github.com/ory/hydra/x" + "github.com/ory/fosite/storage" "github.com/ory/x/sqlxx" @@ -44,6 +47,33 @@ import ( "github.com/ory/hydra/consent" ) +func signatureFromJTI(jti string) string { + return fmt.Sprintf("%x", sha256.Sum256([]byte(jti))) +} + +type blacklistedJTI struct { + JTI string + Signature string `db:"signature"` + Expiry time.Time `db:"expires_at"` +} + +func newBlacklistedJTI(jti string, exp time.Time) *blacklistedJTI { + return &blacklistedJTI{ + JTI: jti, + Signature: signatureFromJTI(jti), + // because the database timestamp types are not as accurate as time.Time we truncate to seconds (which should always work) + Expiry: exp.UTC().Truncate(time.Second), + } +} + +type assertionJWTReader interface { + x.FositeStorer + + getClientAssertionJWT(ctx context.Context, jti string) (*blacklistedJTI, error) + + setClientAssertionJWT(context.Context, *blacklistedJTI) error +} + var defaultRequest = fosite.Request{ ID: "blank", RequestedAt: time.Now().UTC().Round(time.Second), @@ -135,6 +165,8 @@ func TestHelperRunner(t *testing.T, store InternalRegistry, k string) { t.Run(fmt.Sprintf("case=testHelperRevokeRefreshToken/db=%s", k), testHelperRevokeRefreshToken(store)) t.Run(fmt.Sprintf("case=testHelperCreateGetDeletePKCERequestSession/db=%s", k), testHelperCreateGetDeletePKCERequestSession(store)) t.Run(fmt.Sprintf("case=testHelperFlushTokens/db=%s", k), testHelperFlushTokens(store, time.Hour)) + t.Run(fmt.Sprintf("case=testFositeStoreSetClientAssertionJWT/db=%s", k), testFositeStoreSetClientAssertionJWT(store)) + t.Run(fmt.Sprintf("case=testFositeStoreClientAssertionJWTValid/db=%s", k), testFositeStoreClientAssertionJWTValid(store)) } func testHelperUniqueConstraints(m InternalRegistry, storageType string) func(t *testing.T) { @@ -531,6 +563,90 @@ func testFositeSqlStoreTransactionRollbackOpenIdConnectSession(m InternalRegistr } } +func testFositeStoreSetClientAssertionJWT(m InternalRegistry) func(*testing.T) { + return func(t *testing.T) { + t.Run("case=basic setting works", func(t *testing.T) { + store, ok := m.OAuth2Storage().(assertionJWTReader) + require.True(t, ok) + jti := newBlacklistedJTI("basic jti", time.Now().Add(time.Minute)) + + require.NoError(t, store.SetClientAssertionJWT(context.Background(), jti.JTI, jti.Expiry)) + + cmp, err := store.getClientAssertionJWT(context.Background(), jti.JTI) + require.NoError(t, err) + assert.Equal(t, jti, cmp) + }) + + t.Run("case=errors when the JTI is blacklisted", func(t *testing.T) { + store, ok := m.OAuth2Storage().(assertionJWTReader) + require.True(t, ok) + jti := newBlacklistedJTI("already set jti", time.Now().Add(time.Minute)) + require.NoError(t, store.setClientAssertionJWT(context.Background(), jti)) + + assert.True(t, errors.Is(store.SetClientAssertionJWT(context.Background(), jti.JTI, jti.Expiry), fosite.ErrJTIKnown)) + }) + + t.Run("case=deletes expired JTIs", func(t *testing.T) { + store, ok := m.OAuth2Storage().(assertionJWTReader) + require.True(t, ok) + expiredJTI := newBlacklistedJTI("expired jti", time.Now().Add(-time.Minute)) + require.NoError(t, store.setClientAssertionJWT(context.Background(), expiredJTI)) + newJTI := newBlacklistedJTI("some new jti", time.Now().Add(time.Minute)) + + require.NoError(t, store.SetClientAssertionJWT(context.Background(), newJTI.JTI, newJTI.Expiry)) + + _, err := store.getClientAssertionJWT(context.Background(), expiredJTI.JTI) + assert.True(t, errors.Is(err, sqlcon.ErrNoRows)) + cmp, err := store.getClientAssertionJWT(context.Background(), newJTI.JTI) + assert.Equal(t, newJTI, cmp) + }) + + t.Run("case=inserts same JTI if expired", func(t *testing.T) { + store, ok := m.OAuth2Storage().(assertionJWTReader) + require.True(t, ok) + jti := newBlacklistedJTI("going to be reused jti", time.Now().Add(-time.Minute)) + require.NoError(t, store.setClientAssertionJWT(context.Background(), jti)) + + jti.Expiry = jti.Expiry.Add(2 * time.Minute) + assert.NoError(t, store.SetClientAssertionJWT(context.Background(), jti.JTI, jti.Expiry)) + cmp, err := store.getClientAssertionJWT(context.Background(), jti.JTI) + assert.NoError(t, err) + assert.Equal(t, jti, cmp) + }) + } +} + +func testFositeStoreClientAssertionJWTValid(m InternalRegistry) func(*testing.T) { + return func(t *testing.T) { + t.Run("case=returns valid on unknown JTI", func(t *testing.T) { + store, ok := m.OAuth2Storage().(assertionJWTReader) + require.True(t, ok) + + assert.NoError(t, store.ClientAssertionJWTValid(context.Background(), "unknown jti")) + }) + + t.Run("case=returns invalid on known JTI", func(t *testing.T) { + store, ok := m.OAuth2Storage().(assertionJWTReader) + require.True(t, ok) + jti := newBlacklistedJTI("known jti", time.Now().Add(time.Minute)) + + require.NoError(t, store.setClientAssertionJWT(context.Background(), jti)) + + assert.True(t, errors.Is(store.ClientAssertionJWTValid(context.Background(), jti.JTI), fosite.ErrJTIKnown)) + }) + + t.Run("case=returns valid on expired JTI", func(t *testing.T) { + store, ok := m.OAuth2Storage().(assertionJWTReader) + require.True(t, ok) + jti := newBlacklistedJTI("expired jti", time.Now().Add(-time.Minute)) + + require.NoError(t, store.setClientAssertionJWT(context.Background(), jti)) + + assert.NoError(t, store.ClientAssertionJWTValid(context.Background(), jti.JTI)) + }) + } +} + func doTestCommit(m InternalRegistry, t *testing.T, createFn func(context.Context, string, fosite.Requester) error, getFn func(context.Context, string, fosite.Session) (fosite.Requester, error),
oauth2/fosite_store_memory.go+59 −10 modified@@ -34,11 +34,12 @@ import ( ) type FositeMemoryStore struct { - AuthorizeCodes map[string]authorizeCode - IDSessions map[string]fosite.Requester - AccessTokens map[string]fosite.Requester - RefreshTokens map[string]fosite.Requester - PKCES map[string]fosite.Requester + AuthorizeCodes map[string]authorizeCode + IDSessions map[string]fosite.Requester + AccessTokens map[string]fosite.Requester + RefreshTokens map[string]fosite.Requester + PKCES map[string]fosite.Requester + BlacklistedJTIs map[string]time.Time c Configuration r InternalRegistry @@ -53,11 +54,12 @@ func NewFositeMemoryStore( c Configuration, ) *FositeMemoryStore { return &FositeMemoryStore{ - AuthorizeCodes: make(map[string]authorizeCode), - IDSessions: make(map[string]fosite.Requester), - AccessTokens: make(map[string]fosite.Requester), - PKCES: make(map[string]fosite.Requester), - RefreshTokens: make(map[string]fosite.Requester), + AuthorizeCodes: make(map[string]authorizeCode), + IDSessions: make(map[string]fosite.Requester), + AccessTokens: make(map[string]fosite.Requester), + PKCES: make(map[string]fosite.Requester), + RefreshTokens: make(map[string]fosite.Requester), + BlacklistedJTIs: make(map[string]time.Time), c: c, r: r, @@ -73,6 +75,53 @@ func (s *FositeMemoryStore) GetClient(ctx context.Context, id string) (fosite.Cl return s.r.ClientManager().GetClient(ctx, id) } +func (s *FositeMemoryStore) ClientAssertionJWTValid(_ context.Context, jti string) error { + s.RLock() + defer s.RUnlock() + if exp, exists := s.BlacklistedJTIs[jti]; exists && exp.After(time.Now()) { + return errors.WithStack(fosite.ErrJTIKnown) + } + + return nil +} + +func (s *FositeMemoryStore) SetClientAssertionJWT(_ context.Context, jti string, exp time.Time) error { + s.Lock() + defer s.Unlock() + + for j, e := range s.BlacklistedJTIs { + if e.Before(time.Now()) { + delete(s.BlacklistedJTIs, j) + } + } + + if _, exists := s.BlacklistedJTIs[jti]; exists { + return errors.WithStack(fosite.ErrJTIKnown) + } + + s.BlacklistedJTIs[jti] = exp + return nil +} + +func (s *FositeMemoryStore) getClientAssertionJWT(_ context.Context, jti string) (*blacklistedJTI, error) { + s.RLock() + defer s.RUnlock() + + if exp, exists := s.BlacklistedJTIs[jti]; exists { + return newBlacklistedJTI(jti, exp), nil + } + + return nil, errors.WithStack(sqlcon.ErrNoRows) +} + +func (s *FositeMemoryStore) setClientAssertionJWT(_ context.Context, jti *blacklistedJTI) error { + s.Lock() + defer s.Unlock() + + s.BlacklistedJTIs[jti.JTI] = jti.Expiry + return nil +} + func (s *FositeMemoryStore) Authenticate(ctx context.Context, id string, secret []byte) (*client.Client, error) { return s.r.ClientManager().Authenticate(ctx, id, secret) }
oauth2/fosite_store_sql.go+62 −8 modified@@ -31,18 +31,20 @@ import ( "time" "github.com/jmoiron/sqlx" - "github.com/ory/herodot" "github.com/pkg/errors" migrate "github.com/rubenv/sql-migrate" "github.com/sirupsen/logrus" "github.com/tidwall/gjson" + "github.com/ory/herodot" + "github.com/ory/fosite" - "github.com/ory/hydra/client" - "github.com/ory/hydra/jwk" "github.com/ory/x/dbal" "github.com/ory/x/sqlcon" "github.com/ory/x/stringsx" + + "github.com/ory/hydra/client" + "github.com/ory/hydra/jwk" ) type FositeSQLStore struct { @@ -69,11 +71,12 @@ func NewFositeSQLStore(db *sqlx.DB, r InternalRegistry, c Configuration, kc *jwk type tableName string const ( - sqlTableOpenID tableName = "oidc" - sqlTableAccess tableName = "access" - sqlTableRefresh tableName = "refresh" - sqlTableCode tableName = "code" - sqlTablePKCE tableName = "pkce" + sqlTableOpenID tableName = "oidc" + sqlTableAccess tableName = "access" + sqlTableRefresh tableName = "refresh" + sqlTableCode tableName = "code" + sqlTablePKCE tableName = "pkce" + sqlTableBlacklistedJTI tableName = "jti_blacklist" ) var Migrations = map[string]*dbal.PackrMigrationSource{ @@ -228,6 +231,57 @@ func (s *FositeSQLStore) GetClient(ctx context.Context, id string) (fosite.Clien return s.r.ClientManager().GetClient(ctx, id) } +func (s *FositeSQLStore) ClientAssertionJWTValid(ctx context.Context, jti string) error { + d, err := s.getClientAssertionJWT(ctx, jti) + if errors.Is(err, sqlcon.ErrNoRows) { + // the jti is not known => valid + return nil + } else if err != nil { + return err + } + if d.Expiry.After(time.Now()) { + // the jti is not expired yet => invalid + return errors.WithStack(fosite.ErrJTIKnown) + } + // the jti is expired => valid + return nil +} + +func (s *FositeSQLStore) SetClientAssertionJWT(ctx context.Context, j string, exp time.Time) error { + db := s.db(ctx) + + // delete expired + if _, err := db.ExecContext(ctx, fmt.Sprintf("DELETE FROM hydra_oauth2_%s WHERE expires_at < now()", sqlTableBlacklistedJTI)); err != nil { + return sqlcon.HandleError(err) + } + + if err := s.setClientAssertionJWT(ctx, newBlacklistedJTI(j, exp)); errors.Is(err, sqlcon.ErrUniqueViolation) { + // found a jti + return errors.WithStack(fosite.ErrJTIKnown) + } else if err != nil { + return err + } + // setting worked without a problem + return nil +} + +func (s *FositeSQLStore) getClientAssertionJWT(ctx context.Context, j string) (*blacklistedJTI, error) { + sig := signatureFromJTI(j) + jti := blacklistedJTI{ + JTI: j, + } + db := s.db(ctx) + + return &jti, sqlcon.HandleError(db.GetContext(ctx, &jti, db.Rebind(fmt.Sprintf("SELECT * FROM hydra_oauth2_%s WHERE signature=?", sqlTableBlacklistedJTI)), sig)) +} + +func (s *FositeSQLStore) setClientAssertionJWT(ctx context.Context, jti *blacklistedJTI) error { + db := s.db(ctx) + _, err := db.ExecContext(ctx, db.Rebind(fmt.Sprintf("INSERT INTO hydra_oauth2_%s (signature, expires_at) VALUES (?, ?)", sqlTableBlacklistedJTI)), jti.Signature, jti.Expiry) + + return sqlcon.HandleError(err) +} + func (s *FositeSQLStore) Authenticate(ctx context.Context, id string, secret []byte) (*client.Client, error) { return s.r.ClientManager().Authenticate(ctx, id, secret) }
oauth2/handler.go+6 −6 modified@@ -659,12 +659,12 @@ func (h *Handler) AuthHandler(w http.ResponseWriter, r *http.Request, _ httprout authorizeRequest.SetID(session.Challenge) claims := &jwt.IDTokenClaims{ - Subject: session.ConsentRequest.SubjectIdentifier, - Issuer: strings.TrimRight(h.c.IssuerURL().String(), "/") + "/", - IssuedAt: time.Now().UTC(), - AuthTime: time.Time(session.AuthenticatedAt), - RequestedAt: session.RequestedAt, - Extra: session.Session.IDToken, + Subject: session.ConsentRequest.SubjectIdentifier, + Issuer: strings.TrimRight(h.c.IssuerURL().String(), "/") + "/", + IssuedAt: time.Now().UTC(), + AuthTime: time.Time(session.AuthenticatedAt), + RequestedAt: session.RequestedAt, + Extra: session.Session.IDToken, AuthenticationContextClassReference: session.ConsentRequest.ACR, // We do not need to pass the audience because it's included directly by ORY Fosite
oauth2/migrations/sql/cockroach/10.sql+6 −0 added@@ -0,0 +1,6 @@ +-- Empty because CockroachDB creates indices for foreign keys automatically: +-- https://www.cockroachlabs.com/docs/stable/foreign-key.html + +-- +migrate Up + +-- +migrate Down
oauth2/migrations/sql/cockroach/11.sql+10 −0 added@@ -0,0 +1,10 @@ +-- +migrate Up +CREATE TABLE IF NOT EXISTS hydra_oauth2_jti_blacklist ( + signature varchar(64) NOT NULL PRIMARY KEY, + expires_at timestamp NOT NULL DEFAULT now() +); + +CREATE INDEX ON hydra_oauth2_jti_blacklist ( expires_at ); + +-- +migrate Down +DROP TABLE hydra_oauth2_jti_blacklist;
oauth2/migrations/sql/shared/11.sql+11 −0 added@@ -0,0 +1,11 @@ +-- +migrate Up +CREATE TABLE IF NOT EXISTS hydra_oauth2_jti_blacklist ( + signature varchar(64) NOT NULL PRIMARY KEY, + expires_at timestamp NOT NULL DEFAULT now() +); + +-- mysql requires the index to be named +CREATE INDEX hydra_oauth2_jti_blacklist_expiry ON hydra_oauth2_jti_blacklist ( expires_at ); + +-- +migrate Down +DROP TABLE hydra_oauth2_jti_blacklist;
oauth2/migrations/sql/tests/11_test.sql+59 −0 added@@ -0,0 +1,59 @@ +-- +migrate Up +INSERT INTO hydra_client (id, allowed_cors_origins, client_name, client_secret, redirect_uris, grant_types, response_types, scope, owner, policy_uri, tos_uri, client_uri, logo_uri, contacts, client_secret_expires_at, sector_identifier_uri, jwks, jwks_uri, token_endpoint_auth_method, request_uris, request_object_signing_alg, userinfo_signed_response_alg, subject_type, audience, frontchannel_logout_uri, frontchannel_logout_session_required, post_logout_redirect_uris, backchannel_logout_uri, backchannel_logout_session_required, metadata) +VALUES + ('11-client', 'http://localhost|http://google', 'some-client', 'abcdef', 'http://localhost|http://google', 'authorize_code|implicit', 'token|id_token', 'foo|bar', 'aeneas', 'http://policy', 'http://tos', 'http://client', 'http://logo', 'aeneas|foo', 0, 'http://sector', '{"keys": []}', 'http://jwks', 'none', 'http://uri1|http://uri2', 'rs256', 'rs526', 'public', 'https://www.ory.sh/api', 'http://fc-logout/', true, 'http://redir1/|http://redir2/', 'http://bc-logout/', true, '{"foo":"bar"}'); + +INSERT INTO + hydra_oauth2_authentication_session (id, authenticated_at, subject) +VALUES + ('11-login-session-id', NOW(), '11-sub'); + +INSERT INTO + hydra_oauth2_authentication_request (challenge, verifier, client_id, subject, request_url, skip, requested_scope, csrf, authenticated_at, requested_at, oidc_context, login_session_id, requested_at_audience) +VALUES + ('11-challenge', '11-verifier', '11-client', '11-subject', '11-redirect', false, '11-scope', '11-csrf', NOW(), NOW(), '{}', '11-login-session-id', '11-aud'); + +INSERT INTO + hydra_oauth2_consent_request (challenge, verifier, client_id, subject, request_url, skip, requested_scope, csrf, authenticated_at, requested_at, oidc_context, forced_subject_identifier, login_session_id, login_challenge, requested_at_audience, acr, context) +VALUES + ('11-challenge', '11-verifier', '11-client', '11-subject', '11-redirect', false, '11-scope', '11-csrf', NOW(), NOW(), '{}', '11-forced-sub', '11-login-session-id', '11-challenge', '11-aud', '11-acr', '{}'); + +INSERT INTO + hydra_oauth2_consent_request_handled (challenge, granted_scope, remember, remember_for, error, requested_at, session_access_token, session_id_token, authenticated_at, was_used, granted_at_audience) +VALUES + ('11-challenge', '11-scope', true, 3600, '{}', NOW(), '{}', '{}', NOW(), false, '11-aud'); + +-- The previous block is just to get foreign keys working + +INSERT INTO + hydra_oauth2_access (signature, request_id, requested_at, client_id, scope, granted_scope, form_data, session_data, subject, active, requested_audience, granted_audience, challenge_id) +VALUES + ('11-sig', '11-request', NOW(), '11-client', '11-scope', '11-granted-scope', '', '{}', '11-subject', true, '11-challengeed-aud', '11-granted-aud', '11-challenge'); + +INSERT INTO + hydra_oauth2_refresh (signature, request_id, requested_at, client_id, scope, granted_scope, form_data, session_data, subject, active, requested_audience, granted_audience, challenge_id) +VALUES + ('11-sig', '11-request', NOW(), '11-client', '11-scope', '11-granted-scope', '', '{}', '11-subject', true, '11-challengeed-aud', '11-granted-aud', '11-challenge'); + +INSERT INTO + hydra_oauth2_code (signature, request_id, requested_at, client_id, scope, granted_scope, form_data, session_data, subject, active, requested_audience, granted_audience, challenge_id) +VALUES + ('11-sig', '11-request', NOW(), '11-client', '11-scope', '11-granted-scope', '', '{}', '11-subject', true, '11-challengeed-aud', '11-granted-aud', '11-challenge'); + +INSERT INTO + hydra_oauth2_oidc (signature, request_id, requested_at, client_id, scope, granted_scope, form_data, session_data, subject, active, requested_audience, granted_audience, challenge_id) +VALUES + ('11-sig', '11-request', NOW(), '11-client', '11-scope', '11-granted-scope', '', '{}', '11-subject', true, '11-challengeed-aud', '11-granted-aud', '11-challenge'); + +INSERT INTO + hydra_oauth2_pkce (signature, request_id, requested_at, client_id, scope, granted_scope, form_data, session_data, subject, active, requested_audience, granted_audience, challenge_id) +VALUES + ('11-sig', '11-request', NOW(), '11-client', '11-scope', '11-granted-scope', '', '{}', '11-subject', true, '11-challengeed-aud', '11-granted-aud', '11-challenge'); + + -- 11-sig + INSERT INTO + hydra_oauth2_jti_blacklist (signature, expires_at) + VALUES + ('c5b4a7dc4798eb3047523b0db7a899958d64f92a8b24ef38057539ef32763abc', '2038-01-19 01:00:00'); + +-- +migrate Down
oauth2/oauth2_auth_code_test.go+6 −5 modified@@ -38,6 +38,12 @@ import ( djwt "github.com/dgrijalva/jwt-go" "github.com/jmoiron/sqlx" "github.com/julienschmidt/httprouter" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/oauth2" + "golang.org/x/oauth2/clientcredentials" + "github.com/ory/fosite" "github.com/ory/fosite/token/jwt" hc "github.com/ory/hydra/client" @@ -52,11 +58,6 @@ import ( "github.com/ory/x/pointerx" "github.com/ory/x/sqlcon/dockertest" "github.com/ory/x/urlx" - "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "golang.org/x/oauth2" - "golang.org/x/oauth2/clientcredentials" ) func newCookieJar() http.CookieJar {
oauth2/oauth2_refresh_token_test.go+4 −3 modified@@ -11,6 +11,10 @@ import ( "time" "github.com/jmoiron/sqlx" + "github.com/pborman/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/ory/fosite" hc "github.com/ory/hydra/client" "github.com/ory/hydra/driver" @@ -19,9 +23,6 @@ import ( "github.com/ory/x/dbal" "github.com/ory/x/errorsx" "github.com/ory/x/sqlcon/dockertest" - "github.com/pborman/uuid" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) // TestCreateRefreshTokenSessionStress is a sanity test to verify the fix for https://github.com/ory/hydra/issues/1719 &
oauth2/sql_migration_files.go+98 −6 modified@@ -1,5 +1,7 @@ // Code generated for package oauth2 by go-bindata DO NOT EDIT. (@generated) // sources: +// migrations/sql/cockroach/10.sql +// migrations/sql/cockroach/11.sql // migrations/sql/cockroach/9.sql // migrations/sql/mysql/.gitkeep // migrations/sql/mysql/10.sql @@ -14,12 +16,14 @@ // migrations/sql/postgres/7.sql // migrations/sql/postgres/9.sql // migrations/sql/shared/1.sql +// migrations/sql/shared/11.sql // migrations/sql/shared/2.sql // migrations/sql/shared/3.sql // migrations/sql/shared/4.sql // migrations/sql/shared/8.sql // migrations/sql/tests/.gitkeep // migrations/sql/tests/10_test.sql +// migrations/sql/tests/11_test.sql // migrations/sql/tests/1_test.sql // migrations/sql/tests/2_test.sql // migrations/sql/tests/3_test.sql @@ -105,6 +109,46 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } +var _migrationsSqlCockroach10Sql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\xcc\x31\x0e\x83\x30\x0c\x85\xe1\x9d\x53\x78\xaf\x42\x76\xc6\x96\x1e\xa1\x07\x30\xc6\x85\x88\x24\x8e\x62\xa3\x28\xb7\xaf\x90\xda\xa1\xc3\x1b\xde\xf0\x7f\xce\xc1\x33\x15\xeb\xb0\x30\xe1\xa9\x0c\x0f\xa1\xa3\x0a\xd2\x3e\xdf\x81\x2a\xa3\xb1\x42\xc8\x6b\x20\x56\x78\x4b\xbd\xc6\x61\xcb\x70\x70\x57\xc0\xd3\x24\xa1\x05\xc2\x18\xfb\x34\x38\x07\xbb\x59\xd1\xc9\xfb\xd6\xda\x48\x3f\x2a\xe2\xa2\x23\x49\xf2\xab\x90\x7a\x35\x5c\x22\xfb\x2f\xe4\x0e\xee\xe3\x6e\x29\x0e\x57\x7e\x4b\x61\xab\x68\x0c\xaf\xf2\xff\x67\x69\x79\xf8\x04\x00\x00\xff\xff\xb3\xf4\x91\x1c\xad\x00\x00\x00") + +func migrationsSqlCockroach10SqlBytes() ([]byte, error) { + return bindataRead( + _migrationsSqlCockroach10Sql, + "migrations/sql/cockroach/10.sql", + ) +} + +func migrationsSqlCockroach10Sql() (*asset, error) { + bytes, err := migrationsSqlCockroach10SqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "migrations/sql/cockroach/10.sql", size: 173, mode: os.FileMode(420), modTime: time.Unix(1585815362, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _migrationsSqlCockroach11Sql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\x8f\xbd\x4e\xc3\x30\x14\x46\xe7\xf8\x29\xbe\x31\x11\x74\x41\x88\xa5\x53\x20\xae\x14\x61\x9c\xca\x75\xa4\x76\xb2\x2e\xc5\x6a\x0c\xcd\x8f\xec\x5b\x0a\x6f\x8f\xa8\x40\x74\xa1\x77\xbe\xdf\xd1\x39\xb3\x19\xae\xfa\xb0\x8b\xc4\x1e\xed\x24\x1e\x8c\x2c\xad\x84\x2d\xef\x95\x44\xbd\x80\x6e\x2c\xe4\xba\x5e\xd9\x15\xba\xcf\x97\x48\x6e\xa4\x03\x77\x37\xee\x95\x83\x7b\xde\xd3\xf6\x6d\x1f\x12\x23\x17\x59\x0a\xbb\x81\xf8\x10\x3d\x4e\x97\xbd\x53\xdc\x76\x14\xf3\xbb\xdb\xe2\x04\xd1\xad\x52\x58\x9a\xfa\xa9\x34\x1b\x3c\xca\xcd\xb5\xc8\xfc\xc7\x14\xa2\x4f\x8e\x18\xc8\x38\xf4\x3e\x31\xf5\xd3\xdf\x77\x25\x17\x65\xab\x2c\x86\xf1\x98\x17\xa2\x98\x8b\x5f\xbb\x5a\x57\x72\x8d\x46\x5f\x54\xc2\x19\xfe\x7b\x7b\x1e\x5a\x8d\xc7\x41\x54\xa6\x59\xfe\x84\xfe\xcf\x99\x8b\xaf\x00\x00\x00\xff\xff\x52\x42\x55\x41\x21\x01\x00\x00") + +func migrationsSqlCockroach11SqlBytes() ([]byte, error) { + return bindataRead( + _migrationsSqlCockroach11Sql, + "migrations/sql/cockroach/11.sql", + ) +} + +func migrationsSqlCockroach11Sql() (*asset, error) { + bytes, err := migrationsSqlCockroach11SqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "migrations/sql/cockroach/11.sql", size: 289, mode: os.FileMode(420), modTime: time.Unix(1585817202, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + var _migrationsSqlCockroach9Sql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x98\x41\x6f\xda\x30\x14\xc7\xcf\xf8\x53\xbc\x5b\x41\xa3\xd2\x54\xad\x27\x4e\x19\x79\x4c\xd1\xb2\xd0\x05\x47\x6a\x4f\x91\xeb\x3c\x48\x56\x48\x58\x6c\xda\xed\xdb\x4f\xa6\x04\x82\x42\x28\x9b\x80\xa1\x2d\x57\xbf\x7f\xe2\x9f\xed\xdf\x33\x11\xd7\xd7\xf0\x6e\x96\x4c\x72\xa1\x09\x82\x39\xeb\xfb\x68\x71\x04\x6e\x7d\x74\x11\x9c\x01\x78\x43\x0e\x78\xef\x8c\xf8\x08\xe2\x9f\x51\x2e\xc2\x4c\x2c\x74\x7c\x13\x0a\x29\x49\x29\x68\xb3\x96\x4a\x26\xa9\xd0\x8b\x9c\xe0\x59\xe4\x32\x16\x79\xfb\xe6\xf6\xb6\xb3\x7c\xd0\x0b\x5c\x17\xee\x7c\xe7\x8b\xe5\x3f\xc0\x67\x7c\xe8\xb2\x56\x4e\xdf\x17\xa4\x74\x98\x44\xeb\xf8\x87\xf7\x9b\xf4\x26\x41\x51\x28\x34\xe8\x64\x46\x4a\x8b\xd9\x7c\xf3\x3e\x1b\x07\x56\xe0\x72\x48\xb3\x97\x76\xa7\xcb\x5a\x72\x9a\x50\xba\xf5\xc2\xad\xf9\xbb\xac\xa5\x64\x36\x27\xd0\xf4\x43\x97\x47\x27\xb9\x48\xcd\x2c\xbb\xab\xe3\x2c\x9f\x85\x91\xd0\xa2\x52\x51\xa4\x54\x92\xa5\x35\xc5\xc5\xe3\x37\x92\xba\x66\x2b\x0a\xf4\xab\xab\x2e\x6b\x09\xa9\x93\x67\x82\xc7\x2c\x9b\x56\x13\xdc\x0f\x70\x7b\x2f\x16\x51\x42\xa9\x2c\x40\x2b\x6f\x2b\x56\xf3\x56\x4e\xc6\x62\x3a\xa5\x74\x42\x95\x13\x78\x5d\x40\xe0\x39\x5f\x03\x84\xf6\xe6\x9c\xcc\x1e\x3b\x9e\x8d\xf7\xeb\xc1\xe5\xd1\x94\x86\xd7\x27\x50\x1e\x2b\xcd\xd3\x61\x9d\xde\xc1\x62\xe5\x34\xce\x49\xc5\x8d\x59\xff\x95\x59\x47\x55\x48\x66\x11\x35\xfe\xfc\x6b\xfe\x9c\xc2\x94\x2c\x89\x64\x63\x4a\x63\xca\xdb\xa6\xcc\x9f\x64\x73\xa7\xfc\x96\x29\x7f\xae\x07\xc7\xfb\x5d\xb2\x55\xf4\xa8\xc9\x1d\x53\x0f\x66\xb9\x1c\xfd\x95\x1e\xbb\x3e\x80\x2d\xdb\x86\xfe\xd0\x1b\x71\xdf\x72\x3c\xbe\x2b\x12\xae\x27\x0a\xc7\x4f\x30\x18\xfa\xe8\x7c\xf2\x8c\x1f\x65\x04\xf0\x71\x80\x3e\x7a\x7d\x2c\xbc\x7b\xad\xb5\x4d\x6d\xe8\x81\x8d\x2e\x72\x84\xbe\x35\xea\x5b\x36\xf6\xea\xb1\x8a\xcf\xa7\x7d\x5c\xab\xcc\x79\xc1\x96\x3f\xca\xfb\xa8\x4c\xe0\xbc\x48\xcb\xdb\x7f\x1f\x92\x09\x9c\x17\x69\x79\xcd\xec\x43\x32\x81\x93\x20\x1d\xc5\xf4\x52\xfb\x54\xd1\xca\xbd\x55\xa5\x5b\x5b\x90\x2a\xb3\x82\xe2\xe6\x8c\x45\x1a\x4d\x29\xda\x3c\x7d\xc2\x76\xb8\x38\xfa\x03\x7b\xe6\xe2\xb8\x0f\x6c\xac\x8b\xe3\x3e\xb0\xfb\xfe\x16\x37\x2b\xff\x57\x63\x67\x2f\x29\xb3\xfd\xe1\x5d\x7d\xcb\xf6\x6a\xeb\x2b\xe5\xeb\x03\x46\xac\xfa\xaa\x39\xbe\xfa\xaa\xd9\xa4\x1e\xfb\x15\x00\x00\xff\xff\x15\x33\x61\x06\x58\x12\x00\x00") func migrationsSqlCockroach9SqlBytes() ([]byte, error) { @@ -385,6 +429,26 @@ func migrationsSqlShared1Sql() (*asset, error) { return a, nil } +var _migrationsSqlShared11Sql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\x90\xcd\x4e\x83\x50\x10\x85\xd7\xbd\x4f\x71\x96\x10\x65\x63\x8c\x9b\xae\x50\x6e\x13\x22\x42\x43\x21\x69\x57\x64\x5a\x26\xe5\x2a\x7f\xbd\x0c\xb6\xbc\xbd\xb1\xd6\x9f\x8d\x9d\xf5\x9c\x93\xef\x7c\x9e\x87\x9b\xc6\xec\x2d\x09\x23\xef\xd5\x53\xaa\xfd\x4c\x23\xf3\x1f\x23\x8d\x70\x81\x38\xc9\xa0\xd7\xe1\x2a\x5b\xa1\x9a\x4a\x4b\x45\x47\xa3\x54\x77\xc5\xab\x98\x62\x5b\xd3\xee\xad\x36\x83\xc0\x51\xb3\xc1\xec\x5b\x92\xd1\x32\xbe\xee\x9d\xec\xae\x22\xeb\x3c\xdc\xbb\xe7\x92\x38\x8f\x22\x2c\xd3\xf0\xc5\x4f\x37\x78\xd6\x9b\x5b\x35\xe3\x53\x6f\x2c\x0f\x05\x09\x30\x13\xd3\xf0\x20\xd4\xf4\xbf\xdf\x81\x5e\xf8\x79\x94\xa1\xed\x8e\x8e\xab\xdc\xb9\x52\x9e\x87\x66\x1a\x0e\x35\x2c\x1f\xc6\xcf\x2c\xa4\x62\x98\xb6\xe4\x13\xa4\xc3\x96\xd1\x52\xc3\xe5\xf7\x8a\x30\x0e\xf4\xfa\x0a\x77\x71\x26\x98\x90\xc4\x57\xc7\xe1\x0f\xe8\x85\xe2\x47\x59\xd0\x1d\x5b\x15\xa4\xc9\xf2\xa2\xec\xff\x9e\xb9\xfa\x08\x00\x00\xff\xff\x96\x66\xfa\x03\x6b\x01\x00\x00") + +func migrationsSqlShared11SqlBytes() ([]byte, error) { + return bindataRead( + _migrationsSqlShared11Sql, + "migrations/sql/shared/11.sql", + ) +} + +func migrationsSqlShared11Sql() (*asset, error) { + bytes, err := migrationsSqlShared11SqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "migrations/sql/shared/11.sql", size: 363, mode: os.FileMode(420), modTime: time.Unix(1585817202, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + var _migrationsSqlShared2Sql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x90\xb1\x0a\xc2\x30\x14\x45\xf7\x7e\xc5\xdd\xaa\x48\x97\x42\x27\xa7\x68\xea\x14\x5b\x29\xc9\x5c\x62\x1a\x4d\x05\x8d\xbc\xb4\x8a\x7f\x2f\x14\x04\x07\x45\x0b\xfd\x80\x73\x2e\xf7\x24\x09\x16\xe7\xf6\x48\xba\xb3\x50\xd7\x88\x09\x99\x57\x90\x6c\x25\x72\xb8\x47\x43\xba\xf6\xba\xef\x5c\x5a\x6b\x63\x6c\x08\x60\x9c\x23\xf4\xfb\x93\x35\x1d\x6e\x9a\x8c\xd3\x34\x4b\xb3\x6c\x8e\xa2\x94\x28\x94\x10\xe0\xf9\x86\x29\x21\x11\xc7\xcb\xef\x36\xb2\x07\xb2\xc1\x4d\xa5\x33\xbe\xb1\x53\xb9\x7c\xdb\x98\x91\xae\xe8\x3d\x22\xf7\xf7\xcb\xcf\x8c\xe0\x55\xb9\xc3\xba\x14\x6a\x5b\xbc\x86\xfe\xc8\x35\x8e\x1a\xaa\x8c\x43\x86\xf3\x1f\x91\x67\x00\x00\x00\xff\xff\xa3\x05\x9b\x27\x28\x02\x00\x00") func migrationsSqlShared2SqlBytes() ([]byte, error) { @@ -505,6 +569,26 @@ func migrationsSqlTests10_testSql() (*asset, error) { return a, nil } +var _migrationsSqlTests11_testSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x58\x4d\x6f\xe3\x36\x13\x3e\xaf\x7f\xc5\xc0\x17\x27\x78\xa5\xb5\x6c\xc7\xf1\xc7\x7b\x2a\xd0\x3d\x2c\x50\x64\x81\x6e\xb6\x3d\x14\x05\x41\x91\x23\x99\xb1\xcc\x51\x49\x2a\x4e\x1a\xe7\xbf\x17\xd4\xb7\xbd\x46\x76\x0b\x14\x69\x0f\xc9\x21\x20\x67\x34\x24\xe7\x79\x9e\x19\x4a\x0e\x43\xf8\xdf\x4e\xa5\x86\x3b\x84\x2f\xf9\xe0\xe3\xcd\xe7\x0f\x3f\xdf\xc2\xc7\x9b\xdb\x4f\xb0\x79\x94\x86\x33\x91\x29\xd4\x0e\x2e\x94\x0c\x80\x67\x19\xed\x51\x32\x41\xc6\x32\x32\x2a\x55\xda\x06\x50\x3d\xc1\x34\xdf\x61\x3b\xb1\x28\x0c\xba\x00\x0c\x4a\x65\x50\x38\x56\x18\x65\x03\x48\x0d\xd7\x8e\xb9\xc7\x1c\xad\xf7\xd9\x9c\xb4\xc5\x66\x6e\x05\xe5\x18\x00\xed\x35\x9a\x00\x72\xca\x94\x78\xf4\x71\x01\x38\xb2\xd5\xa0\x5e\xbd\x1c\x67\x94\x52\x6d\x25\xed\xb8\x70\xf6\x64\x77\x86\x0f\xb9\x32\x68\x19\x77\x01\x58\x14\x8e\x0c\x53\x12\xb5\x53\x89\x42\x53\x85\xde\xed\xb7\xb6\xfa\xdf\xec\xb4\x45\xcd\x50\xcb\x9c\x94\x76\x8c\x17\x6e\xc3\x76\xe8\x36\x24\xfd\x79\xff\x28\xd0\x36\xa9\x34\x33\x8a\xef\x7c\x7e\x56\xa5\x5a\xe9\x94\xf1\x2c\x0d\xa0\xb0\x68\x94\x4e\xa8\xb4\xa2\x64\x6d\xa6\xa5\xd7\x16\x55\x88\x4f\x3b\x00\x5e\x48\x85\x5a\x60\x00\x89\x21\xed\xc4\x86\x6b\x8d\x19\xf3\xd9\x15\x75\xa6\xe7\x1c\x16\xad\x55\xa4\x99\x3f\x86\x32\x28\x3d\x60\xd6\x35\xde\x13\xd8\x63\x2e\xb6\xe7\x16\x3e\x63\xff\x7a\xdd\x1d\x3a\x2e\xb9\xe3\x97\x83\x5f\x7e\xf8\xe9\xcb\x87\xcf\x03\x80\x8b\xd1\x64\x12\x56\x58\x8f\x02\x18\x6d\x9c\xcb\xd7\xe3\x71\x46\x82\x67\x1b\xb2\xee\x50\x1b\x52\xa2\x34\x43\xff\x84\xa5\x1d\xf6\x02\x78\x2c\x24\x26\xdf\x17\xea\x29\x20\xa3\xfe\x44\x26\x48\xe2\x41\xed\xf2\x4c\x09\x55\x2e\x53\x92\x75\x50\x92\x95\x03\x6f\x49\x88\x0e\x31\x37\x65\x1c\x6a\xe4\xb6\xb7\x47\x25\xa8\x9e\xc1\x51\xdf\x7d\x26\x9d\x94\xba\x85\x0e\x09\xf9\x59\xd4\xf9\x2b\x45\xf9\x27\x9e\x86\x5b\x7c\xb4\xc3\x35\xfc\xf6\xfb\x73\x6f\x01\xaf\x2a\x3f\xd5\xa4\xb1\x67\x2e\x8c\x9a\x1c\xba\xf1\xd4\xbb\x8c\x9d\xce\xaf\xab\xc1\x7c\x5a\x0e\xf2\x22\xce\x94\x68\xc2\xec\x7a\x3c\xde\xef\xf7\xef\xc9\x3c\xbe\xb7\x9b\x31\xcf\x55\x6f\xc1\x44\x84\x15\x79\xe3\x51\x00\xce\x14\xd8\xb9\x4a\x1d\x4c\xc6\x87\xfe\x74\x3a\xee\xc5\xc6\x67\x62\x9f\x86\x09\xd1\x70\x3d\x8c\xb9\x19\x3e\x8f\x2e\xff\x3f\xe8\x77\x85\xc1\xbb\xaa\x2d\x90\xe7\x65\x5a\x16\x88\xaf\x28\xc1\x9d\x57\x4d\xad\x9e\xba\x5b\x74\x3e\x94\x55\x11\x56\xca\x6f\x95\xf4\xae\x14\x52\x46\xa9\xd2\x61\x1d\x1a\x2a\x39\x0a\xe0\xe6\xd3\xaf\x17\x97\x01\x78\xaf\x2d\xe2\xbf\x77\x86\xba\x30\xe1\x42\x6c\x78\x96\xa1\x4e\x31\x80\x7b\x34\x65\xd1\xb7\x1d\xc2\x9f\xaf\x3e\x4d\xbf\xb0\xb3\x00\xec\x56\xe5\xad\x09\x25\xab\xbb\x92\xb0\x26\x39\x97\x52\xf7\xa0\x9f\x91\x92\x82\xf9\x76\x84\x0f\xae\xec\x50\xaa\xc5\xa4\xdc\xb2\xff\x34\x6b\x2a\xff\x04\x8e\xf6\xd8\xa3\x0a\x80\xe6\xec\xf5\xb4\xd3\x69\x05\x8e\x4f\xa1\x9e\x35\x55\x3f\x0a\x20\xe1\x99\xc5\xfa\x19\x9f\x40\x13\x6c\x4d\xd2\xc1\xdb\xa0\xfc\xf4\x5c\xbb\xcf\x30\xe1\xcd\xbc\x90\xdf\xa0\x40\xf8\xee\xa6\xdd\x7f\x08\xfb\x84\x8c\xf0\x2b\xd4\xbd\xb6\xeb\xfb\xe7\x68\xa9\x2c\xbd\x33\x9f\xe5\x29\x00\x2e\x4c\x75\xdb\xe0\xc3\xa9\x88\xff\x45\xd6\xaa\x54\xcb\x42\x79\x91\xc6\xd3\x23\x7a\x5a\xeb\x91\xa8\xfa\xd8\xb7\xaa\xfd\x84\x66\xb6\xe1\x5a\x66\x28\x8f\xe8\x2e\xaf\xf8\x8e\x3b\x83\x3b\xdc\xc5\x1e\xf6\x66\xc4\x12\x32\x01\xa0\x31\x64\x4e\x29\x6c\x38\xe1\x42\xa0\xb5\x55\x5b\xef\xac\x4d\xa3\x3f\x27\x85\x3d\xb7\xac\xb0\xfe\xba\x6a\xf6\xff\xfe\x02\x6b\xa0\xae\xda\xdf\xec\x3a\x8a\x1a\x74\x8f\xa1\xee\x9b\x7a\x4c\x35\xd5\x11\x86\x70\xbb\x41\xc8\x0d\xde\x2b\x2a\x2c\xc4\x19\x89\x2d\x28\x0b\x77\x85\x75\xe0\x08\x52\x74\x5e\x96\xa8\x52\x0d\xfe\xc2\x80\x3d\x99\xad\xd2\xe9\x8b\xbd\xad\x44\x02\x2e\xfc\x8b\x04\x77\x85\xe9\xb4\xf9\x55\x3f\x39\xae\xaf\x0a\xfd\x13\x32\x12\x32\x3b\xe6\xef\xf2\x0e\xd4\x7a\xd6\x94\x23\x17\x4e\xdd\x1f\x17\x40\xab\xfe\x16\xd9\xd6\xd2\x22\xc9\x94\x3c\x01\xd9\xaa\xb4\xd5\x77\xb9\xd4\x71\x57\x3f\xae\x87\x9e\xd6\xeb\x4d\x3a\xdb\xa8\x2f\xf5\xae\x72\xea\xbb\xaa\x4f\x27\xca\x9e\xa4\x9b\x75\x3a\x4b\x47\xfb\xcb\x22\x37\x98\x18\xb4\x9b\x37\xcc\x5f\x11\x73\xff\x6a\xf7\x06\xf8\x2b\x02\xee\x2f\xcb\x37\xc0\x5f\x11\xf0\x7c\x2b\xde\x14\xfe\xcf\x03\x0e\x61\x08\xd5\x31\x07\xd0\x07\x1f\xfc\xdf\x11\x01\x77\x4e\xb1\x38\xe3\x62\x9b\x29\xff\x82\xda\x63\xa2\xfb\x99\xe0\x72\x00\xed\x37\x2e\xf8\xcf\x5c\x31\x8f\xaf\xf8\x42\x8a\xab\xc5\x6a\x89\xf1\x2c\xba\x5a\xcc\xa7\xb3\x38\x92\xf1\x82\x2f\x57\xab\xd5\x7c\x29\xaf\xaf\x92\xd5\x94\x2f\xe3\xe9\x15\x26\xb3\x65\x34\x5f\xcc\x67\x2b\x4c\x66\xd3\xc5\xf5\x8c\xc7\xe5\xf7\xdb\x34\x9a\x2d\xc3\x68\x12\x4e\x56\x10\x4d\xd6\x51\xb4\x8e\xa2\xe6\x7d\xa1\xfd\xe5\xe5\x47\xda\xeb\xc1\x5f\x01\x00\x00\xff\xff\x20\xb9\x94\xb5\x8b\x11\x00\x00") + +func migrationsSqlTests11_testSqlBytes() ([]byte, error) { + return bindataRead( + _migrationsSqlTests11_testSql, + "migrations/sql/tests/11_test.sql", + ) +} + +func migrationsSqlTests11_testSql() (*asset, error) { + bytes, err := migrationsSqlTests11_testSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "migrations/sql/tests/11_test.sql", size: 4491, mode: os.FileMode(420), modTime: time.Unix(1585818782, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + var _migrationsSqlTests1_testSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xdc\x93\x41\x4b\x03\x31\x10\x85\xcf\xcd\xaf\x98\x5b\x76\x31\x7b\xa8\x57\x4f\x82\x3d\x14\x64\x0b\xb6\xd5\x63\x18\x92\x69\x36\x60\x93\x3a\x93\x45\x44\xfc\xef\xd2\xcd\xa2\x9e\xbc\xb7\x87\xc0\xbc\xf7\xe0\x3d\xbe\x43\xba\x0e\x6e\x8e\x31\x30\x16\x82\xfd\x49\xad\xfb\xed\xea\x69\x07\xeb\x7e\xb7\x51\x8b\xe1\xc3\x33\xda\x8c\x63\x19\x6e\x2d\x3a\x47\x22\xd0\x48\x0c\x09\xcb\xc8\x64\x80\xe9\x6d\x24\x29\x36\xfa\x9f\x9b\xbc\xc5\x62\xc0\xbd\x46\x4a\x35\x10\x97\x4f\x64\x20\x30\xa6\x73\x3a\xcb\x43\xe6\xa3\xf5\x58\xd0\x80\x90\x48\xcc\x69\x52\xad\x7a\xbe\x7f\xdc\xaf\xb6\x6a\xd1\xe8\x65\x27\x31\x68\x03\x7a\xd9\xcd\xe5\xda\x40\xbf\x79\x69\xda\xc9\xab\x13\x35\x9f\x4a\xeb\x39\xef\xfc\x5a\xe7\xf7\xf9\xa5\xdb\x3b\xf5\x0f\x1c\xd3\x81\x49\x86\x2b\xa5\x73\xd9\xd3\x95\xa2\xe5\xe8\xdd\x45\xa3\xfd\xfd\x7f\x0f\xf9\x3d\xa9\xef\x00\x00\x00\xff\xff\x72\x0a\x2b\x58\x91\x03\x00\x00") func migrationsSqlTests1_testSqlBytes() ([]byte, error) { @@ -737,6 +821,8 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ + "migrations/sql/cockroach/10.sql": migrationsSqlCockroach10Sql, + "migrations/sql/cockroach/11.sql": migrationsSqlCockroach11Sql, "migrations/sql/cockroach/9.sql": migrationsSqlCockroach9Sql, "migrations/sql/mysql/.gitkeep": migrationsSqlMysqlGitkeep, "migrations/sql/mysql/10.sql": migrationsSqlMysql10Sql, @@ -751,12 +837,14 @@ var _bindata = map[string]func() (*asset, error){ "migrations/sql/postgres/7.sql": migrationsSqlPostgres7Sql, "migrations/sql/postgres/9.sql": migrationsSqlPostgres9Sql, "migrations/sql/shared/1.sql": migrationsSqlShared1Sql, + "migrations/sql/shared/11.sql": migrationsSqlShared11Sql, "migrations/sql/shared/2.sql": migrationsSqlShared2Sql, "migrations/sql/shared/3.sql": migrationsSqlShared3Sql, "migrations/sql/shared/4.sql": migrationsSqlShared4Sql, "migrations/sql/shared/8.sql": migrationsSqlShared8Sql, "migrations/sql/tests/.gitkeep": migrationsSqlTestsGitkeep, "migrations/sql/tests/10_test.sql": migrationsSqlTests10_testSql, + "migrations/sql/tests/11_test.sql": migrationsSqlTests11_testSql, "migrations/sql/tests/1_test.sql": migrationsSqlTests1_testSql, "migrations/sql/tests/2_test.sql": migrationsSqlTests2_testSql, "migrations/sql/tests/3_test.sql": migrationsSqlTests3_testSql, @@ -812,7 +900,9 @@ var _bintree = &bintree{nil, map[string]*bintree{ "migrations": &bintree{nil, map[string]*bintree{ "sql": &bintree{nil, map[string]*bintree{ "cockroach": &bintree{nil, map[string]*bintree{ - "9.sql": &bintree{migrationsSqlCockroach9Sql, map[string]*bintree{}}, + "10.sql": &bintree{migrationsSqlCockroach10Sql, map[string]*bintree{}}, + "11.sql": &bintree{migrationsSqlCockroach11Sql, map[string]*bintree{}}, + "9.sql": &bintree{migrationsSqlCockroach9Sql, map[string]*bintree{}}, }}, "mysql": &bintree{nil, map[string]*bintree{ ".gitkeep": &bintree{migrationsSqlMysqlGitkeep, map[string]*bintree{}}, @@ -831,15 +921,17 @@ var _bintree = &bintree{nil, map[string]*bintree{ "9.sql": &bintree{migrationsSqlPostgres9Sql, map[string]*bintree{}}, }}, "shared": &bintree{nil, map[string]*bintree{ - "1.sql": &bintree{migrationsSqlShared1Sql, map[string]*bintree{}}, - "2.sql": &bintree{migrationsSqlShared2Sql, map[string]*bintree{}}, - "3.sql": &bintree{migrationsSqlShared3Sql, map[string]*bintree{}}, - "4.sql": &bintree{migrationsSqlShared4Sql, map[string]*bintree{}}, - "8.sql": &bintree{migrationsSqlShared8Sql, map[string]*bintree{}}, + "1.sql": &bintree{migrationsSqlShared1Sql, map[string]*bintree{}}, + "11.sql": &bintree{migrationsSqlShared11Sql, map[string]*bintree{}}, + "2.sql": &bintree{migrationsSqlShared2Sql, map[string]*bintree{}}, + "3.sql": &bintree{migrationsSqlShared3Sql, map[string]*bintree{}}, + "4.sql": &bintree{migrationsSqlShared4Sql, map[string]*bintree{}}, + "8.sql": &bintree{migrationsSqlShared8Sql, map[string]*bintree{}}, }}, "tests": &bintree{nil, map[string]*bintree{ ".gitkeep": &bintree{migrationsSqlTestsGitkeep, map[string]*bintree{}}, "10_test.sql": &bintree{migrationsSqlTests10_testSql, map[string]*bintree{}}, + "11_test.sql": &bintree{migrationsSqlTests11_testSql, map[string]*bintree{}}, "1_test.sql": &bintree{migrationsSqlTests1_testSql, map[string]*bintree{}}, "2_test.sql": &bintree{migrationsSqlTests2_testSql, map[string]*bintree{}}, "3_test.sql": &bintree{migrationsSqlTests3_testSql, map[string]*bintree{}},
oauth2/x_fosite_migrations_test.go+9 −2 modified@@ -6,16 +6,18 @@ import ( "testing" "github.com/jmoiron/sqlx" + "github.com/pkg/errors" "github.com/stretchr/testify/require" "github.com/ory/fosite" + "github.com/ory/x/dbal" + "github.com/ory/x/dbal/migratest" + "github.com/ory/hydra/client" "github.com/ory/hydra/consent" "github.com/ory/hydra/internal" "github.com/ory/hydra/oauth2" "github.com/ory/hydra/x" - "github.com/ory/x/dbal" - "github.com/ory/x/dbal/migratest" ) func TestXXMigrations(t *testing.T) { @@ -52,6 +54,7 @@ func TestXXMigrations(t *testing.T) { require.Error(t, err) return } + _, err := s.GetAccessTokenSession(context.Background(), sig, oauth2.NewSession("")) require.NoError(t, err) _, err = s.GetRefreshTokenSession(context.Background(), sig, oauth2.NewSession("")) @@ -64,6 +67,10 @@ func TestXXMigrations(t *testing.T) { _, err = s.GetPKCERequestSession(context.Background(), sig, oauth2.NewSession("")) require.NoError(t, err) } + + if k >= 11 { + require.True(t, errors.Is(s.ClientAssertionJWTValid(context.Background(), sig), fosite.ErrJTIKnown), "%+v", err) + } }) }, )
x/clean_sql.go+1 −0 modified@@ -21,6 +21,7 @@ func CleanSQL(t *testing.T, db *sqlx.DB) { "hydra_oauth2_authentication_session", "hydra_oauth2_obfuscated_authentication_session", "hydra_oauth2_logout_request", + "hydra_oauth2_jti_blacklist", "hydra_jwk", "hydra_client", // Migrations
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-3p3g-vpw6-4w66ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2020-5300ghsaADVISORY
- github.com/ory/hydra/commit/700d17d3b7d507de1b1d459a7261d6fb2571ebe3ghsax_refsource_MISCWEB
- github.com/ory/hydra/releases/tag/v1.4.0ghsax_refsource_MISCWEB
- github.com/ory/hydra/security/advisories/GHSA-3p3g-vpw6-4w66ghsax_refsource_CONFIRMWEB
- openid.net/specs/openid-connect-core-1_0.htmlghsaWEB
News mentions
0No linked articles in our index yet.