Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support disable passkey auth #33348

Merged
merged 11 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
;;
;; Allow users to sign-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 @@ -46,6 +46,7 @@ func LinkAccount(ctx *context.Context) {
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
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 @@ -145,6 +146,7 @@ func LinkAccountPostSignIn(ctx *context.Context) {
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
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 @@ -235,6 +237,7 @@ func LinkAccountPostRegister(ctx *context.Context) {
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
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 @@ -98,7 +98,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)()

mockLinkAccount := func(ctx *context.Context) {
Expand Down Expand Up @@ -141,4 +141,22 @@ func TestEnablePasswordSignInForm(t *testing.T) {
resp = MakeRequest(t, req, http.StatusOK)
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)
})
}
Loading