diff --git a/README.md b/README.md index 0067b0dcf3..338e5f9814 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,6 @@ ![logo](docs/logos/dex-horizontal-color.png) - Dex is an identity service that uses [OpenID Connect][openid-connect] to drive authentication for other apps. Dex acts as a portal to other identity providers through ["connectors."](#connectors) This lets dex defer authentication to LDAP servers, SAML providers, or established identity providers like GitHub, Google, and Active Directory. Clients write their authentication logic once to talk to dex, then dex handles the protocols for a given backend. diff --git a/cmd/dex/config.go b/cmd/dex/config.go index 831156fd40..8cd539707d 100644 --- a/cmd/dex/config.go +++ b/cmd/dex/config.go @@ -150,6 +150,7 @@ type Web struct { TLSCert string `json:"tlsCert"` TLSKey string `json:"tlsKey"` AllowedOrigins []string `json:"allowedOrigins"` + FrameAncestors []string `json:"frameAncestors"` } // Telemetry is the config format for telemetry including the HTTP server config. diff --git a/cmd/dex/serve.go b/cmd/dex/serve.go index 47b090aeab..0de06a56e0 100644 --- a/cmd/dex/serve.go +++ b/cmd/dex/serve.go @@ -253,6 +253,10 @@ func runServe(options serveOptions) error { logger.Infof("config allowed origins: %s", c.Web.AllowedOrigins) } + if len(c.Web.FrameAncestors) > 0 { + logger.Infof("config allowed frame ancestors: %s", c.Web.FrameAncestors) + } + // explicitly convert to UTC. now := func() time.Time { return time.Now().UTC() } diff --git a/server/oauth2.go b/server/oauth2.go index cfae540528..b72431e0e8 100644 --- a/server/oauth2.go +++ b/server/oauth2.go @@ -145,9 +145,13 @@ const ( ) const ( - responseTypeCode = "code" // "Regular" flow - responseTypeToken = "token" // Implicit flow for frontend apps. - responseTypeIDToken = "id_token" // ID Token in url fragment + responseTypeCode = "code" // "Regular" flow + responseTypeToken = "token" // Implicit flow for frontend apps. + responseTypeIDToken = "id_token" // ID Token in url fragment + responseTypeCodeToken = "code token" // "Regular" flow + Implicit flow + responseTypeCodeIDToken = "code id_token" // "Regular" flow + ID Token + responseTypeIDTokenToken = "id_token token" // ID Token + Implicit flow + responseTypeCodeIDTokenToken = "code id_token token" // "Regular" flow + ID Token + Implicit flow ) const ( diff --git a/server/server.go b/server/server.go index 444fb7e15a..7d6e85ed5f 100644 --- a/server/server.go +++ b/server/server.go @@ -77,6 +77,11 @@ type Config struct { // domain. AllowedOrigins []string + // List of domain allowed to frame the content of the application. + // By default no one is accepted to prevent against clickjacking. + // Passing in "*" will allow any domain + FrameAncestors []string + // If enabled, the server won't prompt the user to approve authorization requests. // Logging in implies approval. SkipApprovalScreen bool @@ -225,9 +230,9 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy) for _, respType := range c.SupportedResponseTypes { switch respType { - case responseTypeCode, responseTypeIDToken: + case responseTypeCode, responseTypeIDToken, responseTypeCodeIDToken: // continue - case responseTypeToken: + case responseTypeToken, responseTypeCodeToken, responseTypeIDTokenToken, responseTypeCodeIDTokenToken: // response_type=token is an implicit flow, let's add it to the discovery info // https://datatracker.ietf.org/doc/html/rfc6749#section-4.2.1 allSupportedGrants[grantTypeImplicit] = true @@ -339,7 +344,27 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy) } } + // frame-ancestors middleware + frameAncestorsMidldleware := func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var ancestors string + if len(c.FrameAncestors) > 0 { + for i := 0; i < len(c.FrameAncestors); i++ { + if c.FrameAncestors[i] == issuerURL.String() { + c.FrameAncestors[i] = "'self'" + } + } + ancestors = strings.Join(c.FrameAncestors, " ") + } else { + ancestors = "'none'" + } + w.Header().Set("Content-Security-Policy", "frame-ancestors "+ancestors) + next.ServeHTTP(w, r) + }) + } + r := mux.NewRouter().SkipClean(true).UseEncodedPath() + r.Use(frameAncestorsMidldleware) handle := func(p string, h http.Handler) { r.Handle(path.Join(issuerURL.Path, p), instrumentHandlerCounter(p, h)) }