feat(reverseproxy): validate acr and redirect if applicable

This commit is contained in:
Trong Huu Nguyen
2023-04-29 11:54:53 +02:00
parent 7c98fe161e
commit 3a239a95c3
5 changed files with 73 additions and 16 deletions

27
pkg/handler/acr/acr.go Normal file
View File

@@ -0,0 +1,27 @@
package acr
import (
"github.com/nais/wonderwall/pkg/config"
"github.com/nais/wonderwall/pkg/openid"
"github.com/nais/wonderwall/pkg/session"
)
type Handler struct {
Enabled bool
ExpectedValue string
}
func (h *Handler) Validate(sess *session.Session) error {
if !h.Enabled || sess == nil {
return nil
}
return openid.ValidateAcr(h.ExpectedValue, sess.Acr())
}
func NewHandler(cfg *config.Config) *Handler {
return &Handler{
Enabled: len(cfg.OpenID.ACRValues) > 0,
ExpectedValue: cfg.OpenID.ACRValues,
}
}

View File

@@ -15,6 +15,7 @@ import (
"github.com/nais/wonderwall/pkg/config"
"github.com/nais/wonderwall/pkg/cookie"
"github.com/nais/wonderwall/pkg/crypto"
"github.com/nais/wonderwall/pkg/handler/acr"
"github.com/nais/wonderwall/pkg/handler/autologin"
"github.com/nais/wonderwall/pkg/ingress"
"github.com/nais/wonderwall/pkg/metrics"
@@ -31,6 +32,7 @@ import (
var _ router.Source = &Standalone{}
type Standalone struct {
AcrHandler *acr.Handler
AutoLogin *autologin.AutoLogin
Client *openidclient.Client
Config *config.Config
@@ -77,6 +79,7 @@ func NewStandalone(
}
return &Standalone{
AcrHandler: acr.NewHandler(cfg),
AutoLogin: autoLogin,
Client: openidClient,
Config: cfg,
@@ -90,8 +93,8 @@ func NewStandalone(
}, nil
}
func (s *Standalone) GetSession(r *http.Request) (*session.Session, error) {
return s.SessionManager.GetOrRefresh(r)
func (s *Standalone) GetAcrHandler() *acr.Handler {
return s.AcrHandler
}
func (s *Standalone) GetAutoLogin() *autologin.AutoLogin {
@@ -115,6 +118,10 @@ func (s *Standalone) GetPath(r *http.Request) string {
return GetPath(r, s.GetIngresses())
}
func (s *Standalone) GetSession(r *http.Request) (*session.Session, error) {
return s.SessionManager.GetOrRefresh(r)
}
func (s *Standalone) Login(w http.ResponseWriter, r *http.Request) {
canonicalRedirect := s.Redirect.Canonical(r)
login, err := s.Client.Login(r)

View File

@@ -9,6 +9,7 @@ import (
"github.com/nais/wonderwall/pkg/config"
"github.com/nais/wonderwall/pkg/crypto"
"github.com/nais/wonderwall/pkg/handler/acr"
"github.com/nais/wonderwall/pkg/handler/autologin"
"github.com/nais/wonderwall/pkg/ingress"
logentry "github.com/nais/wonderwall/pkg/middleware"
@@ -22,6 +23,7 @@ import (
var _ router.Source = &SSOProxy{}
type SSOProxy struct {
AcrHandler *acr.Handler
AutoLogin *autologin.AutoLogin
Config *config.Config
Ingresses *ingress.Ingresses
@@ -59,6 +61,7 @@ func NewSSOProxy(cfg *config.Config, crypter crypto.Crypter) (*SSOProxy, error)
}
return &SSOProxy{
AcrHandler: acr.NewHandler(cfg),
AutoLogin: autoLogin,
Config: cfg,
Ingresses: ingresses,
@@ -70,8 +73,8 @@ func NewSSOProxy(cfg *config.Config, crypter crypto.Crypter) (*SSOProxy, error)
}, nil
}
func (s *SSOProxy) GetSession(r *http.Request) (*session.Session, error) {
return s.SessionReader.Get(r)
func (s *SSOProxy) GetAcrHandler() *acr.Handler {
return s.AcrHandler
}
func (s *SSOProxy) GetAutoLogin() *autologin.AutoLogin {
@@ -86,6 +89,10 @@ func (s *SSOProxy) GetPath(r *http.Request) string {
return GetPath(r, s.GetIngresses())
}
func (s *SSOProxy) GetSession(r *http.Request) (*session.Session, error) {
return s.SessionReader.Get(r)
}
func (s *SSOProxy) GetSSOServerURL() *urllib.URL {
u := *s.SSOServerURL
return &u

View File

@@ -10,6 +10,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/nais/wonderwall/pkg/handler/acr"
"github.com/nais/wonderwall/pkg/handler/autologin"
mw "github.com/nais/wonderwall/pkg/middleware"
"github.com/nais/wonderwall/pkg/session"
@@ -17,6 +18,7 @@ import (
)
type ReverseProxySource interface {
GetAcrHandler() *acr.Handler
GetAutoLogin() *autologin.AutoLogin
GetPath(r *http.Request) string
GetSession(r *http.Request) (*session.Session, error)
@@ -65,7 +67,7 @@ func (rp *ReverseProxy) Handler(src ReverseProxySource, w http.ResponseWriter, r
logger := mw.LogEntryFrom(r)
isAuthenticated := false
_, accessToken, err := getSessionWithValidToken(src, r)
sess, accessToken, err := getSessionWithValidToken(src, r)
switch {
case err == nil:
// add authentication if session checks out
@@ -82,18 +84,14 @@ func (rp *ReverseProxy) Handler(src ReverseProxySource, w http.ResponseWriter, r
logger.Errorf("default: unauthenticated: unexpected error: %+v", err)
}
err = src.GetAcrHandler().Validate(sess)
if err != nil {
loginRedirect(src, w, r, err.Error())
return
}
if src.GetAutoLogin().NeedsLogin(r, isAuthenticated) {
redirectTarget := r.URL.String()
path := src.GetPath(r)
loginUrl := url.LoginRelative(path, redirectTarget)
fields := logrus.Fields{
"redirect_after_login": redirectTarget,
"redirect_to": loginUrl,
}
logger.WithFields(fields).Info("default: unauthenticated: request matches auto-login; redirecting to login...")
http.Redirect(w, r, loginUrl, http.StatusFound)
loginRedirect(src, w, r, "request matches autologin")
return
}
@@ -120,6 +118,20 @@ func getSessionWithValidToken(src ReverseProxySource, r *http.Request) (*session
return sess, accessToken, nil
}
func loginRedirect(src ReverseProxySource, w http.ResponseWriter, r *http.Request, message string) {
redirectTarget := r.URL.String()
path := src.GetPath(r)
loginUrl := url.LoginRelative(path, redirectTarget)
fields := logrus.Fields{
"redirect_after_login": redirectTarget,
"redirect_to": loginUrl,
}
mw.LogEntryFrom(r).WithFields(fields).Infof("default: unauthenticated: %s; redirecting to login...", message)
http.Redirect(w, r, loginUrl, http.StatusFound)
}
type logrusErrorWriter struct{}
func (w logrusErrorWriter) Write(p []byte) (n int, err error) {

View File

@@ -60,6 +60,10 @@ func (in *Session) AccessToken() (string, error) {
return "", fmt.Errorf("%w: access token is expired", ErrInvalid)
}
func (in *Session) Acr() string {
return in.data.Acr
}
func (in *Session) ExternalSessionID() string {
return in.data.ExternalSessionID
}