Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
ARG BASE_IMAGE=alpine

FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.9.0@sha256:c64defb9ed5a91eacb37f96ccc3d4cd72521c4bd18d5442905b95e2226b0e707 AS xx
FROM --platform=$BUILDPLATFORM docker.io/tonistiigi/xx:1.9.0@sha256:c64defb9ed5a91eacb37f96ccc3d4cd72521c4bd18d5442905b95e2226b0e707 AS xx

FROM --platform=$BUILDPLATFORM golang:1.26.4-alpine3.22@sha256:727cfc3c40be55cd1bc9a4a059406b28a059857e3be752aa9d09531e12c20c56 AS builder
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.26.4-alpine3.22@sha256:727cfc3c40be55cd1bc9a4a059406b28a059857e3be752aa9d09531e12c20c56 AS builder

COPY --from=xx / /

Expand Down
13 changes: 13 additions & 0 deletions cmd/dex/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"os"
"os/signal"
"path/filepath"
"regexp"
"runtime"
"strings"
"sync/atomic"
Expand Down Expand Up @@ -242,6 +243,18 @@ func runServe(options serveOptions) error {
}
c.StaticClients[i].Secret = os.Getenv(client.SecretEnv)
}
if client.InsecureAllowRegexpRedirectURIs {
for _, uri := range client.RedirectURIs {
if !client.InsecureAllowWildcardRedirectURIs && strings.Contains(uri, ".*") {
return fmt.Errorf("invalid config: InsecureAllowWildcardRedirectURIs is required when using \".*\"")
Comment thread
SGT911 marked this conversation as resolved.
Outdated
}

_, err := regexp.Compile(uri)
if err != nil {
return fmt.Errorf("invalid config: RedirectURI %q is not a valid regexp expression", uri)
}
}
}
logger.Info("config static client", "client_name", client.Name)
}
s = storage.WithStaticClients(s, c.StaticClients)
Expand Down
43 changes: 43 additions & 0 deletions server/authflow/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net"
"net/http"
"net/url"
"regexp"
"slices"
"strconv"
"strings"
Expand Down Expand Up @@ -93,6 +94,16 @@ func validateRedirectURI(client storage.Client, redirectURI string) bool {
return true
}
}

// Check redirectURIs using regexp package if is allowed
if client.InsecureAllowRegexpRedirectURIs {
valid := validateRegexpRedirectURI(client.RedirectURIs, redirectURI, client.InsecureAllowWildcardRedirectURIs)

if valid {
return true
}
}

// For non-public clients or when RedirectURIs is set, we allow only explicitly named RedirectURIs.
if !client.Public || len(client.RedirectURIs) > 0 {
return false
Expand Down Expand Up @@ -126,6 +137,38 @@ func isHostLocal(host string) bool {
return host == "localhost" || net.ParseIP(host).IsLoopback()
}

func validateRegexpRedirectURI(redirectURIs []string, redirectURI string, allowWildcard bool) bool {
for _, uri := range redirectURIs {
if !allowWildcard && strings.Contains(uri, ".*") {
continue
}

rgx, err := regexp.Compile(surroundRedirectURIRegexp(uri))
if err != nil {
continue
}

if rgx.Match([]byte(redirectURI)) {
return true
}
}

return false
}

func surroundRedirectURIRegexp(uri string) (result string) {
result = uri
if result[0] != '^' {
result = "^" + result
}

if result[len(result)-1] != '$' {
result = result + "$"
}

return
}
Comment thread
SGT911 marked this conversation as resolved.
Outdated

func validateConnectorID(connectors []storage.Connector, connectorID string) bool {
for _, c := range connectors {
if c.ID == connectorID {
Expand Down
89 changes: 89 additions & 0 deletions server/authflow/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,95 @@ func TestParseAuthorizationRequest(t *testing.T) {
"scope": "openid email profile",
},
},
{
name: "regexp url",
clients: []storage.Client{
{
ID: "bar",
InsecureAllowRegexpRedirectURIs: true,
RedirectURIs: []string{`https://pr-(\d+).example.com`},
},
},
supportedResponseTypes: []string{"code", "id_token", "token"},
queryParams: map[string]string{
"client_id": "bar",
"redirect_uri": "https://pr-1010.example.com",
"response_type": "code",
"scope": "openid email profile",
},
},
{
name: "regexp url without flag",
clients: []storage.Client{
{
ID: "bar",
InsecureAllowRegexpRedirectURIs: false,
RedirectURIs: []string{`https://pr-(\d+).example.com`},
},
},
supportedResponseTypes: []string{"code", "id_token", "token"},
queryParams: map[string]string{
"client_id": "bar",
"redirect_uri": "https://pr-1010.example.com",
"response_type": "code",
"scope": "openid email profile",
},
expectedError: &displayedAuthErr{Status: http.StatusBadRequest},
},
{
name: "regexp url with malicious uri",
clients: []storage.Client{
{
ID: "bar",
InsecureAllowRegexpRedirectURIs: true,
RedirectURIs: []string{`https://pr-(\d+).example.com`},
},
},
supportedResponseTypes: []string{"code", "id_token", "token"},
queryParams: map[string]string{
"client_id": "bar",
"redirect_uri": "https://pr-1010.example.com.attacker.xyz",
"response_type": "code",
"scope": "openid email profile",
},
expectedError: &displayedAuthErr{Status: http.StatusBadRequest},
},
{
name: "wildcard url",
clients: []storage.Client{
{
ID: "bar",
InsecureAllowRegexpRedirectURIs: true,
InsecureAllowWildcardRedirectURIs: true,
RedirectURIs: []string{`https?://.*`},
},
},
supportedResponseTypes: []string{"code", "id_token", "token"},
queryParams: map[string]string{
"client_id": "bar",
"redirect_uri": "https://example.com",
"response_type": "code",
"scope": "openid email profile",
},
},
{
name: "wildcard url without flag",
clients: []storage.Client{
{
ID: "bar",
InsecureAllowRegexpRedirectURIs: true,
RedirectURIs: []string{`https?://.*`},
},
},
supportedResponseTypes: []string{"code", "id_token", "token"},
queryParams: map[string]string{
"client_id": "bar",
"redirect_uri": "https://example.com",
"response_type": "code",
"scope": "openid email profile",
},
expectedError: &displayedAuthErr{Status: http.StatusBadRequest},
},
{
name: "choose second connector_id",
clients: []storage.Client{
Expand Down
11 changes: 11 additions & 0 deletions storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,17 @@ type Client struct {
Secret string `json:"secret"`
SecretEnv string `json:"secretEnv"`

// InsecureAllowRegexpRedirectURIs is an additiona flag allowing, add to
// RedirectURIs regexp expressions for dynamic URIs.
Comment thread
SGT911 marked this conversation as resolved.
Outdated
//
// Note: The flag does not allow wildcard regexp like: ".*" or "https?://.*" or
// any ".*" in the string, unless using InsecureAllowWildcardRedirectURIs flag.
InsecureAllowRegexpRedirectURIs bool `json:"insecureAllowRegexpRedirectURIs"`

// InsecureAllowWildcardRedirectURIs in use with InsecureAllowRegexpRedirectURIs
// allows to add wildcard regexp mainly for development purpose.
InsecureAllowWildcardRedirectURIs bool `json:"insecureAllowWildcardRedirectURIs"`

// A registered set of redirect URIs. When redirecting from dex to the client, the URI
// requested to redirect to MUST match one of these values, unless the client is "public".
RedirectURIs []string `json:"redirectURIs"`
Expand Down
Loading