mirror of https://github.com/k3s-io/k3s
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. ```pull/6/head
commit
016133cf7d
|
@ -238,7 +238,23 @@ func (a *OIDCAuthenticator) AuthenticateToken(value string) (user.Info, bool, er
|
|||
var username string
|
||||
switch a.usernameClaim {
|
||||
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
|
||||
default:
|
||||
// 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"
|
||||
)
|
||||
|
||||
func generateToken(t *testing.T, op *oidctesting.OIDCProvider, iss, sub, aud string, usernameClaim, value, groupsClaim string, groups interface{}, iat, exp time.Time) string {
|
||||
signer := op.PrivKey.Signer()
|
||||
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 {
|
||||
claims := oidc.NewClaims(iss, sub, aud, iat, exp)
|
||||
claims.Add(usernameClaim, value)
|
||||
if groups != nil && groupsClaim != "" {
|
||||
claims.Add(groupsClaim, groups)
|
||||
}
|
||||
claims.Add("email_verified", emailVerified)
|
||||
|
||||
signer := op.PrivKey.Signer()
|
||||
jwt, err := jose.NewSignedJWT(claims, signer)
|
||||
if err != nil {
|
||||
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()
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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) {
|
||||
|
@ -257,6 +262,15 @@ func TestOIDCAuthentication(t *testing.T) {
|
|||
false,
|
||||
"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",
|
||||
"",
|
||||
|
|
Loading…
Reference in New Issue