mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Support disable passkey auth (#33348)
Fix #33314 --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		| @@ -790,10 +790,13 @@ LEVEL = Info | |||||||
| ;; Please note that setting this to false will not disable OAuth Basic or Basic authentication using a token | ;; Please note that setting this to false will not disable OAuth Basic or Basic authentication using a token | ||||||
| ;ENABLE_BASIC_AUTHENTICATION = true | ;ENABLE_BASIC_AUTHENTICATION = true | ||||||
| ;; | ;; | ||||||
| ;; Show the password sign-in form (for password-based login), otherwise, only show OAuth2 login methods. | ;; Show the password sign-in form (for password-based login), otherwise, only show OAuth2 or passkey login methods if they are enabled. | ||||||
| ;; If you set it to false, maybe it also needs to set ENABLE_BASIC_AUTHENTICATION to false to completely disable password-based authentication. | ;; If you set it to false, maybe it also needs to set ENABLE_BASIC_AUTHENTICATION to false to completely disable password-based authentication. | ||||||
| ;ENABLE_PASSWORD_SIGNIN_FORM = true | ;ENABLE_PASSWORD_SIGNIN_FORM = true | ||||||
| ;; | ;; | ||||||
|  | ;; Allow users to sign-in with a passkey | ||||||
|  | ;ENABLE_PASSKEY_AUTHENTICATION = true | ||||||
|  | ;; | ||||||
| ;; More detail: https://github.com/gogits/gogs/issues/165 | ;; More detail: https://github.com/gogits/gogs/issues/165 | ||||||
| ;ENABLE_REVERSE_PROXY_AUTHENTICATION = false | ;ENABLE_REVERSE_PROXY_AUTHENTICATION = false | ||||||
| ; Enable this to allow reverse proxy authentication for API requests, the reverse proxy is responsible for ensuring that no CSRF is possible. | ; Enable this to allow reverse proxy authentication for API requests, the reverse proxy is responsible for ensuring that no CSRF is possible. | ||||||
|   | |||||||
| @@ -46,6 +46,7 @@ var Service = struct { | |||||||
| 	RequireSignInView                       bool | 	RequireSignInView                       bool | ||||||
| 	EnableNotifyMail                        bool | 	EnableNotifyMail                        bool | ||||||
| 	EnableBasicAuth                         bool | 	EnableBasicAuth                         bool | ||||||
|  | 	EnablePasskeyAuth                       bool | ||||||
| 	EnableReverseProxyAuth                  bool | 	EnableReverseProxyAuth                  bool | ||||||
| 	EnableReverseProxyAuthAPI               bool | 	EnableReverseProxyAuthAPI               bool | ||||||
| 	EnableReverseProxyAutoRegister          bool | 	EnableReverseProxyAutoRegister          bool | ||||||
| @@ -161,6 +162,7 @@ func loadServiceFrom(rootCfg ConfigProvider) { | |||||||
| 	Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool() | 	Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool() | ||||||
| 	Service.EnableBasicAuth = sec.Key("ENABLE_BASIC_AUTHENTICATION").MustBool(true) | 	Service.EnableBasicAuth = sec.Key("ENABLE_BASIC_AUTHENTICATION").MustBool(true) | ||||||
| 	Service.EnablePasswordSignInForm = sec.Key("ENABLE_PASSWORD_SIGNIN_FORM").MustBool(true) | 	Service.EnablePasswordSignInForm = sec.Key("ENABLE_PASSWORD_SIGNIN_FORM").MustBool(true) | ||||||
|  | 	Service.EnablePasskeyAuth = sec.Key("ENABLE_PASSKEY_AUTHENTICATION").MustBool(true) | ||||||
| 	Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool() | 	Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool() | ||||||
| 	Service.EnableReverseProxyAuthAPI = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION_API").MustBool() | 	Service.EnableReverseProxyAuthAPI = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION_API").MustBool() | ||||||
| 	Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool() | 	Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool() | ||||||
|   | |||||||
| @@ -169,6 +169,7 @@ func prepareSignInPageData(ctx *context.Context) { | |||||||
| 	ctx.Data["PageIsLogin"] = true | 	ctx.Data["PageIsLogin"] = true | ||||||
| 	ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx) | 	ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx) | ||||||
| 	ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm | 	ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm | ||||||
|  | 	ctx.Data["EnablePasskeyAuth"] = setting.Service.EnablePasskeyAuth | ||||||
|  |  | ||||||
| 	if setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin { | 	if setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin { | ||||||
| 		context.SetCaptchaData(ctx) | 		context.SetCaptchaData(ctx) | ||||||
|   | |||||||
| @@ -46,6 +46,7 @@ func LinkAccount(ctx *context.Context) { | |||||||
| 	ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration | 	ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration | ||||||
| 	ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm | 	ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm | ||||||
| 	ctx.Data["ShowRegistrationButton"] = false | 	ctx.Data["ShowRegistrationButton"] = false | ||||||
|  | 	ctx.Data["EnablePasskeyAuth"] = setting.Service.EnablePasskeyAuth | ||||||
|  |  | ||||||
| 	// use this to set the right link into the signIn and signUp templates in the link_account template | 	// use this to set the right link into the signIn and signUp templates in the link_account template | ||||||
| 	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin" | 	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin" | ||||||
| @@ -145,6 +146,7 @@ func LinkAccountPostSignIn(ctx *context.Context) { | |||||||
| 	ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration | 	ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration | ||||||
| 	ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm | 	ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm | ||||||
| 	ctx.Data["ShowRegistrationButton"] = false | 	ctx.Data["ShowRegistrationButton"] = false | ||||||
|  | 	ctx.Data["EnablePasskeyAuth"] = setting.Service.EnablePasskeyAuth | ||||||
|  |  | ||||||
| 	// use this to set the right link into the signIn and signUp templates in the link_account template | 	// use this to set the right link into the signIn and signUp templates in the link_account template | ||||||
| 	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin" | 	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin" | ||||||
| @@ -235,6 +237,7 @@ func LinkAccountPostRegister(ctx *context.Context) { | |||||||
| 	ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration | 	ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration | ||||||
| 	ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm | 	ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm | ||||||
| 	ctx.Data["ShowRegistrationButton"] = false | 	ctx.Data["ShowRegistrationButton"] = false | ||||||
|  | 	ctx.Data["EnablePasskeyAuth"] = setting.Service.EnablePasskeyAuth | ||||||
|  |  | ||||||
| 	// use this to set the right link into the signIn and signUp templates in the link_account template | 	// use this to set the right link into the signIn and signUp templates in the link_account template | ||||||
| 	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin" | 	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin" | ||||||
|   | |||||||
| @@ -50,6 +50,11 @@ func WebAuthn(ctx *context.Context) { | |||||||
|  |  | ||||||
| // WebAuthnPasskeyAssertion submits a WebAuthn challenge for the passkey login to the browser | // WebAuthnPasskeyAssertion submits a WebAuthn challenge for the passkey login to the browser | ||||||
| func WebAuthnPasskeyAssertion(ctx *context.Context) { | func WebAuthnPasskeyAssertion(ctx *context.Context) { | ||||||
|  | 	if !setting.Service.EnablePasskeyAuth { | ||||||
|  | 		ctx.Error(http.StatusForbidden) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	assertion, sessionData, err := wa.WebAuthn.BeginDiscoverableLogin() | 	assertion, sessionData, err := wa.WebAuthn.BeginDiscoverableLogin() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.ServerError("webauthn.BeginDiscoverableLogin", err) | 		ctx.ServerError("webauthn.BeginDiscoverableLogin", err) | ||||||
| @@ -66,6 +71,11 @@ func WebAuthnPasskeyAssertion(ctx *context.Context) { | |||||||
|  |  | ||||||
| // WebAuthnPasskeyLogin handles the WebAuthn login process using a Passkey | // WebAuthnPasskeyLogin handles the WebAuthn login process using a Passkey | ||||||
| func WebAuthnPasskeyLogin(ctx *context.Context) { | func WebAuthnPasskeyLogin(ctx *context.Context) { | ||||||
|  | 	if !setting.Service.EnablePasskeyAuth { | ||||||
|  | 		ctx.Error(http.StatusForbidden) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	sessionData, okData := ctx.Session.Get("webauthnPasskeyAssertion").(*webauthn.SessionData) | 	sessionData, okData := ctx.Session.Get("webauthnPasskeyAssertion").(*webauthn.SessionData) | ||||||
| 	if !okData || sessionData == nil { | 	if !okData || sessionData == nil { | ||||||
| 		ctx.ServerError("ctx.Session.Get", errors.New("not in WebAuthn session")) | 		ctx.ServerError("ctx.Session.Get", errors.New("not in WebAuthn session")) | ||||||
|   | |||||||
| @@ -60,10 +60,11 @@ | |||||||
| </div> | </div> | ||||||
|  |  | ||||||
| <div class="ui container fluid"> | <div class="ui container fluid"> | ||||||
| 	{{template "user/auth/webauthn_error" .}} |  | ||||||
|  |  | ||||||
| 	<div class="ui attached segment header top tw-max-w-2xl tw-m-auto tw-flex tw-flex-col tw-items-center"> | 	<div class="ui attached segment header top tw-max-w-2xl tw-m-auto tw-flex tw-flex-col tw-items-center"> | ||||||
| 		<a class="signin-passkey">{{ctx.Locale.Tr "auth.signin_passkey"}}</a> | 		{{if .EnablePasskeyAuth}} | ||||||
|  | 			{{template "user/auth/webauthn_error" .}} | ||||||
|  | 			<a class="signin-passkey">{{ctx.Locale.Tr "auth.signin_passkey"}}</a> | ||||||
|  | 		{{end}} | ||||||
|  |  | ||||||
| 		{{if .ShowRegistrationButton}} | 		{{if .ShowRegistrationButton}} | ||||||
| 			<div class="field"> | 			<div class="field"> | ||||||
|   | |||||||
| @@ -98,7 +98,7 @@ func TestSigninWithRememberMe(t *testing.T) { | |||||||
| 	session.MakeRequest(t, req, http.StatusOK) | 	session.MakeRequest(t, req, http.StatusOK) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestEnablePasswordSignInForm(t *testing.T) { | func TestEnablePasswordSignInFormAndEnablePasskeyAuth(t *testing.T) { | ||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	mockLinkAccount := func(ctx *context.Context) { | 	mockLinkAccount := func(ctx *context.Context) { | ||||||
| @@ -141,4 +141,22 @@ func TestEnablePasswordSignInForm(t *testing.T) { | |||||||
| 		resp = MakeRequest(t, req, http.StatusOK) | 		resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 		NewHTMLParser(t, resp.Body).AssertElement(t, "form[action='/user/link_account_signin']", true) | 		NewHTMLParser(t, resp.Body).AssertElement(t, "form[action='/user/link_account_signin']", true) | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
|  | 	t.Run("EnablePasskeyAuth=false", func(t *testing.T) { | ||||||
|  | 		defer tests.PrintCurrentTest(t)() | ||||||
|  | 		defer test.MockVariableValue(&setting.Service.EnablePasskeyAuth, false)() | ||||||
|  |  | ||||||
|  | 		req := NewRequest(t, "GET", "/user/login") | ||||||
|  | 		resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  | 		NewHTMLParser(t, resp.Body).AssertElement(t, ".signin-passkey", false) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	t.Run("EnablePasskeyAuth=true", func(t *testing.T) { | ||||||
|  | 		defer tests.PrintCurrentTest(t)() | ||||||
|  | 		defer test.MockVariableValue(&setting.Service.EnablePasskeyAuth, true)() | ||||||
|  |  | ||||||
|  | 		req := NewRequest(t, "GET", "/user/login") | ||||||
|  | 		resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  | 		NewHTMLParser(t, resp.Body).AssertElement(t, ".signin-passkey", true) | ||||||
|  | 	}) | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user