mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Merge pull request #36087 from ericchiang/plugin-auth-oidc-verify-email
Automatic merge from submit-queue oidc auth-n plugin: enforce email_verified claim This change causes the OpenID Connect authenticator to start enforcing the 'email_verified' claim. https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims If the OIDC authenticator uses the 'email' claim as a user's username and the 'email_verified' is not set to `true`, reject that authentication attempt. cc @erictune @kubernetes/sig-auth @mlbiam ```release-note When using OIDC authentication and specifying --oidc-username-claim=email, an `"email_verified":true` claim must be returned from the identity provider. ```
This commit is contained in:
		@@ -238,7 +238,23 @@ func (a *OIDCAuthenticator) AuthenticateToken(value string) (user.Info, bool, er
 | 
				
			|||||||
	var username string
 | 
						var username string
 | 
				
			||||||
	switch a.usernameClaim {
 | 
						switch a.usernameClaim {
 | 
				
			||||||
	case "email":
 | 
						case "email":
 | 
				
			||||||
		// TODO(yifan): Check 'email_verified' to make sure the email is valid.
 | 
							verified, ok := claims["email_verified"]
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								return nil, false, errors.New("'email_verified' claim not present")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							emailVerified, ok := verified.(bool)
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								// OpenID Connect spec defines 'email_verified' as a boolean. For now, be a pain and error if
 | 
				
			||||||
 | 
								// it's a different type. If there are enough misbehaving providers we can relax this latter.
 | 
				
			||||||
 | 
								//
 | 
				
			||||||
 | 
								// See: https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
 | 
				
			||||||
 | 
								return nil, false, fmt.Errorf("malformed claim 'email_verified', expected boolean got %T", verified)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if !emailVerified {
 | 
				
			||||||
 | 
								return nil, false, errors.New("email not verified")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		username = claim
 | 
							username = claim
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		// For all other cases, use issuerURL + claim as the user name.
 | 
							// For all other cases, use issuerURL + claim as the user name.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,14 +33,15 @@ import (
 | 
				
			|||||||
	oidctesting "k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/oidc/testing"
 | 
						oidctesting "k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/oidc/testing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func generateToken(t *testing.T, op *oidctesting.OIDCProvider, iss, sub, aud string, usernameClaim, value, groupsClaim string, groups interface{}, iat, exp time.Time) string {
 | 
					func generateToken(t *testing.T, op *oidctesting.OIDCProvider, iss, sub, aud string, usernameClaim, value, groupsClaim string, groups interface{}, iat, exp time.Time, emailVerified bool) string {
 | 
				
			||||||
	signer := op.PrivKey.Signer()
 | 
					 | 
				
			||||||
	claims := oidc.NewClaims(iss, sub, aud, iat, exp)
 | 
						claims := oidc.NewClaims(iss, sub, aud, iat, exp)
 | 
				
			||||||
	claims.Add(usernameClaim, value)
 | 
						claims.Add(usernameClaim, value)
 | 
				
			||||||
	if groups != nil && groupsClaim != "" {
 | 
						if groups != nil && groupsClaim != "" {
 | 
				
			||||||
		claims.Add(groupsClaim, groups)
 | 
							claims.Add(groupsClaim, groups)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						claims.Add("email_verified", emailVerified)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						signer := op.PrivKey.Signer()
 | 
				
			||||||
	jwt, err := jose.NewSignedJWT(claims, signer)
 | 
						jwt, err := jose.NewSignedJWT(claims, signer)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("Cannot generate token: %v", err)
 | 
							t.Fatalf("Cannot generate token: %v", err)
 | 
				
			||||||
@@ -49,16 +50,20 @@ func generateToken(t *testing.T, op *oidctesting.OIDCProvider, iss, sub, aud str
 | 
				
			|||||||
	return jwt.Encode()
 | 
						return jwt.Encode()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func generateTokenWithUnverifiedEmail(t *testing.T, op *oidctesting.OIDCProvider, iss, sub, aud string, email string) string {
 | 
				
			||||||
 | 
						return generateToken(t, op, iss, sub, aud, "email", email, "", nil, time.Now(), time.Now().Add(time.Hour), false)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func generateGoodToken(t *testing.T, op *oidctesting.OIDCProvider, iss, sub, aud string, usernameClaim, value, groupsClaim string, groups interface{}) string {
 | 
					func generateGoodToken(t *testing.T, op *oidctesting.OIDCProvider, iss, sub, aud string, usernameClaim, value, groupsClaim string, groups interface{}) string {
 | 
				
			||||||
	return generateToken(t, op, iss, sub, aud, usernameClaim, value, groupsClaim, groups, time.Now(), time.Now().Add(time.Hour))
 | 
						return generateToken(t, op, iss, sub, aud, usernameClaim, value, groupsClaim, groups, time.Now(), time.Now().Add(time.Hour), true)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func generateMalformedToken(t *testing.T, op *oidctesting.OIDCProvider, iss, sub, aud string, usernameClaim, value, groupsClaim string, groups interface{}) string {
 | 
					func generateMalformedToken(t *testing.T, op *oidctesting.OIDCProvider, iss, sub, aud string, usernameClaim, value, groupsClaim string, groups interface{}) string {
 | 
				
			||||||
	return generateToken(t, op, iss, sub, aud, usernameClaim, value, groupsClaim, groups, time.Now(), time.Now().Add(time.Hour)) + "randombits"
 | 
						return generateToken(t, op, iss, sub, aud, usernameClaim, value, groupsClaim, groups, time.Now(), time.Now().Add(time.Hour), true) + "randombits"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func generateExpiredToken(t *testing.T, op *oidctesting.OIDCProvider, iss, sub, aud string, usernameClaim, value, groupsClaim string, groups interface{}) string {
 | 
					func generateExpiredToken(t *testing.T, op *oidctesting.OIDCProvider, iss, sub, aud string, usernameClaim, value, groupsClaim string, groups interface{}) string {
 | 
				
			||||||
	return generateToken(t, op, iss, sub, aud, usernameClaim, value, groupsClaim, groups, time.Now().Add(-2*time.Hour), time.Now().Add(-1*time.Hour))
 | 
						return generateToken(t, op, iss, sub, aud, usernameClaim, value, groupsClaim, groups, time.Now().Add(-2*time.Hour), time.Now().Add(-1*time.Hour), true)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestTLSConfig(t *testing.T) {
 | 
					func TestTLSConfig(t *testing.T) {
 | 
				
			||||||
@@ -257,6 +262,15 @@ func TestOIDCAuthentication(t *testing.T) {
 | 
				
			|||||||
				false,
 | 
									false,
 | 
				
			||||||
				"custom group claim contains invalid type: float64",
 | 
									"custom group claim contains invalid type: float64",
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									// Email not verified
 | 
				
			||||||
 | 
									"email",
 | 
				
			||||||
 | 
									"",
 | 
				
			||||||
 | 
									generateTokenWithUnverifiedEmail(t, op, srv.URL, "client-foo", "client-foo", "foo@example.com"),
 | 
				
			||||||
 | 
									nil,
 | 
				
			||||||
 | 
									false,
 | 
				
			||||||
 | 
									"email not verified",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				"sub",
 | 
									"sub",
 | 
				
			||||||
				"",
 | 
									"",
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user