mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-01 00:33:52 +00:00
fix: enable iframe use on http://localhost (#7152)
* fix: enable iframe use on http://localhost * docs(iframe): add info about cookies * improve comments
This commit is contained in:
parent
2ccb7baf85
commit
96d0291848
@ -105,7 +105,7 @@ Go to the "Advanced" section, per default login with phone number should be allo
|
|||||||
## Embedding ZITADEL in an iFrame
|
## Embedding ZITADEL in an iFrame
|
||||||
|
|
||||||
To maximise the security during login and in the Console UI, ZITADEL follows security best practices by setting a
|
To maximise the security during login and in the Console UI, ZITADEL follows security best practices by setting a
|
||||||
Content-Security-Policy (CSP) and X-Frame-Options:
|
Content-Security-Policy (CSP), X-Frame-Options and cookies with SameSite Lax:
|
||||||
|
|
||||||
```
|
```
|
||||||
Content-Security-Policy: frame-ancestors 'none'
|
Content-Security-Policy: frame-ancestors 'none'
|
||||||
@ -136,7 +136,13 @@ This will change the CSP to the following:
|
|||||||
Content-Security-Policy: frame-ancestors https://custom-domain.com
|
Content-Security-Policy: frame-ancestors https://custom-domain.com
|
||||||
```
|
```
|
||||||
|
|
||||||
and remove the X-Frame-Options header.
|
remove the X-Frame-Options header and change the SameSite to `None`.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
Please note, that SameSite None requires the cookie to be flagged `secure`, which means it must be sent over TLS (HTTPS) or localhost.
|
||||||
|
This also means that domains other than localhost must use TLS for this option to work.
|
||||||
|
This is due to browser restrictions: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#none
|
||||||
|
:::
|
||||||
|
|
||||||
### Disable Multi-factor (MFA) Prompt
|
### Disable Multi-factor (MFA) Prompt
|
||||||
|
|
||||||
|
@ -10,10 +10,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
prefixSecure = "__Secure-"
|
PrefixSecure cookiePrefix = "__Secure-"
|
||||||
prefixHost = "__Host-"
|
PrefixHost cookiePrefix = "__Host-"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type cookiePrefix string
|
||||||
|
|
||||||
type CookieHandler struct {
|
type CookieHandler struct {
|
||||||
securecookie *securecookie.SecureCookie
|
securecookie *securecookie.SecureCookie
|
||||||
secureOnly bool
|
secureOnly bool
|
||||||
@ -21,6 +23,7 @@ type CookieHandler struct {
|
|||||||
sameSite http.SameSite
|
sameSite http.SameSite
|
||||||
path string
|
path string
|
||||||
maxAge int
|
maxAge int
|
||||||
|
prefix cookiePrefix
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCookieHandler(opts ...CookieHandlerOpt) *CookieHandler {
|
func NewCookieHandler(opts ...CookieHandlerOpt) *CookieHandler {
|
||||||
@ -78,14 +81,17 @@ func WithMaxAge(maxAge int) CookieHandlerOpt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetCookiePrefix(name, domain, path string, secureOnly bool) string {
|
func WithPrefix(prefix cookiePrefix) CookieHandlerOpt {
|
||||||
|
return func(c *CookieHandler) {
|
||||||
|
c.prefix = prefix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetCookiePrefix(name string, secureOnly bool, prefix cookiePrefix) string {
|
||||||
if !secureOnly {
|
if !secureOnly {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
if domain != "" || path != "/" {
|
return string(prefix) + name
|
||||||
return prefixSecure + name
|
|
||||||
}
|
|
||||||
return prefixHost + name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CookieHandler) GetCookieValue(r *http.Request, name string) (string, error) {
|
func (c *CookieHandler) GetCookieValue(r *http.Request, name string) (string, error) {
|
||||||
@ -97,7 +103,7 @@ func (c *CookieHandler) GetCookieValue(r *http.Request, name string) (string, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *CookieHandler) GetEncryptedCookieValue(r *http.Request, name string, value interface{}) error {
|
func (c *CookieHandler) GetEncryptedCookieValue(r *http.Request, name string, value interface{}) error {
|
||||||
cookie, err := r.Cookie(SetCookiePrefix(name, r.Host, c.path, c.secureOnly))
|
cookie, err := r.Cookie(SetCookiePrefix(name, c.secureOnly, c.prefix))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -131,19 +137,24 @@ func (c *CookieHandler) DeleteCookie(w http.ResponseWriter, name string) {
|
|||||||
c.httpSet(w, name, "", "", -1)
|
c.httpSet(w, name, "", "", -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CookieHandler) httpSet(w http.ResponseWriter, name, domain, value string, maxage int) {
|
func (c *CookieHandler) httpSet(w http.ResponseWriter, name, domain, value string, maxAge int) {
|
||||||
c.httpSetWithSameSite(w, name, domain, value, maxage, c.sameSite)
|
c.httpSetWithSameSite(w, name, domain, value, maxAge, c.sameSite)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CookieHandler) httpSetWithSameSite(w http.ResponseWriter, name, domain, value string, maxage int, sameSite http.SameSite) {
|
func (c *CookieHandler) httpSetWithSameSite(w http.ResponseWriter, name, host, value string, maxAge int, sameSite http.SameSite) {
|
||||||
|
domain := strings.Split(host, ":")[0]
|
||||||
|
// same site none requires the secure flag, so we'll set it even if the cookie is set on non-TLS for localhost
|
||||||
|
secure := c.secureOnly || (sameSite == http.SameSiteNoneMode && domain == "localhost")
|
||||||
|
// prefix the cookie for secure cookies (TLS only, therefore not for samesite none on http://localhost)
|
||||||
|
prefixedName := SetCookiePrefix(name, c.secureOnly, c.prefix)
|
||||||
http.SetCookie(w, &http.Cookie{
|
http.SetCookie(w, &http.Cookie{
|
||||||
Name: SetCookiePrefix(name, domain, c.path, c.secureOnly),
|
Name: prefixedName,
|
||||||
Value: value,
|
Value: value,
|
||||||
Domain: strings.Split(domain, ":")[0],
|
Domain: domain,
|
||||||
Path: c.path,
|
Path: c.path,
|
||||||
MaxAge: maxage,
|
MaxAge: maxAge,
|
||||||
HttpOnly: c.httpOnly,
|
HttpOnly: c.httpOnly,
|
||||||
Secure: c.secureOnly,
|
Secure: secure,
|
||||||
SameSite: sameSite,
|
SameSite: sameSite,
|
||||||
})
|
})
|
||||||
varyValues := w.Header().Values("vary")
|
varyValues := w.Header().Values("vary")
|
||||||
|
@ -46,6 +46,7 @@ func NewUserAgentHandler(config *UserAgentCookieConfig, cookieKey []byte, idGene
|
|||||||
opts := []http_utils.CookieHandlerOpt{
|
opts := []http_utils.CookieHandlerOpt{
|
||||||
http_utils.WithEncryption(cookieKey, cookieKey),
|
http_utils.WithEncryption(cookieKey, cookieKey),
|
||||||
http_utils.WithMaxAge(int(config.MaxAge.Seconds())),
|
http_utils.WithMaxAge(int(config.MaxAge.Seconds())),
|
||||||
|
http_utils.WithPrefix(http_utils.PrefixSecure),
|
||||||
}
|
}
|
||||||
if !externalSecure {
|
if !externalSecure {
|
||||||
opts = append(opts, http_utils.WithUnsecure())
|
opts = append(opts, http_utils.WithUnsecure())
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/csrf"
|
"github.com/gorilla/csrf"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/feature"
|
"github.com/zitadel/zitadel/feature"
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
http_utils "github.com/zitadel/zitadel/internal/api/http"
|
http_utils "github.com/zitadel/zitadel/internal/api/http"
|
||||||
@ -122,13 +123,21 @@ func createCSRFInterceptor(cookieName string, csrfCookieKey []byte, externalSecu
|
|||||||
handler.ServeHTTP(w, r)
|
handler.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// by default we use SameSite Lax and the externalSecure (TLS) for the secure flag
|
||||||
sameSiteMode := csrf.SameSiteLaxMode
|
sameSiteMode := csrf.SameSiteLaxMode
|
||||||
if len(authz.GetInstance(r.Context()).SecurityPolicyAllowedOrigins()) > 0 {
|
secureOnly := externalSecure
|
||||||
|
instance := authz.GetInstance(r.Context())
|
||||||
|
// in case of `allow iframe`...
|
||||||
|
if len(instance.SecurityPolicyAllowedOrigins()) > 0 {
|
||||||
|
// ... we need to change to SameSite none ...
|
||||||
sameSiteMode = csrf.SameSiteNoneMode
|
sameSiteMode = csrf.SameSiteNoneMode
|
||||||
|
// ... and since SameSite none requires the secure flag, we'll set it for TLS and for localhost
|
||||||
|
// (regardless of the TLS / externalSecure settings)
|
||||||
|
secureOnly = externalSecure || instance.RequestedDomain() == "localhost"
|
||||||
}
|
}
|
||||||
csrf.Protect(csrfCookieKey,
|
csrf.Protect(csrfCookieKey,
|
||||||
csrf.Secure(externalSecure),
|
csrf.Secure(secureOnly),
|
||||||
csrf.CookieName(http_utils.SetCookiePrefix(cookieName, "", path, externalSecure)),
|
csrf.CookieName(http_utils.SetCookiePrefix(cookieName, externalSecure, http_utils.PrefixHost)),
|
||||||
csrf.Path(path),
|
csrf.Path(path),
|
||||||
csrf.ErrorHandler(errorHandler),
|
csrf.ErrorHandler(errorHandler),
|
||||||
csrf.SameSite(sameSiteMode),
|
csrf.SameSite(sameSiteMode),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user