diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go
index 1219690200..b0f5a9ed79 100644
--- a/routers/web/auth/auth.go
+++ b/routers/web/auth/auth.go
@@ -218,18 +218,50 @@ func performAutoLogin(ctx *context.Context) bool {
return false
}
-func prepareSignInPageData(ctx *context.Context) {
+func performAutoLoginOAuth2(ctx *context.Context, data *preparedSignInData) bool {
+ // If only 1 OAuth provider is present and other login methods are disabled, redirect to the OAuth provider.
+ onlySingleOAuth2 := len(data.oauth2Providers) == 1 &&
+ !setting.Service.EnablePasswordSignInForm &&
+ !setting.Service.EnableOpenIDSignIn &&
+ !setting.Service.EnablePasskeyAuth &&
+ !data.enableSSPI
+
+ if !onlySingleOAuth2 {
+ return false
+ }
+
+ skipToOAuthURL := setting.AppSubURL + "/user/oauth2/" + url.QueryEscape(data.oauth2Providers[0].DisplayName())
+ if redirectTo := ctx.FormString("redirect_to"); redirectTo != "" {
+ skipToOAuthURL += "?redirect_to=" + url.QueryEscape(redirectTo)
+ }
+ ctx.Redirect(skipToOAuthURL)
+ return true
+}
+
+type preparedSignInData struct {
+ oauth2Providers []oauth2.Provider
+ enableSSPI bool
+}
+
+func prepareSignInPageData(ctx *context.Context) (ret preparedSignInData) {
+ var err error
+ ret.enableSSPI = auth.IsSSPIEnabled(ctx)
+ ret.oauth2Providers, err = oauth2.GetOAuth2Providers(ctx, optional.Some(true))
+ if err != nil {
+ log.Error("Failed to get OAuth2 providers: %v", err)
+ }
ctx.Data["Title"] = ctx.Tr("sign_in")
- ctx.Data["OAuth2Providers"], _ = oauth2.GetOAuth2Providers(ctx, optional.Some(true))
+ ctx.Data["OAuth2Providers"] = ret.oauth2Providers
ctx.Data["Title"] = ctx.Tr("sign_in")
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login"
ctx.Data["PageIsSignIn"] = true
ctx.Data["PageIsLogin"] = true
- ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx)
+ ctx.Data["EnableSSPI"] = ret.enableSSPI
prepareCommonAuthPageData(ctx, CommonAuthOptions{
EnableCaptcha: setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin,
})
+ return ret
}
// SignIn render sign in page
@@ -241,7 +273,10 @@ func SignIn(ctx *context.Context) {
redirectAfterAuth(ctx)
return
}
- prepareSignInPageData(ctx)
+ data := prepareSignInPageData(ctx)
+ if performAutoLoginOAuth2(ctx, &data) {
+ return
+ }
ctx.HTML(http.StatusOK, tplSignIn)
}
@@ -471,6 +506,7 @@ func prepareSignUpPageData(ctx *context.Context) bool {
ctx.Data["Title"] = ctx.Tr("sign_up")
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up"
ctx.Data["PageIsSignUp"] = true
+ ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx)
hasUsers, err := user_model.HasUsers(ctx)
if err != nil {
diff --git a/routers/web/auth/auth_test.go b/routers/web/auth/auth_test.go
index d1f808181a..943085a963 100644
--- a/routers/web/auth/auth_test.go
+++ b/routers/web/auth/auth_test.go
@@ -96,6 +96,37 @@ func TestWebAuthOAuth2(t *testing.T) {
assert.Contains(t, ctx.Flash.ErrorMsg, "auth.oauth.signin.error.general")
})
+ t.Run("RedirectSingleProvider", func(t *testing.T) {
+ enablePassword := &setting.Service.EnablePasswordSignInForm
+ enableOpenID := &setting.Service.EnableOpenIDSignIn
+ enablePasskey := &setting.Service.EnablePasskeyAuth
+ defer test.MockVariableValue(enablePassword, false)()
+ defer test.MockVariableValue(enableOpenID, false)()
+ defer test.MockVariableValue(enablePasskey, false)()
+
+ testSignIn := func(t *testing.T, link string, expectedCode int, expectedRedirect string) {
+ ctx, resp := contexttest.MockContext(t, link)
+ SignIn(ctx)
+ assert.Equal(t, expectedCode, resp.Code)
+ if expectedCode == http.StatusSeeOther {
+ assert.Equal(t, expectedRedirect, test.RedirectURL(resp))
+ }
+ }
+ testSignIn(t, "/user/login", http.StatusSeeOther, "/user/oauth2/dummy-auth-source")
+ testSignIn(t, "/user/login?redirect_to=/", http.StatusSeeOther, "/user/oauth2/dummy-auth-source?redirect_to=%2F")
+
+ *enablePassword, *enableOpenID, *enablePasskey = true, false, false
+ testSignIn(t, "/user/login", http.StatusOK, "")
+ *enablePassword, *enableOpenID, *enablePasskey = false, true, false
+ testSignIn(t, "/user/login", http.StatusOK, "")
+ *enablePassword, *enableOpenID, *enablePasskey = false, false, true
+ testSignIn(t, "/user/login", http.StatusOK, "")
+
+ *enablePassword, *enableOpenID, *enablePasskey = false, false, false
+ addOAuth2Source(t, "dummy-auth-source-2", oauth2.Source{})
+ testSignIn(t, "/user/login", http.StatusOK, "")
+ })
+
t.Run("OIDCLogout", func(t *testing.T) {
var mockServer *httptest.Server
mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
diff --git a/services/auth/source/oauth2/providers.go b/services/auth/source/oauth2/providers.go
index 68bd4a1d4c..f719d23fc3 100644
--- a/services/auth/source/oauth2/providers.go
+++ b/services/auth/source/oauth2/providers.go
@@ -62,7 +62,7 @@ func (p *AuthSourceProvider) DisplayName() string {
func (p *AuthSourceProvider) IconHTML(size int) template.HTML {
if p.iconURL != "" {
- img := fmt.Sprintf(``,
+ img := fmt.Sprintf(`
`,
size,
size,
html.EscapeString(p.iconURL), html.EscapeString(p.DisplayName()),
diff --git a/services/auth/source/oauth2/providers_base.go b/services/auth/source/oauth2/providers_base.go
index d34597d6d9..bc761e9742 100644
--- a/services/auth/source/oauth2/providers_base.go
+++ b/services/auth/source/oauth2/providers_base.go
@@ -42,10 +42,10 @@ func (b *BaseProvider) IconHTML(size int) template.HTML {
case "github":
svgName = "octicon-mark-github"
}
- svgHTML := svg.RenderHTML(svgName, size, "tw-mr-2")
+ svgHTML := svg.RenderHTML(svgName, size)
if svgHTML == "" {
log.Error("No SVG icon for oauth2 provider %q", b.name)
- svgHTML = svg.RenderHTML("gitea-openid", size, "tw-mr-2")
+ svgHTML = svg.RenderHTML("gitea-openid", size)
}
return svgHTML
}
diff --git a/services/auth/source/oauth2/providers_openid.go b/services/auth/source/oauth2/providers_openid.go
index e86dc48232..fc0d77a7e6 100644
--- a/services/auth/source/oauth2/providers_openid.go
+++ b/services/auth/source/oauth2/providers_openid.go
@@ -33,7 +33,7 @@ func (o *OpenIDProvider) DisplayName() string {
// IconHTML returns icon HTML for this provider
func (o *OpenIDProvider) IconHTML(size int) template.HTML {
- return svg.RenderHTML("gitea-openid", size, "tw-mr-2")
+ return svg.RenderHTML("gitea-openid", size)
}
// CreateGothProvider creates a GothProvider from this Provider
diff --git a/templates/user/auth/external_auth_methods.tmpl b/templates/user/auth/external_auth_methods.tmpl
new file mode 100644
index 0000000000..c23cab6565
--- /dev/null
+++ b/templates/user/auth/external_auth_methods.tmpl
@@ -0,0 +1,18 @@
+