Repository: ory/kratos
Pull request #1025 (2021-01-31 06:39:13)

fix: give specific message instead of only 404 when method disabled
## Related issue

#954 

## Proposed changes

Return 400 with message `This endpoint was disabled by system administrator. Please check your url or contact the system administrator to enable it.` when user requests endpoints belonging to disabled methods. 

## Checklist

<!--
Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of
them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.
-->

- [x] I have read the [contributing guidelines](../blob/master/CONTRIBUTING.md).
- [x] I have read the [security policy](../security/policy).
- [x] I confirm that this pull request does not address a security
      vulnerability. If this pull request addresses a security. vulnerability, I
      confirm that I got green light (please contact
      [security@ory.sh](mailto:security@ory.sh)) from the maintainers to push
      the changes.
- [x] I have added tests that prove my fix is effective or that my feature
      works.
- [x] I have added or changed [the documentation](docs/docs).




Patch:
diff --git a/driver/registry_default.go b/driver/registry_default.go
index 8c806ea02..f879a77e6 100644
--- a/driver/registry_default.go
+++ b/driver/registry_default.go
@@ -144,15 +144,15 @@ func (m *RegistryDefault) RegisterPublicRoutes(router *x.RouterPublic) {
 	m.RegistrationHandler().RegisterPublicRoutes(router)
 	m.LogoutHandler().RegisterPublicRoutes(router)
 	m.SettingsHandler().RegisterPublicRoutes(router)
-	m.LoginStrategies().RegisterPublicRoutes(router)
-	m.SettingsStrategies().RegisterPublicRoutes(router)
-	m.RegistrationStrategies().RegisterPublicRoutes(router)
+	m.AllLoginStrategies().RegisterPublicRoutes(router)
+	m.AllSettingsStrategies().RegisterPublicRoutes(router)
+	m.AllRegistrationStrategies().RegisterPublicRoutes(router)
 	m.SessionHandler().RegisterPublicRoutes(router)
 	m.SelfServiceErrorHandler().RegisterPublicRoutes(router)
 	m.SchemaHandler().RegisterPublicRoutes(router)
 
 	if m.c.SelfServiceFlowRecoveryEnabled() {
-		m.RecoveryStrategies().RegisterPublicRoutes(router)
+		m.AllRecoveryStrategies().RegisterPublicRoutes(router)
 		m.RecoveryHandler().RegisterPublicRoutes(router)
 	}
 
@@ -173,7 +173,7 @@ func (m *RegistryDefault) RegisterAdminRoutes(router *x.RouterAdmin) {
 
 	if m.c.SelfServiceFlowRecoveryEnabled() {
 		m.RecoveryHandler().RegisterAdminRoutes(router)
-		m.RecoveryStrategies().RegisterAdminRoutes(router)
+		m.AllRecoveryStrategies().RegisterAdminRoutes(router)
 	}
 
 	m.VerificationHandler().RegisterAdminRoutes(router)
@@ -265,6 +265,17 @@ func (m *RegistryDefault) RegistrationStrategies() registration.Strategies {
 	return m.registrationStrategies
 }
 
+func (m *RegistryDefault) AllRegistrationStrategies() registration.Strategies {
+	var registrationStrategies []registration.Strategy
+
+	for _, strategy := range m.selfServiceStrategies() {
+		if s, ok := strategy.(registration.Strategy); ok {
+			registrationStrategies = append(registrationStrategies, s)
+		}
+	}
+	return registrationStrategies
+}
+
 func (m *RegistryDefault) LoginStrategies() login.Strategies {
 	if len(m.loginStrategies) == 0 {
 		for _, strategy := range m.selfServiceStrategies() {
@@ -278,6 +289,16 @@ func (m *RegistryDefault) LoginStrategies() login.Strategies {
 	return m.loginStrategies
 }
 
+func (m *RegistryDefault) AllLoginStrategies() login.Strategies {
+	var loginStrategies []login.Strategy
+	for _, strategy := range m.selfServiceStrategies() {
+		if s, ok := strategy.(login.Strategy); ok {
+			loginStrategies = append(loginStrategies, s)
+		}
+	}
+	return loginStrategies
+}
+
 func (m *RegistryDefault) VerificationStrategies() verification.Strategies {
 	if len(m.verificationStrategies) == 0 {
 		for _, strategy := range m.selfServiceStrategies() {
diff --git a/driver/registry_default_recovery.go b/driver/registry_default_recovery.go
index 6fcb25dce..a86955dde 100644
--- a/driver/registry_default_recovery.go
+++ b/driver/registry_default_recovery.go
@@ -32,3 +32,13 @@ func (m *RegistryDefault) RecoveryStrategies() recovery.Strategies {
 	}
 	return m.recoveryStrategies
 }
+
+func (m *RegistryDefault) AllRecoveryStrategies() recovery.Strategies {
+	var recoveryStrategies []recovery.Strategy
+	for _, strategy := range m.selfServiceStrategies() {
+		if s, ok := strategy.(recovery.Strategy); ok {
+			recoveryStrategies = append(recoveryStrategies, s)
+		}
+	}
+	return recoveryStrategies
+}
diff --git a/driver/registry_default_settings.go b/driver/registry_default_settings.go
index f1a137fd3..ca3cfa41a 100644
--- a/driver/registry_default_settings.go
+++ b/driver/registry_default_settings.go
@@ -57,3 +57,13 @@ func (m *RegistryDefault) SettingsStrategies() settings.Strategies {
 	}
 	return m.profileStrategies
 }
+
+func (m *RegistryDefault) AllSettingsStrategies() settings.Strategies {
+	var profileStrategies []settings.Strategy
+	for _, strategy := range m.selfServiceStrategies() {
+		if s, ok := strategy.(settings.Strategy); ok {
+			profileStrategies = append(profileStrategies, s)
+		}
+	}
+	return profileStrategies
+}
diff --git a/selfservice/strategy/handler.go b/selfservice/strategy/handler.go
new file mode 100644
index 000000000..b73f60778
--- /dev/null
+++ b/selfservice/strategy/handler.go
@@ -0,0 +1,25 @@
+package strategy
+
+import (
+	"github.com/julienschmidt/httprouter"
+	"github.com/ory/herodot"
+	"github.com/ory/kratos/driver/config"
+	"github.com/ory/kratos/x"
+	"net/http"
+)
+
+const EndpointDisabledMessage = "This endpoint was disabled by system administrator. Please check your url or contact the system administrator to enable it."
+
+func IsDisabled(c interface {
+	config.Provider
+	x.WriterProvider
+}, strategy string, wrap httprouter.Handle) httprouter.Handle {
+	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+		enabled := c.Config(r.Context()).SelfServiceStrategy(strategy).Enabled
+		if !enabled {
+			c.Writer().WriteError(w, r, herodot.ErrNotFound.WithReason(EndpointDisabledMessage))
+			return
+		}
+		wrap(w, r, ps)
+	}
+}
diff --git a/selfservice/strategy/link/strategy.go b/selfservice/strategy/link/strategy.go
index d27e8c4d1..81e8337a6 100644
--- a/selfservice/strategy/link/strategy.go
+++ b/selfservice/strategy/link/strategy.go
@@ -1,10 +1,8 @@
 package link
 
 import (
-	"github.com/ory/kratos/driver/config"
-	"github.com/ory/x/decoderx"
-
 	"github.com/ory/kratos/courier"
+	"github.com/ory/kratos/driver/config"
 	"github.com/ory/kratos/identity"
 	"github.com/ory/kratos/schema"
 	"github.com/ory/kratos/selfservice/errorx"
@@ -14,6 +12,7 @@ import (
 	"github.com/ory/kratos/selfservice/form"
 	"github.com/ory/kratos/session"
 	"github.com/ory/kratos/x"
+	"github.com/ory/x/decoderx"
 )
 
 var _ recovery.Strategy = new(Strategy)
diff --git a/selfservice/strategy/link/strategy_recovery.go b/selfservice/strategy/link/strategy_recovery.go
index d2c8f5c14..c18f231d8 100644
--- a/selfservice/strategy/link/strategy_recovery.go
+++ b/selfservice/strategy/link/strategy_recovery.go
@@ -25,6 +25,7 @@ import (
 	"github.com/ory/kratos/selfservice/flow"
 	"github.com/ory/kratos/selfservice/flow/recovery"
 	"github.com/ory/kratos/selfservice/form"
+	"github.com/ory/kratos/selfservice/strategy"
 	"github.com/ory/kratos/session"
 	"github.com/ory/kratos/text"
 	"github.com/ory/kratos/x"
@@ -41,12 +42,16 @@ func (s *Strategy) RecoveryStrategyID() string {
 
 func (s *Strategy) RegisterPublicRecoveryRoutes(public *x.RouterPublic) {
 	redirect := session.RedirectOnAuthenticated(s.d)
-	public.GET(RouteRecovery, s.d.SessionHandler().IsNotAuthenticated(s.handleRecovery, redirect))
-	public.POST(RouteRecovery, s.d.SessionHandler().IsNotAuthenticated(s.handleRecovery, redirect))
+
+	wrappedHandleRecovery := strategy.IsDisabled(s.d, s.RecoveryStrategyID(), s.handleRecovery)
+	public.GET(RouteRecovery, s.d.SessionHandler().IsNotAuthenticated(wrappedHandleRecovery, redirect))
+
+	public.POST(RouteRecovery, s.d.SessionHandler().IsNotAuthenticated(wrappedHandleRecovery, redirect))
 }
 
 func (s *Strategy) RegisterAdminRecoveryRoutes(admin *x.RouterAdmin) {
-	admin.POST(RouteAdminCreateRecoveryLink, s.createRecoveryLink)
+	wrappedCreateRecoveryLink := strategy.IsDisabled(s.d, s.RecoveryStrategyID(), s.createRecoveryLink)
+	admin.POST(RouteAdminCreateRecoveryLink, wrappedCreateRecoveryLink)
 }
 
 func (s *Strategy) PopulateRecoveryMethod(r *http.Request, req *recovery.Flow) error {
diff --git a/selfservice/strategy/oidc/strategy.go b/selfservice/strategy/oidc/strategy.go
index bf2b5819a..553a20c20 100644
--- a/selfservice/strategy/oidc/strategy.go
+++ b/selfservice/strategy/oidc/strategy.go
@@ -32,6 +32,7 @@ import (
 	"github.com/ory/kratos/selfservice/flow/registration"
 	"github.com/ory/kratos/selfservice/flow/settings"
 	"github.com/ory/kratos/selfservice/form"
+	"github.com/ory/kratos/selfservice/strategy"
 	"github.com/ory/kratos/session"
 	"github.com/ory/kratos/x"
 )
@@ -53,6 +54,7 @@ type dependencies interface {
 	x.LoggingProvider
 	x.CookieProvider
 	x.CSRFTokenGeneratorProvider
+	x.WriterProvider
 
 	identity.ValidationProvider
 	identity.PrivilegedPoolProvider
@@ -89,8 +91,8 @@ func isForced(req interface{}) bool {
 	return ok && f.IsForced()
 }
 
-// Strategy implements selfservice.LoginStrategy, selfservice.RegistrationStrategy. It supports both login
-// and registration via OpenID Providers.
+// Strategy implements selfservice.LoginStrategy, selfservice.RegistrationStrategy and selfservice.SettingsStrategy.
+// It supports login, registration and settings via OpenID Providers.
 type Strategy struct {
 	d         dependencies
 	f         *fetcher.Fetcher
@@ -129,16 +131,18 @@ func (s *Strategy) CountActiveCredentials(cc map[identity.CredentialsType]identi
 }
 
 func (s *Strategy) setRoutes(r *x.RouterPublic) {
+	wrappedHandleCallback := strategy.IsDisabled(s.d, s.ID().String(), s.handleCallback)
 	if handle, _, _ := r.Lookup("GET", RouteCallback); handle == nil {
-		r.GET(RouteCallback, s.handleCallback)
+		r.GET(RouteCallback, wrappedHandleCallback)
 	}
 
+	wrappedHandleAuth := strategy.IsDisabled(s.d, s.ID().String(), s.handleAuth)
 	if handle, _, _ := r.Lookup("POST", RouteAuth); handle == nil {
-		r.POST(RouteAuth, s.handleAuth)
+		r.POST(RouteAuth, wrappedHandleAuth)
 	}
 
 	if handle, _, _ := r.Lookup("GET", RouteAuth); handle == nil {
-		r.GET(RouteAuth, s.handleAuth)
+		r.GET(RouteAuth, wrappedHandleAuth)
 	}
 }
 
diff --git a/selfservice/strategy/oidc/strategy_settings.go b/selfservice/strategy/oidc/strategy_settings.go
index d0753efd6..52712a640 100644
--- a/selfservice/strategy/oidc/strategy_settings.go
+++ b/selfservice/strategy/oidc/strategy_settings.go
@@ -20,6 +20,7 @@ import (
 	"github.com/ory/kratos/selfservice/flow"
 	"github.com/ory/kratos/selfservice/flow/settings"
 	"github.com/ory/kratos/selfservice/form"
+	"github.com/ory/kratos/selfservice/strategy"
 	"github.com/ory/kratos/x"
 )
 
@@ -34,8 +35,9 @@ var ConnectionExistValidationError = &jsonschema.ValidationError{
 	Message: "can not link unknown or already existing OpenID Connect connection", InstancePtr: "#/"}
 
 func (s *Strategy) RegisterSettingsRoutes(router *x.RouterPublic) {
-	router.POST(SettingsPath, s.completeSettingsFlow)
-	router.GET(SettingsPath, s.completeSettingsFlow)
+	wrappedCompleteSettingsFlow := strategy.IsDisabled(s.d, s.SettingsStrategyID(), s.completeSettingsFlow)
+	router.POST(SettingsPath, wrappedCompleteSettingsFlow)
+	router.GET(SettingsPath, wrappedCompleteSettingsFlow)
 }
 
 func (s *Strategy) SettingsStrategyID() string {
diff --git a/selfservice/strategy/password/login.go b/selfservice/strategy/password/login.go
index f8729bd17..b786be597 100644
--- a/selfservice/strategy/password/login.go
+++ b/selfservice/strategy/password/login.go
@@ -21,6 +21,7 @@ import (
 	"github.com/ory/kratos/selfservice/flow"
 	"github.com/ory/kratos/selfservice/flow/login"
 	"github.com/ory/kratos/selfservice/form"
+	"github.com/ory/kratos/selfservice/strategy"
 	"github.com/ory/kratos/x"
 )
 
@@ -31,7 +32,8 @@ const (
 func (s *Strategy) RegisterLoginRoutes(r *x.RouterPublic) {
 	s.d.CSRFHandler().IgnorePath(RouteLogin)
 
-	r.POST(RouteLogin, s.handleLogin)
+	wrappedHandleLogin := strategy.IsDisabled(s.d, s.ID().String(), s.handleLogin)
+	r.POST(RouteLogin, wrappedHandleLogin)
 }
 
 func (s *Strategy) handleLoginError(w http.ResponseWriter, r *http.Request, rr *login.Flow, payload *CompleteSelfServiceLoginFlowWithPasswordMethod, err error) {
diff --git a/selfservice/strategy/password/registration.go b/selfservice/strategy/password/registration.go
index 854dc3ac1..285ba03fb 100644
--- a/selfservice/strategy/password/registration.go
+++ b/selfservice/strategy/password/registration.go
@@ -27,6 +27,7 @@ import (
 	"github.com/ory/kratos/schema"
 	"github.com/ory/kratos/selfservice/flow/registration"
 	"github.com/ory/kratos/selfservice/form"
+	"github.com/ory/kratos/selfservice/strategy"
 	"github.com/ory/kratos/x"
 )
 
@@ -43,7 +44,8 @@ type RegistrationFormPayload struct {
 func (s *Strategy) RegisterRegistrationRoutes(public *x.RouterPublic) {
 	s.d.CSRFHandler().IgnorePath(RouteRegistration)
 
-	public.POST(RouteRegistration, s.d.SessionHandler().IsNotAuthenticated(s.handleRegistration, func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	wrappedHandleRegistration := strategy.IsDisabled(s.d, s.ID().String(), s.handleRegistration)
+	public.POST(RouteRegistration, s.d.SessionHandler().IsNotAuthenticated(wrappedHandleRegistration, func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
 		handler := session.RedirectOnAuthenticated(s.d)
 		if x.IsJSONRequest(r) {
 			handler = session.RespondWithJSONErrorOnAuthenticated(s.d.Writer(), registration.ErrAlreadyLoggedIn)
diff --git a/selfservice/strategy/password/settings.go b/selfservice/strategy/password/settings.go
index 7449a5aab..ca1f7651a 100644
--- a/selfservice/strategy/password/settings.go
+++ b/selfservice/strategy/password/settings.go
@@ -22,6 +22,7 @@ import (
 	"github.com/ory/kratos/selfservice/flow"
 	"github.com/ory/kratos/selfservice/flow/settings"
 	"github.com/ory/kratos/selfservice/form"
+	"github.com/ory/kratos/selfservice/strategy"
 	"github.com/ory/kratos/x"
 )
 
@@ -32,8 +33,9 @@ const (
 func (s *Strategy) RegisterSettingsRoutes(router *x.RouterPublic) {
 	s.d.CSRFHandler().IgnorePath(RouteSettings)
 
-	router.POST(RouteSettings, s.submitSettingsFlow)
-	router.GET(RouteSettings, s.submitSettingsFlow)
+	wrappedSubmmitSettingsFlow := strategy.IsDisabled(s.d, s.SettingsStrategyID(), s.submitSettingsFlow)
+	router.POST(RouteSettings, wrappedSubmmitSettingsFlow)
+	router.GET(RouteSettings, wrappedSubmmitSettingsFlow)
 }
 
 func (s *Strategy) SettingsStrategyID() string {
diff --git a/selfservice/strategy/profile/strategy.go b/selfservice/strategy/profile/strategy.go
index 8b576d14f..5bf374641 100644
--- a/selfservice/strategy/profile/strategy.go
+++ b/selfservice/strategy/profile/strategy.go
@@ -27,6 +27,7 @@ import (
 	"github.com/ory/kratos/selfservice/flow"
 	"github.com/ory/kratos/selfservice/flow/settings"
 	"github.com/ory/kratos/selfservice/form"
+	"github.com/ory/kratos/selfservice/strategy"
 	"github.com/ory/kratos/session"
 	"github.com/ory/kratos/x"
 )
@@ -87,8 +88,9 @@ func (s *Strategy) SettingsStrategyID() string {
 func (s *Strategy) RegisterSettingsRoutes(public *x.RouterPublic) {
 	s.d.CSRFHandler().IgnorePath(RouteSettings)
 
-	public.POST(RouteSettings, s.d.SessionHandler().IsAuthenticated(s.handleSubmit, settings.OnUnauthenticated(s.d)))
-	public.GET(RouteSettings, s.d.SessionHandler().IsAuthenticated(s.handleSubmit, settings.OnUnauthenticated(s.d)))
+	wrappedHandleSubmit := strategy.IsDisabled(s.d, s.SettingsStrategyID(), s.handleSubmit)
+	public.POST(RouteSettings, s.d.SessionHandler().IsAuthenticated(wrappedHandleSubmit, settings.OnUnauthenticated(s.d)))
+	public.GET(RouteSettings, s.d.SessionHandler().IsAuthenticated(wrappedHandleSubmit, settings.OnUnauthenticated(s.d)))
 }
 
 func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity, pr *settings.Flow) error {


Test patch:
diff --git a/driver/registry_default_test.go b/driver/registry_default_test.go
index e8611893d..d29341401 100644
--- a/driver/registry_default_test.go
+++ b/driver/registry_default_test.go
@@ -189,3 +189,43 @@ func TestDriverDefault_Strategies(t *testing.T) {
 		}
 	})
 }
+
+func TestDefaultRegistry_AllStrategies(t *testing.T) {
+	_, reg := internal.NewFastRegistryWithMocks(t)
+
+	t.Run("case=all login strategies", func(t *testing.T) {
+		expects := []string{"password", "oidc"}
+		s := reg.AllLoginStrategies()
+		require.Len(t, s, len(expects))
+		for k, e := range expects {
+			assert.Equal(t, e, s[k].ID().String())
+		}
+	})
+
+	t.Run("case=all registration strategies", func(t *testing.T) {
+		expects := []string{"password", "oidc"}
+		s := reg.AllRegistrationStrategies()
+		require.Len(t, s, len(expects))
+		for k, e := range expects {
+			assert.Equal(t, e, s[k].ID().String())
+		}
+	})
+
+	t.Run("case=all settings strategies", func(t *testing.T) {
+		expects := []string{"password", "oidc", "profile"}
+		s := reg.AllSettingsStrategies()
+		require.Len(t, s, len(expects))
+		for k, e := range expects {
+			assert.Equal(t, e, s[k].SettingsStrategyID())
+		}
+	})
+
+	t.Run("case=all recovery strategies", func(t *testing.T) {
+		expects := []string{"link"}
+		s := reg.AllRecoveryStrategies()
+		require.Len(t, s, len(expects))
+		for k, e := range expects {
+			assert.Equal(t, e, s[k].RecoveryStrategyID())
+		}
+	})
+}
diff --git a/selfservice/strategy/link/strategy_recovery_test.go b/selfservice/strategy/link/strategy_recovery_test.go
index 4dd213fa8..d00ce5309 100644
--- a/selfservice/strategy/link/strategy_recovery_test.go
+++ b/selfservice/strategy/link/strategy_recovery_test.go
@@ -130,6 +130,7 @@ func TestAdminStrategy(t *testing.T) {
 		require.Len(t, sr.Payload.Messages, 1)
 		assert.Equal(t, "You successfully recovered your account. Please change your password or set up an alternative login method (e.g. social sign in) within the next 60.00 minutes.", sr.Payload.Messages[0].Text)
 	})
+
 }
 
 func TestRecovery(t *testing.T) {
@@ -344,3 +345,55 @@ func TestRecovery(t *testing.T) {
 		assert.Contains(t, sr.Payload.Messages[0].Text, "The recovery flow expired")
 	})
 }
+
+func TestDisabledEndpoint(t *testing.T) {
+	conf, reg := internal.NewFastRegistryWithMocks(t)
+	initViper(t, conf)
+	conf.MustSet(config.ViperKeySelfServiceStrategyConfig+"."+recovery.StrategyRecoveryLinkName+".enabled", false)
+
+	publicTS, adminTS := testhelpers.NewKratosServer(t, reg)
+	adminSDK := testhelpers.NewSDKClient(adminTS)
+
+	t.Run("role=admin", func(t *testing.T) {
+		t.Run("description=can not create recovery link when link method is disabled", func(t *testing.T) {
+			id := identity.Identity{Traits: identity.Traits(`{"email":"recovery-endpoint-disabled@ory.sh"}`)}
+
+			require.NoError(t, reg.IdentityManager().Create(context.Background(),
+				&id, identity.ManagerAllowWriteProtectedTraits))
+
+			uuid := models.UUID(id.ID.String())
+			rl, err := adminSDK.Admin.CreateRecoveryLink(admin.NewCreateRecoveryLinkParams().
+				WithBody(&models.CreateRecoveryLink{IdentityID: &uuid}))
+			assert.Nil(t, rl)
+			require.IsType(t, new(admin.CreateRecoveryLinkNotFound), err, "%s", err)
+
+			br, _ := err.(*admin.CreateRecoveryLinkNotFound)
+			assert.Contains(t, br.Payload.Error.Reason, "This endpoint was disabled by system administrator")
+		})
+	})
+
+	t.Run("role=public", func(t *testing.T) {
+		c := testhelpers.NewClientWithCookies(t)
+		t.Run("description=can not recover an account by get request when link method is disabled", func(t *testing.T) {
+			u := publicTS.URL + link.RouteRecovery + "?token=endpoint-disabled"
+			res, err := c.Get(u)
+			require.NoError(t, err)
+			assert.Equal(t, http.StatusNotFound, res.StatusCode)
+
+			b := make([]byte, res.ContentLength)
+			_, _ = res.Body.Read(b)
+			assert.Contains(t, string(b), "This endpoint was disabled by system administrator")
+		})
+
+		t.Run("description=can not recover an account by post request when link method is disabled", func(t *testing.T) {
+			u := publicTS.URL + link.RouteRecovery
+			res, err := c.PostForm(u, url.Values{"email": {"email@ory.sh"}})
+			require.NoError(t, err)
+			assert.Equal(t, http.StatusNotFound, res.StatusCode)
+
+			b := make([]byte, res.ContentLength)
+			_, _ = res.Body.Read(b)
+			assert.Contains(t, string(b), "This endpoint was disabled by system administrator")
+		})
+	})
+}
diff --git a/selfservice/strategy/oidc/strategy_settings_test.go b/selfservice/strategy/oidc/strategy_settings_test.go
index 0ceea2ca5..ee4805a9f 100644
--- a/selfservice/strategy/oidc/strategy_settings_test.go
+++ b/selfservice/strategy/oidc/strategy_settings_test.go
@@ -676,3 +676,35 @@ func TestPopulateSettingsMethod(t *testing.T) {
 		})
 	}
 }
+
+func TestDisabledSettingsEndpoint(t *testing.T) {
+	conf, reg := internal.NewFastRegistryWithMocks(t)
+	testhelpers.StrategyEnable(t, conf, identity.CredentialsTypeOIDC.String(), false)
+
+	publicTS, _ := testhelpers.NewKratosServer(t, reg)
+
+	t.Run("case=should not complete settings oidc method is disabled", func(t *testing.T) {
+		c := testhelpers.NewClientWithCookies(t)
+
+		t.Run("method=GET", func(t *testing.T) {
+			res, err := c.Get(publicTS.URL + oidc.SettingsPath)
+			require.NoError(t, err)
+			assert.Equal(t, http.StatusNotFound, res.StatusCode)
+
+			b := make([]byte, res.ContentLength)
+			_, _ = res.Body.Read(b)
+			assert.Contains(t, string(b), "This endpoint was disabled by system administrator")
+		})
+
+		t.Run("method=POST", func(t *testing.T) {
+			res, err := c.PostForm(publicTS.URL+oidc.SettingsPath, url.Values{"link": {"xxx"}})
+			require.NoError(t, err)
+			assert.Equal(t, http.StatusNotFound, res.StatusCode)
+
+			b := make([]byte, res.ContentLength)
+			_, _ = res.Body.Read(b)
+			assert.Contains(t, string(b), "This endpoint was disabled by system administrator")
+		})
+	})
+
+}
diff --git a/selfservice/strategy/oidc/strategy_test.go b/selfservice/strategy/oidc/strategy_test.go
index 95672f5ae..6774a92ea 100644
--- a/selfservice/strategy/oidc/strategy_test.go
+++ b/selfservice/strategy/oidc/strategy_test.go
@@ -581,3 +581,45 @@ func TestCountActiveCredentials(t *testing.T) {
 		})
 	}
 }
+
+func TestDisabledEndpoint(t *testing.T) {
+	conf, reg := internal.NewFastRegistryWithMocks(t)
+	testhelpers.StrategyEnable(t, conf, identity.CredentialsTypeOIDC.String(), false)
+
+	publicTS, _ := testhelpers.NewKratosServer(t, reg)
+
+	t.Run("case=should not callback when oidc method is disabled", func(t *testing.T) {
+		c := testhelpers.NewClientWithCookies(t)
+		res, err := c.Get(publicTS.URL + oidc.RouteCallback)
+		require.NoError(t, err)
+		assert.Equal(t, http.StatusNotFound, res.StatusCode)
+
+		b := make([]byte, res.ContentLength)
+		_, _ = res.Body.Read(b)
+		assert.Contains(t, string(b), "This endpoint was disabled by system administrator")
+	})
+
+	t.Run("case=should not auth when oidc method is disabled", func(t *testing.T) {
+		c := testhelpers.NewClientWithCookies(t)
+
+		t.Run("method=GET", func(t *testing.T) {
+			res, err := c.Get(publicTS.URL + oidc.RouteAuth)
+			require.NoError(t, err)
+			assert.Equal(t, http.StatusNotFound, res.StatusCode)
+
+			b := make([]byte, res.ContentLength)
+			_, _ = res.Body.Read(b)
+			assert.Contains(t, string(b), "This endpoint was disabled by system administrator")
+		})
+
+		t.Run("method=POST", func(t *testing.T) {
+			res, err := c.PostForm(publicTS.URL+oidc.RouteAuth, url.Values{"provider": {"github"}})
+			require.NoError(t, err)
+			assert.Equal(t, http.StatusNotFound, res.StatusCode)
+
+			b := make([]byte, res.ContentLength)
+			_, _ = res.Body.Read(b)
+			assert.Contains(t, string(b), "This endpoint was disabled by system administrator")
+		})
+	})
+}
diff --git a/selfservice/strategy/password/strategy_test.go b/selfservice/strategy/password/strategy_test.go
index 77c434edb..7f8fdcf44 100644
--- a/selfservice/strategy/password/strategy_test.go
+++ b/selfservice/strategy/password/strategy_test.go
@@ -3,8 +3,10 @@ package password_test
 import (
 	"context"
 	"fmt"
+	"github.com/ory/kratos/internal/testhelpers"
 	"net/http"
 	"net/http/httptest"
+	"net/url"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -105,3 +107,55 @@ func TestCountActiveCredentials(t *testing.T) {
 		})
 	}
 }
+
+func TestDisabledEndpoint(t *testing.T) {
+	conf, reg := internal.NewFastRegistryWithMocks(t)
+	testhelpers.StrategyEnable(t, conf, identity.CredentialsTypePassword.String(), false)
+
+	publicTS, _ := testhelpers.NewKratosServer(t, reg)
+
+	c := testhelpers.NewClientWithCookies(t)
+	t.Run("case=should not login when password method is disabled", func(t *testing.T) {
+		res, err := c.PostForm(publicTS.URL+password.RouteLogin, url.Values{"identifier": []string{"identifier"}, "password": []string{"password"}})
+		require.NoError(t, err)
+		assert.Equal(t, http.StatusNotFound, res.StatusCode)
+
+		b := make([]byte, res.ContentLength)
+		_, _ = res.Body.Read(b)
+		assert.Contains(t, string(b), "This endpoint was disabled by system administrator")
+	})
+
+	t.Run("case=should not registration when password method is disabled", func(t *testing.T) {
+
+		res, err := c.PostForm(publicTS.URL+password.RouteRegistration, url.Values{"identifier": []string{"identifier"}, "password": []string{"password"}})
+		require.NoError(t, err)
+		assert.Equal(t, http.StatusNotFound, res.StatusCode)
+
+		b := make([]byte, res.ContentLength)
+		_, _ = res.Body.Read(b)
+		assert.Contains(t, string(b), "This endpoint was disabled by system administrator")
+	})
+
+	t.Run("case=should not settings when password method is disabled", func(t *testing.T) {
+
+		t.Run("method=GET", func(t *testing.T) {
+			res, err := c.Get(publicTS.URL + password.RouteSettings)
+			require.NoError(t, err)
+			assert.Equal(t, http.StatusNotFound, res.StatusCode)
+
+			b := make([]byte, res.ContentLength)
+			_, _ = res.Body.Read(b)
+			assert.Contains(t, string(b), "This endpoint was disabled by system administrator")
+		})
+
+		t.Run("method=POST", func(t *testing.T) {
+			res, err := c.PostForm(publicTS.URL+password.RouteSettings, url.Values{"age": {"16"}})
+			require.NoError(t, err)
+			assert.Equal(t, http.StatusNotFound, res.StatusCode)
+
+			b := make([]byte, res.ContentLength)
+			_, _ = res.Body.Read(b)
+			assert.Contains(t, string(b), "This endpoint was disabled by system administrator")
+		})
+	})
+}
diff --git a/selfservice/strategy/profile/strategy_test.go b/selfservice/strategy/profile/strategy_test.go
index ca814f918..20e6d405a 100644
--- a/selfservice/strategy/profile/strategy_test.go
+++ b/selfservice/strategy/profile/strategy_test.go
@@ -495,3 +495,35 @@ func TestStrategyTraits(t *testing.T) {
 		})
 	})
 }
+func TestDisabledEndpoint(t *testing.T) {
+	conf, reg := internal.NewFastRegistryWithMocks(t)
+	conf.MustSet(config.ViperKeyDefaultIdentitySchemaURL, "file://./stub/identity.schema.json")
+	testhelpers.StrategyEnable(t, conf, settings.StrategyProfile, false)
+
+	publicTS, _ := testhelpers.NewKratosServer(t, reg)
+	browserIdentity1 := newIdentityWithPassword("john-browser@doe.com")
+	browserUser1 := testhelpers.NewHTTPClientWithIdentitySessionCookie(t, reg, browserIdentity1)
+
+	t.Run("case=should not submit when profile method is disabled", func(t *testing.T) {
+
+		t.Run("method=GET", func(t *testing.T) {
+			res, err := browserUser1.Get(publicTS.URL + profile.RouteSettings)
+			require.NoError(t, err)
+			assert.Equal(t, http.StatusNotFound, res.StatusCode)
+
+			b := make([]byte, 10000)
+			_, _ = res.Body.Read(b)
+			assert.Contains(t, string(b), "This endpoint was disabled by system administrator")
+		})
+
+		t.Run("method=POST", func(t *testing.T) {
+			res, err := browserUser1.PostForm(publicTS.URL+profile.RouteSettings, url.Values{"age": {"16"}})
+			require.NoError(t, err)
+			assert.Equal(t, http.StatusNotFound, res.StatusCode)
+
+			b := make([]byte, res.ContentLength)
+			_, _ = res.Body.Read(b)
+			assert.Contains(t, string(b), "This endpoint was disabled by system administrator")
+		})
+	})
+}