mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +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:
commit
016133cf7d
@ -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",
|
||||||
"",
|
"",
|
||||||
|
Loading…
Reference in New Issue
Block a user