Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSO - Refresh hsdpamcookie to allow signle sign on between other Philips products #49

Merged
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,5 @@ _testmain.go
# vi Dockerfile.dev
# docker build -f Dockerfile.dev .
Dockerfile.dev

obj
2 changes: 2 additions & 0 deletions docs/docs/configuration/alpha_config.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,8 @@ character.
| `userIDClaim` | _string_ | UserIDClaim indicates which claim contains the user ID<br/>default set to 'email' |
| `audienceClaims` | _[]string_ | AudienceClaim allows to define any claim that is verified against the client id<br/>By default `aud` claim is used for verification. |
| `extraAudiences` | _[]string_ | ExtraAudiences is a list of additional audiences that are allowed<br/>to pass verification in addition to the client id. |
| `enableCookieRefresh` | _bool_ | Enable cookie refresh functionality that is going to be triggered every time the session is updated |
| `cookieRefreshName` | _string_ | Name of the cookie that is going to be extracted from the request and refreshed |

### Provider

Expand Down
6 changes: 6 additions & 0 deletions oauthproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,12 @@ func buildSessionChain(opts *options.Options, provider providers.Provider, sessi
ValidateSession: provider.ValidateSession,
}))

oidcProviderSettings := opts.Providers[0].OIDCConfig
if oidcProviderSettings.EnableCookieRefresh {
chain = chain.Append(middleware.NewCookieRefresh(&middleware.CookieRefreshOptions{IssuerURL: oidcProviderSettings.IssuerURL, CookieRefreshName: oidcProviderSettings.CookieRefreshName}))
logger.Printf("Enabling OIDC cookie refresh for the cookie '%s' functionality because OIDCEnableCookieRefresh is enabled", oidcProviderSettings.CookieRefreshName)
}

return chain
}

Expand Down
6 changes: 6 additions & 0 deletions pkg/apis/options/legacy_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,8 @@ type LegacyProvider struct {
OIDCGroupsClaim string `flag:"oidc-groups-claim" cfg:"oidc_groups_claim"`
OIDCAudienceClaims []string `flag:"oidc-audience-claim" cfg:"oidc_audience_claims"`
OIDCExtraAudiences []string `flag:"oidc-extra-audience" cfg:"oidc_extra_audiences"`
OIDCEnableCookieRefresh bool `flag:"oidc-enable-cookie-refresh" cfg:"oidc_enable_cookie_refresh"`
OIDCCookieRefreshName string `flag:"oidc-cookie-refresh-name" cfg:"oidc_cookie_refresh_name"`
LoginURL string `flag:"login-url" cfg:"login_url"`
RedeemURL string `flag:"redeem-url" cfg:"redeem_url"`
ProfileURL string `flag:"profile-url" cfg:"profile_url"`
Expand Down Expand Up @@ -601,6 +603,8 @@ func legacyProviderFlagSet() *pflag.FlagSet {
flagSet.String("oidc-email-claim", OIDCEmailClaim, "which OIDC claim contains the user's email")
flagSet.StringSlice("oidc-audience-claim", OIDCAudienceClaims, "which OIDC claims are used as audience to verify against client id")
flagSet.StringSlice("oidc-extra-audience", []string{}, "additional audiences allowed to pass audience verification")
flagSet.Bool("oidc-enable-cookie-refresh", false, "Refresh the OIDC provider cookies to enable SSO in an extended period of time")
flagSet.String("oidc-cookie-refresh-name", "hsdpamcookie", "The name of the cookie that the OIDC provider uses to keep its session fresh")
flagSet.String("login-url", "", "Authentication endpoint")
flagSet.String("redeem-url", "", "Token redemption endpoint")
flagSet.String("profile-url", "", "Profile access endpoint")
Expand Down Expand Up @@ -702,6 +706,8 @@ func (l *LegacyProvider) convert() (Providers, error) {
GroupsClaim: l.OIDCGroupsClaim,
AudienceClaims: l.OIDCAudienceClaims,
ExtraAudiences: l.OIDCExtraAudiences,
EnableCookieRefresh: l.OIDCEnableCookieRefresh,
CookieRefreshName: l.OIDCCookieRefreshName,
}

// Support for legacy configuration option
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/options/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ type OIDCOptions struct {
// ExtraAudiences is a list of additional audiences that are allowed
// to pass verification in addition to the client id.
ExtraAudiences []string `json:"extraAudiences,omitempty"`
// Enable cookie refresh functionality that is going to be triggered every time the session is updated
EnableCookieRefresh bool `json:"enableCookieRefresh,omitempty"`
// Name of the cookie that is going to be extracted from the request and refreshed
CookieRefreshName string `json:"cookieRefreshName,omitempty"`
}

type LoginGovOptions struct {
Expand Down
5 changes: 3 additions & 2 deletions pkg/apis/sessions/session_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
62 changes: 62 additions & 0 deletions pkg/middleware/cookie_refresh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
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
CookieRefreshName string
}

func NewCookieRefresh(opts *CookieRefreshOptions) alice.Constructor {
cr := &cookieRefresh{
HTTPClient: &http.Client{},
IssuerURL: opts.IssuerURL,
CookieRefreshName: opts.CookieRefreshName,
}
return cr.refreshCookie
}

type cookieRefresh struct {
HTTPClient *http.Client
IssuerURL string
CookieRefreshName string
}

func (cr *cookieRefresh) refreshCookie(next http.Handler) http.Handler {
EriksonBahr marked this conversation as resolved.
Show resolved Hide resolved
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(cr.CookieRefreshName)
if err != nil {
logger.Errorf("SSO Cookie Refresher - Could find '%s' cookie in the request: %v", cr.CookieRefreshName, 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("%s=%s", cr.CookieRefreshName, cookie.Value)).
Do()

if resp.StatusCode() != http.StatusNoContent {
bodyString := string(resp.Body())
logger.Errorf("SSO Cookie Refresher - Could not refresh the '%s' cookie due to status and content: %v - %v", cr.CookieRefreshName, resp.StatusCode(), bodyString)
return
}

logger.Printf("SSO Cookie Refresher - Cookie '%s' refreshed", cr.CookieRefreshName)
next.ServeHTTP(rw, req)
})
}
3 changes: 3 additions & 0 deletions pkg/middleware/stored_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions providers/oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Loading