Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion custom/conf/app.example.ini
Original file line number Diff line number Diff line change
Expand Up @@ -790,10 +790,13 @@ LEVEL = Info
;; Please note that setting this to false will not disable OAuth Basic or Basic authentication using a token
;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.
;ENABLE_PASSWORD_SIGNIN_FORM = true
;;
;; This setting enables gitea to be signed in with a passkey
;ENABLE_PASSKEY_AUTHENTICATION = true
;;
;; More detail: https://github.com/gogits/gogs/issues/165
;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.
Expand Down
2 changes: 2 additions & 0 deletions modules/setting/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ var Service = struct {
RequireSignInView bool
EnableNotifyMail bool
EnableBasicAuth bool
EnablePasskeyAuth bool
EnableReverseProxyAuth bool
EnableReverseProxyAuthAPI bool
EnableReverseProxyAutoRegister bool
Expand Down Expand Up @@ -161,6 +162,7 @@ func loadServiceFrom(rootCfg ConfigProvider) {
Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool()
Service.EnableBasicAuth = sec.Key("ENABLE_BASIC_AUTHENTICATION").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.EnableReverseProxyAuthAPI = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION_API").MustBool()
Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool()
Expand Down
1 change: 1 addition & 0 deletions routers/web/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ func prepareSignInPageData(ctx *context.Context) {
ctx.Data["PageIsLogin"] = true
ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx)
ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
ctx.Data["EnablePasskeyAuth"] = setting.Service.EnablePasskeyAuth

if setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin {
context.SetCaptchaData(ctx)
Expand Down
3 changes: 3 additions & 0 deletions routers/web/auth/linkaccount.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func LinkAccount(ctx *context.Context) {
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
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
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
Expand Down Expand Up @@ -136,6 +137,7 @@ func LinkAccountPostSignIn(ctx *context.Context) {
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
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
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
Expand Down Expand Up @@ -224,6 +226,7 @@ func LinkAccountPostRegister(ctx *context.Context) {
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
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
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
Expand Down
10 changes: 10 additions & 0 deletions routers/web/auth/webauthn.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ func WebAuthn(ctx *context.Context) {

// WebAuthnPasskeyAssertion submits a WebAuthn challenge for the passkey login to the browser
func WebAuthnPasskeyAssertion(ctx *context.Context) {
if !setting.Service.EnablePasskeyAuth {
ctx.Error(http.StatusForbidden)
return
}

assertion, sessionData, err := wa.WebAuthn.BeginDiscoverableLogin()
if err != nil {
ctx.ServerError("webauthn.BeginDiscoverableLogin", err)
Expand All @@ -66,6 +71,11 @@ func WebAuthnPasskeyAssertion(ctx *context.Context) {

// WebAuthnPasskeyLogin handles the WebAuthn login process using a Passkey
func WebAuthnPasskeyLogin(ctx *context.Context) {
if !setting.Service.EnablePasskeyAuth {
ctx.Error(http.StatusForbidden)
return
}

sessionData, okData := ctx.Session.Get("webauthnPasskeyAssertion").(*webauthn.SessionData)
if !okData || sessionData == nil {
ctx.ServerError("ctx.Session.Get", errors.New("not in WebAuthn session"))
Expand Down
7 changes: 4 additions & 3 deletions templates/user/auth/signin_inner.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,11 @@
</div>

<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">
<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}}
<div class="field">
Expand Down
20 changes: 19 additions & 1 deletion tests/integration/signin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func TestSigninWithRememberMe(t *testing.T) {
session.MakeRequest(t, req, http.StatusOK)
}

func TestEnablePasswordSignInForm(t *testing.T) {
func TestEnablePasswordSignInFormAndEnablePasskeyAuth(t *testing.T) {
defer tests.PrepareTestEnv(t)()

t.Run("EnablePasswordSignInForm=false", func(t *testing.T) {
Expand All @@ -121,4 +121,22 @@ func TestEnablePasswordSignInForm(t *testing.T) {
req = NewRequest(t, "POST", "/user/login")
MakeRequest(t, req, http.StatusOK)
})

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)
})
}
Loading