fix(auth): harden pending oauth and backend mode flows
This commit is contained in:
@@ -27,23 +27,50 @@ func BackendModeUserGuard(settingService *service.SettingService) gin.HandlerFun
|
||||
}
|
||||
}
|
||||
|
||||
func backendModeAllowsAuthPath(path string) bool {
|
||||
path = strings.ToLower(strings.TrimSpace(path))
|
||||
for _, suffix := range []string{"/auth/login", "/auth/login/2fa", "/auth/logout", "/auth/refresh"} {
|
||||
if strings.HasSuffix(path, suffix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, suffix := range []string{
|
||||
"/auth/oauth/linuxdo/callback",
|
||||
"/auth/oauth/wechat/callback",
|
||||
"/auth/oauth/wechat/payment/callback",
|
||||
"/auth/oauth/oidc/callback",
|
||||
"/auth/oauth/linuxdo/complete-registration",
|
||||
"/auth/oauth/wechat/complete-registration",
|
||||
"/auth/oauth/oidc/complete-registration",
|
||||
"/auth/oauth/linuxdo/create-account",
|
||||
"/auth/oauth/wechat/create-account",
|
||||
"/auth/oauth/oidc/create-account",
|
||||
"/auth/oauth/linuxdo/bind-login",
|
||||
"/auth/oauth/wechat/bind-login",
|
||||
"/auth/oauth/oidc/bind-login",
|
||||
} {
|
||||
if strings.HasSuffix(path, suffix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Contains(path, "/auth/oauth/pending/")
|
||||
}
|
||||
|
||||
// BackendModeAuthGuard selectively blocks auth endpoints when backend mode is enabled.
|
||||
// Allows: login, login/2fa, logout, refresh (admin needs these).
|
||||
// Blocks: register, forgot-password, reset-password, OAuth, etc.
|
||||
// Allows the minimal auth surface admins still need in backend mode, including
|
||||
// OAuth callbacks and pending continuations. Handler-level backend mode checks
|
||||
// still enforce admin-only login and forbid self-service registration.
|
||||
func BackendModeAuthGuard(settingService *service.SettingService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if settingService == nil || !settingService.IsBackendModeEnabled(c.Request.Context()) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
path := c.Request.URL.Path
|
||||
// Allow login, 2FA, logout, refresh, public settings
|
||||
allowedSuffixes := []string{"/auth/login", "/auth/login/2fa", "/auth/logout", "/auth/refresh"}
|
||||
for _, suffix := range allowedSuffixes {
|
||||
if strings.HasSuffix(path, suffix) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
if backendModeAllowsAuthPath(c.Request.URL.Path) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
response.Forbidden(c, "Backend mode is active. Registration and self-service auth flows are disabled.")
|
||||
c.Abort()
|
||||
|
||||
@@ -198,6 +198,96 @@ func TestBackendModeAuthGuard(t *testing.T) {
|
||||
path: "/api/v1/auth/refresh",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "enabled_blocks_linuxdo_oauth_start",
|
||||
enabled: "true",
|
||||
path: "/api/v1/auth/oauth/linuxdo/start",
|
||||
wantStatus: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
name: "enabled_allows_linuxdo_oauth_callback",
|
||||
enabled: "true",
|
||||
path: "/api/v1/auth/oauth/linuxdo/callback",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "enabled_blocks_wechat_oauth_start",
|
||||
enabled: "true",
|
||||
path: "/api/v1/auth/oauth/wechat/start",
|
||||
wantStatus: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
name: "enabled_allows_wechat_oauth_callback",
|
||||
enabled: "true",
|
||||
path: "/api/v1/auth/oauth/wechat/callback",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "enabled_blocks_wechat_payment_oauth_start",
|
||||
enabled: "true",
|
||||
path: "/api/v1/auth/oauth/wechat/payment/start",
|
||||
wantStatus: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
name: "enabled_allows_wechat_payment_oauth_callback",
|
||||
enabled: "true",
|
||||
path: "/api/v1/auth/oauth/wechat/payment/callback",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "enabled_blocks_oidc_oauth_start",
|
||||
enabled: "true",
|
||||
path: "/api/v1/auth/oauth/oidc/start",
|
||||
wantStatus: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
name: "enabled_allows_oidc_oauth_callback",
|
||||
enabled: "true",
|
||||
path: "/api/v1/auth/oauth/oidc/callback",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "enabled_allows_oauth_pending_exchange",
|
||||
enabled: "true",
|
||||
path: "/api/v1/auth/oauth/pending/exchange",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "enabled_allows_oauth_pending_send_verify_code",
|
||||
enabled: "true",
|
||||
path: "/api/v1/auth/oauth/pending/send-verify-code",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "enabled_allows_oauth_pending_create_account",
|
||||
enabled: "true",
|
||||
path: "/api/v1/auth/oauth/pending/create-account",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "enabled_allows_oauth_pending_bind_login",
|
||||
enabled: "true",
|
||||
path: "/api/v1/auth/oauth/pending/bind-login",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "enabled_allows_provider_bind_login",
|
||||
enabled: "true",
|
||||
path: "/api/v1/auth/oauth/oidc/bind-login",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "enabled_allows_provider_create_account",
|
||||
enabled: "true",
|
||||
path: "/api/v1/auth/oauth/wechat/create-account",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "enabled_allows_legacy_complete_registration",
|
||||
enabled: "true",
|
||||
path: "/api/v1/auth/oauth/linuxdo/complete-registration",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "enabled_blocks_register",
|
||||
enabled: "true",
|
||||
|
||||
Reference in New Issue
Block a user