Skip to content

Commit

Permalink
feat: flush refresh tokens for service oauth2/flush (#2373)
Browse files Browse the repository at this point in the history
See #1574 (comment)

Co-authored-by: hackerman <3372410+aeneasr@users.noreply.github.com>
  • Loading branch information
naveenpaul1 and aeneasr authored Mar 8, 2021
1 parent 277afe9 commit b46a14c
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 0 deletions.
5 changes: 5 additions & 0 deletions oauth2/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
77 changes: 77 additions & 0 deletions oauth2/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand All @@ -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 {
Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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) {
Expand Down
13 changes: 13 additions & 0 deletions persistence/sql/persister_oauth2.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 2 additions & 0 deletions x/fosite_storer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

0 comments on commit b46a14c

Please sign in to comment.