Uploaded image for project: 'Hybrid Cloud Console'
  1. Hybrid Cloud Console
  2. RHCLOUD-40797

[Sources] Add OIDC support for service to service authentication

XMLWordPrintable

    • Product / Portfolio Work
    • 8
    • False
    • Hide

      None

      Show
      None
    • False
    • None
    • Unset
    • None

      Jira ticket assisted-by: Claude-4-Sonnet (via Cursor)

      S2S JWT Authentication Migration Plan

      *Objective*: Add JWT-based server-to-server authentication as fallback to existing PSK authentication

      Implementation Steps:

      1. *Add JWT dependency* - Use `github.com/golang-jwt/jwt/v5` (21k+ stars, actively maintained)

      2. *Create TokenValidator struct* (~40 lines)

         type TokenValidator struct {
             jwksURL    string
             issuer     string
             audience   string
             keyCache   map[string]*rsa.PublicKey // Simple in-memory cache
             cacheExpiry time.Time
         }
      
      • Method: `ValidateToken(tokenString) (*SystemUser, error)`
      • Method: `fetchJWKS()` using standard `net/http` and `encoding/json`

      3. *Define SystemUser struct* (~15 lines)

         type SystemUser struct {
             Subject     string   `json:"subject"`
             Roles       []string `json:"roles"`
             Permissions []string `json:"permissions"`
             OrgID       string   `json:"org_id"`
         }
      

      4. *Environment configuration* - Add to existing config structure:

      • `OIDC_ISSUER_URL` - JWT issuer validation
      • `OIDC_JWKS_URL` - Public keys endpoint
      • `OIDC_AUDIENCE` - Expected audience claim
      • `SYSTEM_USERS` - JSON mapping: `{"service-account-1": {"roles": ["admin"], "permissions": ["read:all"]}}`

      5. *Extend existing S2S middleware* (~20 lines addition)

         // In existing middleware function
         if pskIdentity == nil {
             authHeader := r.Header.Get("Authorization")
             if strings.HasPrefix(authHeader, "Bearer ") {
                 token := strings.TrimPrefix(authHeader, "Bearer ")
                 if user, err := tokenValidator.ValidateToken(token); err == nil {
                     return user, nil
                 }
             }
         }
      

      6. *JWKS caching logic* (~25 lines)

      • Fetch keys on first use, cache for 1 hour
      • Refresh on cache miss or expiry
      • Handle HTTP errors gracefully, log failures

      7. *Token validation flow*:

      • Parse JWT header to get key ID (`kid`)
      • Fetch/use cached public key for verification
      • Validate issuer, audience, expiration claims
      • Extract subject, map to SystemUser via env config

      8. *Testing* (~30 lines total)

      • Unit test: Mock JWKS server using `httptest.NewServer`
      • Integration test: Add JWT scenario to existing middleware tests
      • Test invalid tokens, expired tokens, unknown subjects

      9. *Configuration updates*:

      • Add new env vars to deployment YAML
      • Update application config loading to include OIDC settings
      • Document SYSTEM_USERS JSON schema in deployment files

      *Total code*: ~130 lines across 3 new files + middleware modification
      *Dependencies*: Only `github.com/golang-jwt/jwt/v5` + Go standard library
      *Deployment*: Add 4 environment variables, zero breaking changes

              glepage@redhat.com Gwenneg Lepage
              glepage@redhat.com Gwenneg Lepage
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

                Created:
                Updated: