diff --git a/oauth2/handler.go b/oauth2/handler.go index 9b1a9d3fe7e..157919f3b4d 100644 --- a/oauth2/handler.go +++ b/oauth2/handler.go @@ -517,6 +517,11 @@ func (h *Handler) FlushHandler(w http.ResponseWriter, r *http.Request, _ httprou return } + if err := h.r.OAuth2Storage().FlushInactiveRefreshTokens(r.Context(), fr.NotAfter); err != nil { + h.r.Writer().WriteError(w, r, err) + return + } + w.WriteHeader(http.StatusNoContent) } diff --git a/oauth2/handler_test.go b/oauth2/handler_test.go index 4eb0d89d14a..6460da8e53b 100644 --- a/oauth2/handler_test.go +++ b/oauth2/handler_test.go @@ -88,6 +88,57 @@ var flushRequests = []*fosite.Request{ Session: &oauth2.Session{DefaultSession: &openid.DefaultSession{Subject: "bar"}}, }, } +var tokenSignature = "4c7c7e8b3a77ad0c3ec846a21653c48b45dbfa31" +var flushRefreshRequests = []*fosite.AccessRequest{ + { + GrantTypes: []string{ + "refresh_token", + }, + Request: fosite.Request{ + RequestedAt: time.Now().Round(time.Second), + ID: "flush-refresh-1", + Client: &client.Client{OutfacingID: "foobar"}, + RequestedScope: []string{"offline"}, + GrantedScope: []string{"offline"}, + Session: &oauth2.Session{DefaultSession: &openid.DefaultSession{Subject: "bar"}}, + Form: url.Values{ + "refresh_token": []string{fmt.Sprintf("%s.%s", "flush-refresh-1", tokenSignature)}, + }, + }, + }, + { + GrantTypes: []string{ + "refresh_token", + }, + Request: fosite.Request{ + RequestedAt: time.Now().Round(time.Second).Add(-(lifespan + time.Minute)), + ID: "flush-refresh-2", + Client: &client.Client{OutfacingID: "foobar"}, + RequestedScope: []string{"offline"}, + GrantedScope: []string{"offline"}, + Session: &oauth2.Session{DefaultSession: &openid.DefaultSession{Subject: "bar"}}, + Form: url.Values{ + "refresh_token": []string{fmt.Sprintf("%s.%s", "flush-refresh-2", tokenSignature)}, + }, + }, + }, + { + GrantTypes: []string{ + "refresh_token", + }, + Request: fosite.Request{ + RequestedAt: time.Now().Round(time.Second).Add(-(lifespan + time.Hour)), + ID: "flush-refresh-3", + Client: &client.Client{OutfacingID: "foobar"}, + RequestedScope: []string{"offline"}, + GrantedScope: []string{"offline"}, + Session: &oauth2.Session{DefaultSession: &openid.DefaultSession{Subject: "bar"}}, + Form: url.Values{ + "refresh_token": []string{fmt.Sprintf("%s.%s", "flush-refresh-3", tokenSignature)}, + }, + }, + }, +} func TestHandlerDeleteHandler(t *testing.T) { conf := internal.NewConfigurationWithDefaults() @@ -132,6 +183,7 @@ func TestHandlerFlushHandler(t *testing.T) { conf := internal.NewConfigurationWithDefaults() conf.MustSet(config.KeyScopeStrategy, "DEPRECATED_HIERARCHICAL_SCOPE_STRATEGY") conf.MustSet(config.KeyIssuerURL, "http://hydra.localhost") + conf.MustSet(config.KeyRefreshTokenLifespan, time.Hour*1) reg := internal.NewRegistryMemory(t, conf) cl := reg.ClientManager() @@ -142,6 +194,10 @@ func TestHandlerFlushHandler(t *testing.T) { _ = cl.CreateClient(context.Background(), r.Client.(*client.Client)) require.NoError(t, store.CreateAccessTokenSession(context.Background(), r.ID, r)) } + for _, fr := range flushRefreshRequests { + _ = cl.CreateClient(context.Background(), fr.Client.(*client.Client)) + require.NoError(t, store.CreateRefreshTokenSession(context.Background(), fr.ID, fr)) + } r := x.NewRouterAdmin() h.SetRoutes(r, r.RouterPublic(), func(h http.Handler) http.Handler { @@ -164,6 +220,13 @@ func TestHandlerFlushHandler(t *testing.T) { _, err = store.GetAccessTokenSession(ctx, "flush-3", ds) require.NoError(t, err) + _, err = store.GetRefreshTokenSession(ctx, "flush-refresh-1", ds) + require.NoError(t, err) + _, err = store.GetRefreshTokenSession(ctx, "flush-refresh-2", ds) + require.NoError(t, err) + _, err = store.GetRefreshTokenSession(ctx, "flush-refresh-3", ds) + require.NoError(t, err) + _, err = c.Admin.FlushInactiveOAuth2Tokens(admin.NewFlushInactiveOAuth2TokensParams().WithBody(&models.FlushInactiveOAuth2TokensRequest{NotAfter: strfmt.DateTime(time.Now().Add(-(lifespan + time.Hour/2)))})) require.NoError(t, err) @@ -174,6 +237,13 @@ func TestHandlerFlushHandler(t *testing.T) { _, err = store.GetAccessTokenSession(ctx, "flush-3", ds) require.Error(t, err) + _, err = store.GetRefreshTokenSession(ctx, "flush-refresh-1", ds) + require.NoError(t, err) + _, err = store.GetRefreshTokenSession(ctx, "flush-refresh-2", ds) + require.NoError(t, err) + _, err = store.GetRefreshTokenSession(ctx, "flush-refresh-3", ds) + require.Error(t, err) + _, err = c.Admin.FlushInactiveOAuth2Tokens(admin.NewFlushInactiveOAuth2TokensParams().WithBody(&models.FlushInactiveOAuth2TokensRequest{NotAfter: strfmt.DateTime(time.Now())})) require.NoError(t, err) @@ -183,6 +253,13 @@ func TestHandlerFlushHandler(t *testing.T) { require.Error(t, err) _, err = store.GetAccessTokenSession(ctx, "flush-3", ds) require.Error(t, err) + + _, err = store.GetRefreshTokenSession(ctx, "flush-refresh-1", ds) + require.NoError(t, err) + _, err = store.GetRefreshTokenSession(ctx, "flush-refresh-2", ds) + require.Error(t, err) + _, err = store.GetRefreshTokenSession(ctx, "flush-refresh-3", ds) + require.Error(t, err) } func TestUserinfo(t *testing.T) { diff --git a/persistence/sql/persister_oauth2.go b/persistence/sql/persister_oauth2.go index cd32a03aa7d..93a24ef67c4 100644 --- a/persistence/sql/persister_oauth2.go +++ b/persistence/sql/persister_oauth2.go @@ -362,6 +362,19 @@ func (p *Persister) FlushInactiveAccessTokens(ctx context.Context, notAfter time return sqlcon.HandleError(err) } +func (p *Persister) FlushInactiveRefreshTokens(ctx context.Context, notAfter time.Time) error { + /* #nosec G201 table is static */ + err := p.Connection(ctx).RawQuery( + fmt.Sprintf("DELETE FROM %s WHERE requested_at < ? AND requested_at < ?", OAuth2RequestSQL{Table: sqlTableRefresh}.TableName()), + time.Now().Add(-p.config.RefreshTokenLifespan()), + notAfter, + ).Exec() + if err == sql.ErrNoRows { + return errors.Wrap(fosite.ErrNotFound, "") + } + return sqlcon.HandleError(err) +} + func (p *Persister) DeleteAccessTokens(ctx context.Context, clientID string) error { /* #nosec G201 table is static */ return sqlcon.HandleError( diff --git a/x/fosite_storer.go b/x/fosite_storer.go index b41c0b13767..0c9c0b8cbff 100644 --- a/x/fosite_storer.go +++ b/x/fosite_storer.go @@ -43,4 +43,6 @@ type FositeStorer interface { FlushInactiveAccessTokens(ctx context.Context, notAfter time.Time) error DeleteAccessTokens(ctx context.Context, clientID string) error + + FlushInactiveRefreshTokens(ctx context.Context, notAfter time.Time) error }