SSO - Refresh hsdpamcookie to allow signle sign on between other Philips products (#49)
<!--- Provide a general summary of your changes in the Title above --> ## Description Users with different tabs, applications etc will now have their cookie refreshed so when they open other tabs that have other applications, these applications will now be authenticated. Example: I'm doing logging for 1 hour, when I open reporting, hsp reporting will ask for login due to expired cookie. Now this is no longer happer https://www.hsdp.io/documentation/identity-and-access-management-iam/api-documents/resource-reference-api/oauth2-api#/Session%20Refresh/refreshSessionUsingGET New settings introduced: `OAUTH2_PROXY_OIDC_ENABLE_COOKIE_REFRESH` default false `OAUTH2_PROXY_OIDC_COOKIE_REFRESH_NAME` default 'hsdpamcookie' ## Motivation and Context <!--- Why is this change required? What problem does it solve? --> <!--- If it fixes an open issue, please link to the issue here. --> ## How Has This Been Tested? <!--- Please describe in detail how you tested your changes. --> <!--- Include details of your testing environment, and the tests you ran to --> <!--- see how your change affects other areas of the code, etc. --> Tested locally ## Checklist: <!--- Go over all the following points, and put an `x` in all the boxes that apply. --> <!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> - [ ] My change requires a change to the documentation or CHANGELOG. - [ ] I have updated the documentation/CHANGELOG accordingly. - [ ] I have created a feature (non-master) branch for my PR.
This commit is contained in:
commit
5de6ce45d0
|
|
@ -41,3 +41,5 @@ _testmain.go
|
|||
# vi Dockerfile.dev
|
||||
# docker build -f Dockerfile.dev .
|
||||
Dockerfile.dev
|
||||
|
||||
obj
|
||||
4
Makefile
4
Makefile
|
|
@ -116,3 +116,7 @@ validate-go-version:
|
|||
.PHONY: local-env-%
|
||||
local-env-%:
|
||||
make -C contrib/local-environment $*
|
||||
|
||||
.PHONY: local-debug-build
|
||||
local-debug-build:
|
||||
go build -gcflags="all=-N -l" -o app.exe
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ providers:
|
|||
insecureSkipNonce: true
|
||||
audienceClaims: [aud]
|
||||
extraAudiences: []
|
||||
cookieRefreshName: 'hsdpamcookie'
|
||||
loginURLParameters:
|
||||
- name: approval_prompt
|
||||
default:
|
||||
|
|
@ -157,6 +158,7 @@ redirect_url="http://localhost:4180/oauth2/callback"
|
|||
AudienceClaims: []string{"aud"},
|
||||
ExtraAudiences: []string{},
|
||||
InsecureSkipNonce: true,
|
||||
CookieRefreshName: "hsdpamcookie",
|
||||
},
|
||||
LoginURLParameters: []options.LoginURLParameter{
|
||||
{Name: "approval_prompt", Default: []string{"force"}},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
|
|
@ -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")
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ var _ = Describe("Load", func() {
|
|||
OIDCGroupsClaim: "groups",
|
||||
OIDCAudienceClaims: []string{"aud"},
|
||||
InsecureOIDCSkipNonce: true,
|
||||
OIDCCookieRefreshName: "hsdpamcookie",
|
||||
},
|
||||
|
||||
Options: Options{
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue