Redirect to the only OAuth2 provider when no other login methods and fix various problems (#36901)

Fixes: #36846 

1. When there is only on OAuth2 login method, automatically direct to it
2. Fix legacy problems in code, including:
   * Rename template filename and fix TODO comments
   * Fix legacy variable names
   * Add missing SSPI variable for template
   * Fix unnecessary layout, remove garbage styles
* Only do AppUrl(ROOT_URL) check when it is needed (avoid unnecessary
warnings to end users)

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
Navneet
2026-04-01 18:20:57 +05:30
committed by GitHub
parent ca8c71359c
commit 3ffccb8fe5
11 changed files with 116 additions and 58 deletions

View File

@@ -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 {

View File

@@ -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) {