mirror of
https://github.com/nais/wonderwall.git
synced 2026-08-23 21:16:14 +00:00
feat(openid): retry front-channel logouts
This commit is contained in:
+27
-7
@@ -417,23 +417,43 @@ func (s *Standalone) LogoutFrontChannel(w http.ResponseWriter, r *http.Request)
|
||||
// Unconditionally destroy all local references to the session.
|
||||
cookie.Clear(w, cookie.Session, s.GetCookieOptions(r))
|
||||
|
||||
lfc := s.Client.LogoutFrontchannel(r)
|
||||
if lfc.MissingSidParameter() {
|
||||
// sid is the session identifier that SHOULD be included as a parameter in the front-channel logout request.
|
||||
sid := r.URL.Query().Get("sid")
|
||||
if sid == "" {
|
||||
span.SetAttributes(attribute.Bool("logout.frontchannel.missing_sid_parameter", true))
|
||||
logger.Debugf("front-channel logout: sid parameter not found in request; ignoring")
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
return
|
||||
}
|
||||
|
||||
id := lfc.Sid()
|
||||
err := s.SessionManager.DeleteForExternalID(r.Context(), id)
|
||||
if err != nil {
|
||||
logger.Warnf("front-channel logout: destroying session with id %q: %+v", id, err)
|
||||
if err := s.SessionManager.DeleteForExternalID(r.Context(), sid); err != nil {
|
||||
if errors.Is(err, session.ErrNotFound) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Infof("front-channel logout: destroying session with id %q: %+v", sid, err)
|
||||
go func() {
|
||||
// attempt background delete with retries
|
||||
err := retry.Do(context.Background(), func(ctx context.Context) error {
|
||||
err = s.SessionManager.DeleteForExternalID(ctx, sid)
|
||||
if err == nil || errors.Is(err, session.ErrNotFound) {
|
||||
return nil
|
||||
}
|
||||
return retry.RetryableError(err)
|
||||
}, retry.WithMax(10*time.Minute))
|
||||
if err != nil {
|
||||
logger.Warnf("front-channel logout: retries exhausted for deletion of session with id %q: %+v", sid, err)
|
||||
} else {
|
||||
logger.WithField("sid", sid).Info("front-channel logout: session deleted in background")
|
||||
}
|
||||
}()
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
return
|
||||
}
|
||||
|
||||
logger.WithField("sid", id).Info("front-channel logout: session deleted")
|
||||
logger.WithField("sid", sid).Info("front-channel logout: session deleted")
|
||||
cookie.Clear(w, cookie.Retry, s.GetCookieOptions(r))
|
||||
metrics.ObserveLogout(metrics.LogoutOperationFrontChannel)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
@@ -87,10 +87,6 @@ func (c *Client) LogoutCallback(r *http.Request, cookie *openid.LogoutCookie, va
|
||||
return NewLogoutCallback(c, r, cookie, validator)
|
||||
}
|
||||
|
||||
func (c *Client) LogoutFrontchannel(r *http.Request) *LogoutFrontchannel {
|
||||
return NewLogoutFrontchannel(r)
|
||||
}
|
||||
|
||||
func (c *Client) AuthCodeGrant(ctx context.Context, code string, opts []oauth2.AuthCodeOption) (*oauth2.Token, error) {
|
||||
ctx = context.WithValue(ctx, oauth2.HTTPClient, c.httpClient)
|
||||
return c.oauth2Config.Exchange(ctx, code, opts...)
|
||||
|
||||
@@ -86,7 +86,3 @@ func newTestClientWithConfig(config *mock.TestConfiguration) *client.Client {
|
||||
jwksProvider := mock.NewTestJwksProvider()
|
||||
return client.NewClient(config, jwksProvider)
|
||||
}
|
||||
|
||||
func newTestClient() *client.Client {
|
||||
return newTestClientWithConfig(mock.NewTestConfiguration(mock.Config()))
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type LogoutFrontchannel struct {
|
||||
sid string
|
||||
}
|
||||
|
||||
func NewLogoutFrontchannel(r *http.Request) *LogoutFrontchannel {
|
||||
params := r.URL.Query()
|
||||
sid := params.Get("sid")
|
||||
|
||||
return &LogoutFrontchannel{
|
||||
sid: sid,
|
||||
}
|
||||
}
|
||||
|
||||
// Sid is the session identifier which SHOULD be included as a parameter in the front-channel logout request.
|
||||
func (l *LogoutFrontchannel) Sid() string {
|
||||
return l.sid
|
||||
}
|
||||
|
||||
func (l *LogoutFrontchannel) MissingSidParameter() bool {
|
||||
return len(l.sid) <= 0
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package client_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/nais/wonderwall/pkg/openid/client"
|
||||
)
|
||||
|
||||
func TestLogoutFrontchannel_Sid(t *testing.T) {
|
||||
t.Run("missing sid parameter in request", func(t *testing.T) {
|
||||
url := "http://localhost/oauth2/logout/frontchannel"
|
||||
lf := newLogoutFrontchannel(url)
|
||||
|
||||
assert.Empty(t, lf.Sid())
|
||||
assert.True(t, lf.MissingSidParameter())
|
||||
})
|
||||
|
||||
t.Run("has sid parameter in request", func(t *testing.T) {
|
||||
url := "http://localhost/oauth2/logout/frontchannel?sid=some-session-id"
|
||||
lf := newLogoutFrontchannel(url)
|
||||
|
||||
assert.Equal(t, "some-session-id", lf.Sid())
|
||||
assert.False(t, lf.MissingSidParameter())
|
||||
})
|
||||
}
|
||||
|
||||
func newLogoutFrontchannel(url string) *client.LogoutFrontchannel {
|
||||
req := httptest.NewRequest(http.MethodGet, url, nil)
|
||||
return newTestClient().LogoutFrontchannel(req)
|
||||
}
|
||||
Reference in New Issue
Block a user