Skip to content

Step-up auth: Group protection (OIDC backend support) [2/4]

What does this MR do and why?

This MR introduces the foundational OIDC backend changes needed to support step-up authentication at the namespace/group level. It extends the existing OIDC step-up authentication classes to handle a new namespace scope alongside the existing admin_mode scope.

Context: This MR builds upon the step-up authentication foundation that was first introduced for admin mode in !171643 (merged), and extends the model and UI settings added for group-based step-up auth in !199423 (merged). GitLab needs to provide enhanced security for sensitive group/namespace operations by requiring additional authentication verification (step-up authentication) through OIDC providers.

This MR (2/4) includes:

  • Extended OIDC step-up authentication flow to support namespace scope
  • Updated OAuth callback handling to process namespace step-up auth requests
  • Added proper scope separation between admin_mode and namespace contexts
  • Enhanced session management for namespace step-up authentication
  • Comprehensive test coverage for all new namespace scope scenarios

Technical changes:

  • lib/gitlab/auth/oidc/step_up_auth_before_request_phase.rb: Extended request phase handler to support namespace scope with feature flag isolation
  • lib/gitlab/auth/oidc/step_up_authentication_flow.rb: Added namespace scope support to authentication flow management
  • app/controllers/omniauth_callbacks_controller.rb: Updated OAuth callback to handle both admin_mode and namespace scopes independently
  • Comprehensive test coverage with 288+ lines of new tests ensuring proper isolation and functionality

This MR implements Phase 2: OIDC Backend Extensions of the group-based OIDC step-up authentication feature, building upon the model and UI settings from Phase 1 (!199423 (merged)).

All changes are protected by the omniauth_step_up_auth_for_namespace feature flag and maintain full backward compatibility with existing admin_mode step-up authentication.

🚦 Safety Note: This MR is safe to merge as:

  • All changes are behind the omniauth_step_up_auth_for_namespace feature flag (disabled by default)
  • This MR only adds backend authentication flow support - no enforcement logic is included
  • The actual enforcement of step-up authentication will be implemented in future phases
  • No user-facing changes or disruptions will occur until the complete feature is enabled
  • Existing authentication flows remain completely unaffected

🛠️ with ❤️ at Siemens

References

Screenshots or screen recordings

N/A - Backend changes only, no UI modifications in this MR.

How to set up and validate locally

Part 1: Prepare your local GitLab gdk instance

  1. In your local gdk instance, add the following omniauth configuation to the config/gitlab.yml, see collapsed section below
  2. Enable the feature flag :omniauth_step_up_auth_for_namespace via the rails console
    Feature.enable(:omniauth_step_up_auth_for_namespace)
  3. Create a simple GitLab group that we want to protect with step-up auth.
Click to expand the `config/gitlab.yml`
development:
  <<: *base
  omniauth:
    allow_bypass_two_factor:  ["openid_connect"]
    allow_single_sign_on: ["openid_connect"]
    auto_link_ldap_user: null
    auto_link_saml_user: null
    auto_link_user: ["openid_connect"]
    auto_sign_in_with_provider: 
    block_auto_created_users: false
    external_providers: []

    providers:
    - { name: "openid_connect",
        label: "[OIDC] Keycloak",
        args: {
          name: "openid_connect",
          # strategy_class: "OmniAuth::Strategies::OpenIDConnect",
          scope: ["openid", "profile", "email"],
          response_type: "code",
          issuer: "http://localhost:8080/realms/step-up-auth-gitlab-realm",
          client_auth_method: "query",
          discovery: false,
          uid_field: "preferred_username",
          pkce: true,
          allow_authorize_params: ["claims"],
          client_options: {
            host: "localhost",
            scheme: "http",
            port: "8080",
            identifier: "step-up-auth-gitlab-client",
            secret: "C6S2b1ZkifZa6BI8Jy5K3lz2Eglb4JuQ",
            redirect_uri: "http://gdk.test:3000/users/auth/openid_connect/callback",
            authorization_endpoint: "/realms/step-up-auth-gitlab-realm/protocol/openid-connect/auth",
            token_endpoint: "/realms/step-up-auth-gitlab-realm/protocol/openid-connect/token",
            userinfo_endpoint: "/realms/step-up-auth-gitlab-realm/protocol/openid-connect/userinfo",
            jwks_uri: "http://localhost:8080/realms/step-up-auth-gitlab-realm/protocol/openid-connect/certs",
            end_session_endpoint: "/realms/step-up-auth-gitlab-realm/protocol/openid-connect/logout"
          }
        },
        step_up_auth: {
          namespace: {
            id_token: {
              required: {
                acr: 'gold' 
              }
            },
            params: {
              claims: { 
                id_token: { 
                  acr: { 
                    essential: true, 
                    values: ['gold'] 
                  } 
                } 
              }
            }
          },          
        }
      }

Part 2: Manual Testing via Rails Console

Since this MR only adds backend support without enforcement, you can validate the OIDC backend changes directly via Rails console:

# Quick validation in Rails console
session = {}
provider = 'openid_connect'

# Test namespace scope flow
namespace_flow = Gitlab::Auth::Oidc::StepUpAuthenticationFlow.new(
  session: session,
  provider: provider,
  scope: 'namespace'
)

# Simulate step-up auth flow
namespace_flow.request!
puts "Requested: #{namespace_flow.requested?}"  # Should be true

namespace_flow.succeed!
puts "Succeeded: #{namespace_flow.succeeded?}"  # Should be true

# Check session isolation
puts session.inspect
# Should show: {"omniauth_step_up_auth"=>{"openid_connect"=>{"namespace"=>{"state"=>"succeeded"}}}}

# Test scope isolation with admin_mode
admin_flow = Gitlab::Auth::Oidc::StepUpAuthenticationFlow.new(
  session: session,
  provider: provider,
  scope: 'admin_mode'
)
admin_flow.request!

# Both scopes should coexist in session
puts session['omniauth_step_up_auth']['openid_connect'].keys
# Should show: ["namespace", "admin_mode"]

Part 3: Run the automated tests

  • Run the comprehensive test suite to verify all functionality:
    # Run OIDC-specific tests for namespace scope
    bundle exec rspec spec/lib/gitlab/auth/oidc
    
    # Verify OAuth callback handling for step-up auth
    bundle exec rspec spec/controllers/omniauth_callbacks_controller_spec.rb -e "step-up"

Part 4: Further checks

  • Verify that existing admin_mode step-up authentication continues to work normally
  • Test that namespace scope requests are properly handled when the feature flag is enabled
  • Confirm feature flag isolation - namespace flag should not affect admin_mode and vice versa
  • Test OAuth provider configuration loading for namespace scope

MR acceptance checklist

Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

MR Checklist (@gerardo-navarro)

Related to #474650 #556943

Edited by Gerardo Navarro

Merge request reports

Loading