9
9
"flag"
10
10
"fmt"
11
11
"net/http"
12
+ "net/url"
12
13
"reflect"
13
14
"strings"
14
15
"time"
@@ -26,6 +27,61 @@ import (
26
27
27
28
var Validate * validator.Validate
28
29
30
+ // isValidOAuth2RedirectURI validates OAuth2 redirect URIs according to RFC 6749.
31
+ // It requires a proper scheme and host, rejecting malformed URIs that would be
32
+ // problematic for OAuth2 flows.
33
+ func isValidOAuth2RedirectURI (uri string ) bool {
34
+ if uri == "" {
35
+ return false
36
+ }
37
+
38
+ parsed , err := url .Parse (uri )
39
+ if err != nil {
40
+ return false
41
+ }
42
+
43
+ // Must have a scheme
44
+ if parsed .Scheme == "" {
45
+ return false
46
+ }
47
+
48
+ // Reject patterns that look like "host:port" without proper scheme
49
+ // These get parsed as scheme="host" and path="port" which is ambiguous
50
+ if parsed .Host == "" && parsed .Path != "" && ! strings .HasPrefix (uri , parsed .Scheme + "://" ) {
51
+ // Check if this looks like a host:port pattern (contains digits after colon)
52
+ if strings .Contains (parsed .Path , ":" ) {
53
+ return false
54
+ }
55
+ // Also reject if the "scheme" part looks like a hostname
56
+ if strings .Contains (parsed .Scheme , "." ) || parsed .Scheme == "localhost" {
57
+ return false
58
+ }
59
+ }
60
+
61
+ // For standard schemes (http/https), host is required
62
+ if parsed .Scheme == "http" || parsed .Scheme == "https" {
63
+ if parsed .Host == "" {
64
+ return false
65
+ }
66
+ }
67
+
68
+ // Reject scheme-only URIs like "http://"
69
+ if parsed .Host == "" && parsed .Path == "" {
70
+ return false
71
+ }
72
+
73
+ // For custom schemes, we allow no host (like "myapp://callback")
74
+ // But if there's a host, it should be valid
75
+ if parsed .Host != "" {
76
+ // Basic host validation - should not be empty after parsing
77
+ if strings .TrimSpace (parsed .Host ) == "" {
78
+ return false
79
+ }
80
+ }
81
+
82
+ return true
83
+ }
84
+
29
85
// This init is used to create a validator and register validation-specific
30
86
// functionality for the HTTP API.
31
87
//
@@ -113,6 +169,19 @@ func init() {
113
169
if err != nil {
114
170
panic (err )
115
171
}
172
+
173
+ oauth2RedirectURIValidator := func (fl validator.FieldLevel ) bool {
174
+ f := fl .Field ().Interface ()
175
+ str , ok := f .(string )
176
+ if ! ok {
177
+ return false
178
+ }
179
+ return isValidOAuth2RedirectURI (str )
180
+ }
181
+ err = Validate .RegisterValidation ("oauth2_redirect_uri" , oauth2RedirectURIValidator )
182
+ if err != nil {
183
+ panic (err )
184
+ }
116
185
}
117
186
118
187
// Is404Error returns true if the given error should return a 404 status code.
0 commit comments