From ac2d04686ffaf8229c921c4c39631d14e7412d08 Mon Sep 17 00:00:00 2001 From: Erikson Bahr Date: Wed, 31 Jan 2024 16:59:52 -0300 Subject: [PATCH] implement oidc singl sign on cookie refresher --- .gitignore | 2 + oauthproxy.go | 3 ++ pkg/apis/sessions/session_state.go | 5 ++- pkg/middleware/cookie_refresh.go | 60 ++++++++++++++++++++++++++++++ pkg/middleware/stored_session.go | 3 ++ providers/oidc.go | 1 + 6 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 pkg/middleware/cookie_refresh.go diff --git a/.gitignore b/.gitignore index 57f30444..393f27d5 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,5 @@ _testmain.go # vi Dockerfile.dev # docker build -f Dockerfile.dev . Dockerfile.dev + +obj \ No newline at end of file diff --git a/oauthproxy.go b/oauthproxy.go index d11040c3..d5f79cb7 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -393,6 +393,9 @@ func buildSessionChain(opts *options.Options, provider providers.Provider, sessi ValidateSession: provider.ValidateSession, })) + x := opts.Providers[0] + chain = chain.Append(middleware.NewCookieRefresh(&middleware.CookieRefreshOptions{IssuerURL: x.OIDCConfig.IssuerURL})) + return chain } diff --git a/pkg/apis/sessions/session_state.go b/pkg/apis/sessions/session_state.go index 2fd51613..43075adc 100644 --- a/pkg/apis/sessions/session_state.go +++ b/pkg/apis/sessions/session_state.go @@ -31,8 +31,9 @@ type SessionState struct { IntrospectClaims string `msgpack:"ic,omitempty"` // Internal helpers, not serialized - Clock clock.Clock `msgpack:"-"` - Lock Lock `msgpack:"-"` + Clock clock.Clock `msgpack:"-"` + Lock Lock `msgpack:"-"` + SessionJustRefreshed bool `msgpack:"-"` } func (s *SessionState) ObtainLock(ctx context.Context, expiration time.Duration) error { diff --git a/pkg/middleware/cookie_refresh.go b/pkg/middleware/cookie_refresh.go new file mode 100644 index 00000000..e5fc9104 --- /dev/null +++ b/pkg/middleware/cookie_refresh.go @@ -0,0 +1,60 @@ +package middleware + +import ( + "fmt" + "net/http" + + "github.com/justinas/alice" + middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" +) + +type CookieRefreshOptions struct { + IssuerURL string +} + +func NewCookieRefresh(opts *CookieRefreshOptions) alice.Constructor { + cr := &cookieRefresh{ + HttpClient: &http.Client{}, + IssuerURL: opts.IssuerURL, + } + return cr.refreshCookie +} + +type cookieRefresh struct { + HttpClient *http.Client + IssuerURL string +} + +func (cr *cookieRefresh) refreshCookie(next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + scope := middlewareapi.GetRequestScope(req) + if scope.Session == nil || !scope.Session.SessionJustRefreshed { + next.ServeHTTP(rw, req) + return + } + + cookie, err := req.Cookie("hsdpamcookie") + if err != nil { + logger.Errorf("SSO Cookie Refresher - Could find 'hsdpamcookie' cookie in the request: %v", err) + return + } + resp := requests.New(fmt.Sprintf("%s/session/refresh", cr.IssuerURL)). + WithContext(req.Context()). + WithMethod("GET"). + SetHeader("api-version", "1"). + SetHeader("Cookie", fmt.Sprintf("hsdpamcookie=%s", cookie.Value)). + Do() + + if resp.StatusCode() != http.StatusNoContent { + bodyString := string(resp.Body()) + logger.Errorf("SSO Cookie Refresher - Could not refresh the 'hsdpamcookie' cookie due to status and content: %v - %v", resp.StatusCode(), bodyString) + return + } else { + logger.Print("SSO Cookie Refresher - Cookie 'hsdpamcookie' refreshed") + } + + next.ServeHTTP(rw, req) + }) +} diff --git a/pkg/middleware/stored_session.go b/pkg/middleware/stored_session.go index 1afe6d0c..75501ac4 100644 --- a/pkg/middleware/stored_session.go +++ b/pkg/middleware/stored_session.go @@ -48,6 +48,9 @@ type StoredSessionLoaderOptions struct { // If the sesssion is older than `RefreshPeriod` but the provider doesn't // refresh it, we must re-validate using this validation. ValidateSession func(context.Context, *sessionsapi.SessionState) bool + + // Callback that is called when a session is refreshed + OnSessionRefreshed *func(context.Context, *http.Request, *sessionsapi.SessionState) } // NewStoredSessionLoader creates a new storedSessionLoader which loads diff --git a/providers/oidc.go b/providers/oidc.go index de7b8277..f55ec0e4 100644 --- a/providers/oidc.go +++ b/providers/oidc.go @@ -159,6 +159,7 @@ func (p *OIDCProvider) RefreshSession(ctx context.Context, s *sessions.SessionSt if err != nil { return false, fmt.Errorf("unable to redeem refresh token: %v", err) } + s.SessionJustRefreshed = true return true, nil }