Skip to content

Commit 30b7dd6

Browse files
committed
feat: Allowing on RedirectURIs the usage of regexp due to issue #448.
Adding new flags for conditional logic and allowing back compatibility of the feature
1 parent e1956ec commit 30b7dd6

5 files changed

Lines changed: 127 additions & 2 deletions

File tree

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
ARG BASE_IMAGE=alpine
22

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

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

77
COPY --from=xx / /
88

cmd/dex/serve.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"os"
1515
"os/signal"
1616
"path/filepath"
17+
"regexp"
1718
"runtime"
1819
"strings"
1920
"sync/atomic"
@@ -242,6 +243,18 @@ func runServe(options serveOptions) error {
242243
}
243244
c.StaticClients[i].Secret = os.Getenv(client.SecretEnv)
244245
}
246+
if client.InsecureAllowRegexpRedirectURIs {
247+
for _, uri := range client.RedirectURIs {
248+
if client.InsecureAllowWildcardRedirectURIs && strings.Contains(uri, ".*") {
249+
return fmt.Errorf("invalid config: InsecureAllowWildcardRedirectURIs is required when using \".*\"")
250+
}
251+
252+
_, err := regexp.Compile(uri)
253+
if err != nil {
254+
return fmt.Errorf("invalid config: RedirectURI %q is not a valid regexp expression", uri)
255+
}
256+
}
257+
}
245258
logger.Info("config static client", "client_name", client.Name)
246259
}
247260
s = storage.WithStaticClients(s, c.StaticClients)

server/authflow/request.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net"
88
"net/http"
99
"net/url"
10+
"regexp"
1011
"slices"
1112
"strconv"
1213
"strings"
@@ -93,6 +94,16 @@ func validateRedirectURI(client storage.Client, redirectURI string) bool {
9394
return true
9495
}
9596
}
97+
98+
// Check redirectURIs using regexp package if is allowed
99+
if client.InsecureAllowRegexpRedirectURIs {
100+
valid := validateRegexpRedirectURI(client.RedirectURIs, redirectURI, client.InsecureAllowWildcardRedirectURIs)
101+
102+
if valid {
103+
return true
104+
}
105+
}
106+
96107
// For non-public clients or when RedirectURIs is set, we allow only explicitly named RedirectURIs.
97108
if !client.Public || len(client.RedirectURIs) > 0 {
98109
return false
@@ -126,6 +137,25 @@ func isHostLocal(host string) bool {
126137
return host == "localhost" || net.ParseIP(host).IsLoopback()
127138
}
128139

140+
func validateRegexpRedirectURI(redirectURIs []string, redirectURI string, allowWildcard bool) bool {
141+
for _, uri := range redirectURIs {
142+
if !allowWildcard && strings.Contains(uri, ".*") {
143+
continue
144+
}
145+
146+
rgx, err := regexp.Compile(uri)
147+
if err != nil {
148+
continue
149+
}
150+
151+
if rgx.Match([]byte(redirectURI)) {
152+
return true
153+
}
154+
}
155+
156+
return false
157+
}
158+
129159
func validateConnectorID(connectors []storage.Connector, connectorID string) bool {
130160
for _, c := range connectors {
131161
if c.ID == connectorID {

server/authflow/request_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,77 @@ func TestParseAuthorizationRequest(t *testing.T) {
169169
"scope": "openid email profile",
170170
},
171171
},
172+
{
173+
name: "regexp url",
174+
clients: []storage.Client{
175+
{
176+
ID: "bar",
177+
InsecureAllowRegexpRedirectURIs: true,
178+
RedirectURIs: []string{`https://pr-(\d+).example.com`},
179+
},
180+
},
181+
supportedResponseTypes: []string{"code", "id_token", "token"},
182+
queryParams: map[string]string{
183+
"client_id": "bar",
184+
"redirect_uri": "https://pr-1010.example.com",
185+
"response_type": "code",
186+
"scope": "openid email profile",
187+
},
188+
},
189+
{
190+
name: "regexp url without flag",
191+
clients: []storage.Client{
192+
{
193+
ID: "bar",
194+
InsecureAllowRegexpRedirectURIs: false,
195+
RedirectURIs: []string{`https://pr-(\d+).example.com`},
196+
},
197+
},
198+
supportedResponseTypes: []string{"code", "id_token", "token"},
199+
queryParams: map[string]string{
200+
"client_id": "bar",
201+
"redirect_uri": "https://pr-1010.example.com",
202+
"response_type": "code",
203+
"scope": "openid email profile",
204+
},
205+
expectedError: &displayedAuthErr{Status: http.StatusBadRequest},
206+
},
207+
{
208+
name: "wildcard url",
209+
clients: []storage.Client{
210+
{
211+
ID: "bar",
212+
InsecureAllowRegexpRedirectURIs: true,
213+
InsecureAllowWildcardRedirectURIs: true,
214+
RedirectURIs: []string{`https?://.*`},
215+
},
216+
},
217+
supportedResponseTypes: []string{"code", "id_token", "token"},
218+
queryParams: map[string]string{
219+
"client_id": "bar",
220+
"redirect_uri": "https://example.com",
221+
"response_type": "code",
222+
"scope": "openid email profile",
223+
},
224+
},
225+
{
226+
name: "wildcard url without flag",
227+
clients: []storage.Client{
228+
{
229+
ID: "bar",
230+
InsecureAllowRegexpRedirectURIs: true,
231+
RedirectURIs: []string{`https?://.*`},
232+
},
233+
},
234+
supportedResponseTypes: []string{"code", "id_token", "token"},
235+
queryParams: map[string]string{
236+
"client_id": "bar",
237+
"redirect_uri": "https://example.com",
238+
"response_type": "code",
239+
"scope": "openid email profile",
240+
},
241+
expectedError: &displayedAuthErr{Status: http.StatusBadRequest},
242+
},
172243
{
173244
name: "choose second connector_id",
174245
clients: []storage.Client{

storage/storage.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,17 @@ type Client struct {
166166
Secret string `json:"secret"`
167167
SecretEnv string `json:"secretEnv"`
168168

169+
// InsecureAllowRegexpRedirectURIs is an additiona flag allowing, add to
170+
// RedirectURIs regexp expressions for dynamic URIs.
171+
//
172+
// Note: The flag does not allow wildcard regexp like: ".*" or "https?://.*" or
173+
// any ".*" in the string, unless using InsecureAllowWildcardRedirectURIs flag.
174+
InsecureAllowRegexpRedirectURIs bool `json:"insecureAllowRegexpRedirectURIs"`
175+
176+
// InsecureAllowWildcardRedirectURIs in use with InsecureAllowRegexpRedirectURIs
177+
// allows to add wildcard regexp mainly for development purpose.
178+
InsecureAllowWildcardRedirectURIs bool `json:"insecureAllowWildcardRedirectURIs"`
179+
169180
// A registered set of redirect URIs. When redirecting from dex to the client, the URI
170181
// requested to redirect to MUST match one of these values, unless the client is "public".
171182
RedirectURIs []string `json:"redirectURIs"`

0 commit comments

Comments
 (0)