diff --git a/identity/clients/clients.go b/identity/clients/clients.go index ba25f5b..fe0b892 100644 --- a/identity/clients/clients.go +++ b/identity/clients/clients.go @@ -19,6 +19,7 @@ package clients import ( "crypto" + "net/url" ) // Details hold detail information about clients identified by ID. @@ -44,3 +45,19 @@ type Secured struct { Registration *ClientRegistration } + +// IsLocalNativeHTTPURI returns true if the provided URI qualifies to be used +// as http redirect URI for a native client. +func IsLocalNativeHTTPURI(uri *url.URL) bool { + if uri.Scheme != "http" { + return false + } + return IsLocalNativeHostURI(uri) +} + +// IsLocalNativeHostURI returns true if the provided URI hostname is considered +// as localhost for a native client. +func IsLocalNativeHostURI(uri *url.URL) bool { + hostname := uri.Hostname() + return hostname == "localhost" || hostname == "127.0.0.1" || hostname == "::1" +} diff --git a/identity/clients/registry.go b/identity/clients/registry.go index 53039d3..bd28073 100644 --- a/identity/clients/registry.go +++ b/identity/clients/registry.go @@ -146,8 +146,8 @@ func (r *Registry) Register(client *ClientRegistration) error { return fmt.Errorf("invalid redirect_uri %v - invalid or no hostname", urlString) } else if !client.Insecure && parsed.Scheme != "https" { return fmt.Errorf("invalid redirect_uri %v - make sure to use https when application_type is web", parsed) - } else if parsed.Host == "localhost" { - return fmt.Errorf("invalid redirect_uri %v - host must not be localhost", parsed) + } else if IsLocalNativeHTTPURI(parsed) { + return fmt.Errorf("invalid redirect_uri %v - host must not be localhost when application_type is web", parsed) } if len(client.Origins) == 0 { @@ -170,7 +170,7 @@ func (r *Registry) Register(client *ClientRegistration) error { return fmt.Errorf("invalid redirect_uri %v - invalid uri or no hostname", urlString) } else if parsed.Scheme == "https" { return fmt.Errorf("invalid redirect_uri %v - scheme must not be https when application_type is native", parsed) - } else if parsed.Scheme == "http" && parsed.Hostname() != "localhost" { + } else if parsed.Scheme == "http" && !IsLocalNativeHTTPURI(parsed) { return fmt.Errorf("invalid redirect_uri %v = http host must be localhost when application_type is native", parsed) } } @@ -209,17 +209,16 @@ func (r *Registry) Validate(client *ClientRegistration, clientSecret string, red // Make sure to validate the redirect URI unless client is marked insecure // and has no configured redirect URIs. redirectURIOK := false - for _, urlString := range client.RedirectURIs { + for _, registeredURIString := range client.RedirectURIs { if client.ApplicationType == oidc.ApplicationTypeNative { - urlStringParsed, _ := url.Parse(urlString) - if urlStringParsed.Host == "localhost" && urlStringParsed.Scheme == "http" { - u, err := url.Parse(redirectURIString) + registeredURI, _ := url.Parse(registeredURIString) + if IsLocalNativeHTTPURI(registeredURI) { + redirectURI, err := url.Parse(redirectURIString) if err != nil { break } - host := u.Hostname() - if host == "localhost" && u.Scheme == "http" { - if urlStringParsed.Path == "" || u.Path == urlStringParsed.Path { + if IsLocalNativeHTTPURI(redirectURI) { + if registeredURI.Path == "" || redirectURI.Path == registeredURI.Path { redirectURIOK = true break } @@ -227,7 +226,7 @@ func (r *Registry) Validate(client *ClientRegistration, clientSecret string, red continue } } - if urlString == redirectURIString { + if registeredURIString == redirectURIString { redirectURIOK = true break } diff --git a/identity/clients/registry_test.go b/identity/clients/registry_test.go index 9e056c9..b04af75 100644 --- a/identity/clients/registry_test.go +++ b/identity/clients/registry_test.go @@ -12,6 +12,9 @@ func TestRedirectUriWithDynamicPort(t *testing.T) { }{ {"http://localhost:12345", false}, {"http://localhost:12345/callback", false}, + {"http://127.0.0.1:12345/callback", false}, + {"http://192.168.88.4:8080/callback", true}, + {"http://[::1]:12345/callback", false}, {"http://localhost", false}, {"custom://callback.example.net", false}, {"http://localhost:12345/other-callback", false}, @@ -50,6 +53,8 @@ func TestRedirectUriWithSpecificPath(t *testing.T) { }{ {"http://localhost:12345", true}, {"http://localhost:12345/callback", false}, + {"http://127.0.0.1:12345/callback", false}, + {"http://[::1]:12345/callback", false}, {"http://localhost", true}, {"custom://callback.example.net", true}, {"http://localhost:12345/callback-disallowed", true}, @@ -58,6 +63,9 @@ func TestRedirectUriWithSpecificPath(t *testing.T) { {"http://host-with-port:123/callback", true}, {"https://localhost:123/callback", true}, {"http://localhost/other-callback", false}, + {"http://127.0.0.1/other-callback", false}, + {"http://10.0.0.1/other-callback", true}, + {"http://[::1]/other-callback", false}, {"http://localhost:8080/other-callback", false}, } diff --git a/oidc/payload/registration.go b/oidc/payload/registration.go index ce6e243..c69681a 100644 --- a/oidc/payload/registration.go +++ b/oidc/payload/registration.go @@ -173,10 +173,10 @@ func (crr *ClientRegistrationRequest) Validate() error { } if ok := registeredGrantTypes[oidc.GrantTypeImplicit]; ok { if uri.Scheme != "https" { - return konnectoidc.NewOAuth2Error(oidc.ErrorCodeOIDCInvalidRedirectURI, "implicit web clients must use https redirect_uris") + return konnectoidc.NewOAuth2Error(oidc.ErrorCodeOIDCInvalidRedirectURI, "web clients must use https redirect_uris") } - if uri.Hostname() == "localhost" { - return konnectoidc.NewOAuth2Error(oidc.ErrorCodeOIDCInvalidRedirectURI, "implicit web clients must not use localhost redirect_uris") + if clients.IsLocalNativeHostURI(uri) { + return konnectoidc.NewOAuth2Error(oidc.ErrorCodeOIDCInvalidRedirectURI, "web clients must not use localhost redirect_uris") } } } @@ -189,10 +189,9 @@ func (crr *ClientRegistrationRequest) Validate() error { if err != nil { return konnectoidc.NewOAuth2Error(oidc.ErrorCodeOIDCInvalidRedirectURI, "failed to parse redirect_uris") } - if uri.Scheme == "http" { - if uri.Hostname() != "localhost" { - return konnectoidc.NewOAuth2Error(oidc.ErrorCodeOIDCInvalidRedirectURI, "native clients must only use localhost redirect_uris with http") - } + + if !clients.IsLocalNativeHTTPURI(uri) { + return konnectoidc.NewOAuth2Error(oidc.ErrorCodeOIDCInvalidRedirectURI, "native clients must only use localhost redirect_uris with http") } }