mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-05 10:19:50 +00:00
Merge pull request #123282 from enj/enj/i/authn_config_algs
Support all key algs with structured authn config
This commit is contained in:
commit
72c3c7c924
@ -40,6 +40,7 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/server/egressselector"
|
"k8s.io/apiserver/pkg/server/egressselector"
|
||||||
genericoptions "k8s.io/apiserver/pkg/server/options"
|
genericoptions "k8s.io/apiserver/pkg/server/options"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
"k8s.io/apiserver/plugin/pkg/authenticator/token/oidc"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
v1listers "k8s.io/client-go/listers/core/v1"
|
v1listers "k8s.io/client-go/listers/core/v1"
|
||||||
@ -461,6 +462,9 @@ func (o *BuiltInAuthenticationOptions) ToAuthenticationConfig() (kubeauthenticat
|
|||||||
if ret.AuthenticationConfig, err = loadAuthenticationConfig(o.AuthenticationConfigFile); err != nil {
|
if ret.AuthenticationConfig, err = loadAuthenticationConfig(o.AuthenticationConfigFile); err != nil {
|
||||||
return kubeauthenticator.Config{}, err
|
return kubeauthenticator.Config{}, err
|
||||||
}
|
}
|
||||||
|
// all known signing algs are allowed when using authentication config
|
||||||
|
// TODO: what we really want to express is 'any alg is fine as long it matches a public key'
|
||||||
|
ret.OIDCSigningAlgs = oidc.AllValidSigningAlgorithms()
|
||||||
} else if o.OIDC != nil && len(o.OIDC.IssuerURL) > 0 && len(o.OIDC.ClientID) > 0 {
|
} else if o.OIDC != nil && len(o.OIDC.IssuerURL) > 0 && len(o.OIDC.ClientID) > 0 {
|
||||||
usernamePrefix := o.OIDC.UsernamePrefix
|
usernamePrefix := o.OIDC.UsernamePrefix
|
||||||
|
|
||||||
|
@ -446,6 +446,8 @@ func TestBuiltInAuthenticationOptionsAddFlags(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestToAuthenticationConfig_OIDC(t *testing.T) {
|
func TestToAuthenticationConfig_OIDC(t *testing.T) {
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StructuredAuthenticationConfiguration, true)()
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
args []string
|
args []string
|
||||||
@ -640,6 +642,43 @@ func TestToAuthenticationConfig_OIDC(t *testing.T) {
|
|||||||
OIDCSigningAlgs: []string{"RS256"},
|
OIDCSigningAlgs: []string{"RS256"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "basic authentication configuration",
|
||||||
|
args: []string{
|
||||||
|
"--authentication-config=" + writeTempFile(t, `
|
||||||
|
apiVersion: apiserver.config.k8s.io/v1alpha1
|
||||||
|
kind: AuthenticationConfiguration
|
||||||
|
jwt:
|
||||||
|
- issuer:
|
||||||
|
url: https://test-issuer
|
||||||
|
audiences: [ "🐼" ]
|
||||||
|
claimMappings:
|
||||||
|
username:
|
||||||
|
claim: sub
|
||||||
|
prefix: ""
|
||||||
|
`),
|
||||||
|
},
|
||||||
|
expectConfig: kubeauthenticator.Config{
|
||||||
|
TokenSuccessCacheTTL: 10 * time.Second,
|
||||||
|
AuthenticationConfig: &apiserver.AuthenticationConfiguration{
|
||||||
|
JWT: []apiserver.JWTAuthenticator{
|
||||||
|
{
|
||||||
|
Issuer: apiserver.Issuer{
|
||||||
|
URL: "https://test-issuer",
|
||||||
|
Audiences: []string{"🐼"},
|
||||||
|
},
|
||||||
|
ClaimMappings: apiserver.ClaimMappings{
|
||||||
|
Username: apiserver.PrefixedClaimOrExpression{
|
||||||
|
Claim: "sub",
|
||||||
|
Prefix: pointer.String(""),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OIDCSigningAlgs: []string{"ES256", "ES384", "ES512", "PS256", "PS384", "PS512", "RS256", "RS384", "RS512"},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testcase := range testCases {
|
for _, testcase := range testCases {
|
||||||
|
@ -204,8 +204,11 @@ func (a *Authenticator) Close() {
|
|||||||
a.cancel()
|
a.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
// whitelist of signing algorithms to ensure users don't mistakenly pass something
|
func AllValidSigningAlgorithms() []string {
|
||||||
// goofy.
|
return sets.List(sets.KeySet(allowedSigningAlgs))
|
||||||
|
}
|
||||||
|
|
||||||
|
// allowlist of signing algorithms to ensure users don't mistakenly pass something goofy.
|
||||||
var allowedSigningAlgs = map[string]bool{
|
var allowedSigningAlgs = map[string]bool{
|
||||||
oidc.RS256: true,
|
oidc.RS256: true,
|
||||||
oidc.RS384: true,
|
oidc.RS384: true,
|
||||||
|
@ -18,6 +18,8 @@ package oidc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
@ -35,11 +37,13 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"gopkg.in/square/go-jose.v2"
|
||||||
|
|
||||||
authenticationv1 "k8s.io/api/authentication/v1"
|
authenticationv1 "k8s.io/api/authentication/v1"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
utilrand "k8s.io/apimachinery/pkg/util/rand"
|
||||||
"k8s.io/apiserver/pkg/features"
|
"k8s.io/apiserver/pkg/features"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
@ -116,29 +120,10 @@ func TestStructuredAuthenticationConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runTests(t *testing.T, useAuthenticationConfig bool) {
|
func runTests(t *testing.T, useAuthenticationConfig bool) {
|
||||||
var tests = []struct {
|
var tests = []singleTest[*rsa.PrivateKey, *rsa.PublicKey]{
|
||||||
name string
|
|
||||||
configureInfrastructure func(t *testing.T, fn authenticationConfigFunc) (
|
|
||||||
oidcServer *utilsoidc.TestServer,
|
|
||||||
apiServer *kubeapiserverapptesting.TestServer,
|
|
||||||
signingPrivateKey *rsa.PrivateKey,
|
|
||||||
caCertContent []byte,
|
|
||||||
caFilePath string,
|
|
||||||
)
|
|
||||||
configureOIDCServerBehaviour func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey)
|
|
||||||
configureClient func(
|
|
||||||
t *testing.T,
|
|
||||||
restCfg *rest.Config,
|
|
||||||
caCert []byte,
|
|
||||||
certPath,
|
|
||||||
oidcServerURL,
|
|
||||||
oidcServerTokenURL string,
|
|
||||||
) kubernetes.Interface
|
|
||||||
assertErrFn func(t *testing.T, errorToCheck error)
|
|
||||||
}{
|
|
||||||
{
|
{
|
||||||
name: "ID token is ok",
|
name: "ID token is ok",
|
||||||
configureInfrastructure: configureTestInfrastructure,
|
configureInfrastructure: configureTestInfrastructure[*rsa.PrivateKey, *rsa.PublicKey],
|
||||||
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
||||||
idTokenLifetime := time.Second * 1200
|
idTokenLifetime := time.Second * 1200
|
||||||
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
||||||
@ -161,7 +146,7 @@ func runTests(t *testing.T, useAuthenticationConfig bool) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ID token is expired",
|
name: "ID token is expired",
|
||||||
configureInfrastructure: configureTestInfrastructure,
|
configureInfrastructure: configureTestInfrastructure[*rsa.PrivateKey, *rsa.PublicKey],
|
||||||
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
||||||
configureOIDCServerToReturnExpiredIDToken(t, 2, oidcServer, signingPrivateKey)
|
configureOIDCServerToReturnExpiredIDToken(t, 2, oidcServer, signingPrivateKey)
|
||||||
},
|
},
|
||||||
@ -172,7 +157,7 @@ func runTests(t *testing.T, useAuthenticationConfig bool) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "wrong client ID",
|
name: "wrong client ID",
|
||||||
configureInfrastructure: configureTestInfrastructure,
|
configureInfrastructure: configureTestInfrastructure[*rsa.PrivateKey, *rsa.PublicKey],
|
||||||
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, _ *rsa.PrivateKey) {
|
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, _ *rsa.PrivateKey) {
|
||||||
oidcServer.TokenHandler().EXPECT().Token().Times(2).Return(utilsoidc.Token{}, utilsoidc.ErrBadClientID)
|
oidcServer.TokenHandler().EXPECT().Token().Times(2).Return(utilsoidc.Token{}, utilsoidc.ErrBadClientID)
|
||||||
},
|
},
|
||||||
@ -189,7 +174,7 @@ func runTests(t *testing.T, useAuthenticationConfig bool) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "client has wrong CA",
|
name: "client has wrong CA",
|
||||||
configureInfrastructure: configureTestInfrastructure,
|
configureInfrastructure: configureTestInfrastructure[*rsa.PrivateKey, *rsa.PublicKey],
|
||||||
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, _ *rsa.PrivateKey) {},
|
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, _ *rsa.PrivateKey) {},
|
||||||
configureClient: func(t *testing.T, restCfg *rest.Config, caCert []byte, _, oidcServerURL, oidcServerTokenURL string) kubernetes.Interface {
|
configureClient: func(t *testing.T, restCfg *rest.Config, caCert []byte, _, oidcServerURL, oidcServerTokenURL string) kubernetes.Interface {
|
||||||
tempDir := t.TempDir()
|
tempDir := t.TempDir()
|
||||||
@ -207,7 +192,7 @@ func runTests(t *testing.T, useAuthenticationConfig bool) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "refresh flow does not return ID Token",
|
name: "refresh flow does not return ID Token",
|
||||||
configureInfrastructure: configureTestInfrastructure,
|
configureInfrastructure: configureTestInfrastructure[*rsa.PrivateKey, *rsa.PublicKey],
|
||||||
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
||||||
configureOIDCServerToReturnExpiredIDToken(t, 1, oidcServer, signingPrivateKey)
|
configureOIDCServerToReturnExpiredIDToken(t, 1, oidcServer, signingPrivateKey)
|
||||||
oidcServer.TokenHandler().EXPECT().Token().Times(1).Return(utilsoidc.Token{
|
oidcServer.TokenHandler().EXPECT().Token().Times(1).Return(utilsoidc.Token{
|
||||||
@ -230,7 +215,7 @@ func runTests(t *testing.T, useAuthenticationConfig bool) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ID token signature can not be verified due to wrong JWKs",
|
name: "ID token signature can not be verified due to wrong JWKs",
|
||||||
configureInfrastructure: func(t *testing.T, fn authenticationConfigFunc) (
|
configureInfrastructure: func(t *testing.T, fn authenticationConfigFunc, keyFunc func(t *testing.T) (*rsa.PrivateKey, *rsa.PublicKey)) (
|
||||||
oidcServer *utilsoidc.TestServer,
|
oidcServer *utilsoidc.TestServer,
|
||||||
apiServer *kubeapiserverapptesting.TestServer,
|
apiServer *kubeapiserverapptesting.TestServer,
|
||||||
signingPrivateKey *rsa.PrivateKey,
|
signingPrivateKey *rsa.PrivateKey,
|
||||||
@ -239,8 +224,7 @@ func runTests(t *testing.T, useAuthenticationConfig bool) {
|
|||||||
) {
|
) {
|
||||||
caCertContent, _, caFilePath, caKeyFilePath := generateCert(t)
|
caCertContent, _, caFilePath, caKeyFilePath := generateCert(t)
|
||||||
|
|
||||||
signingPrivateKey, wantErr := rsa.GenerateKey(rand.Reader, rsaKeyBitSize)
|
signingPrivateKey, _ = keyFunc(t)
|
||||||
require.NoError(t, wantErr)
|
|
||||||
|
|
||||||
oidcServer = utilsoidc.BuildAndRunTestServer(t, caFilePath, caKeyFilePath)
|
oidcServer = utilsoidc.BuildAndRunTestServer(t, caFilePath, caKeyFilePath)
|
||||||
|
|
||||||
@ -260,16 +244,16 @@ jwt:
|
|||||||
claim: sub
|
claim: sub
|
||||||
prefix: %s
|
prefix: %s
|
||||||
`, oidcServer.URL(), defaultOIDCClientID, indentCertificateAuthority(string(caCertContent)), defaultOIDCUsernamePrefix)
|
`, oidcServer.URL(), defaultOIDCClientID, indentCertificateAuthority(string(caCertContent)), defaultOIDCUsernamePrefix)
|
||||||
apiServer = startTestAPIServerForOIDC(t, "", "", "", authenticationConfig)
|
apiServer = startTestAPIServerForOIDC(t, "", "", "", authenticationConfig, &signingPrivateKey.PublicKey)
|
||||||
} else {
|
} else {
|
||||||
apiServer = startTestAPIServerForOIDC(t, oidcServer.URL(), defaultOIDCClientID, caFilePath, "")
|
apiServer = startTestAPIServerForOIDC(t, oidcServer.URL(), defaultOIDCClientID, caFilePath, "", &signingPrivateKey.PublicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
adminClient := kubernetes.NewForConfigOrDie(apiServer.ClientConfig)
|
adminClient := kubernetes.NewForConfigOrDie(apiServer.ClientConfig)
|
||||||
configureRBAC(t, adminClient, defaultRole, defaultRoleBinding)
|
configureRBAC(t, adminClient, defaultRole, defaultRoleBinding)
|
||||||
|
|
||||||
anotherSigningPrivateKey, wantErr := rsa.GenerateKey(rand.Reader, rsaKeyBitSize)
|
anotherSigningPrivateKey, _ := keyFunc(t)
|
||||||
require.NoError(t, wantErr)
|
|
||||||
oidcServer.JwksHandler().EXPECT().KeySet().AnyTimes().DoAndReturn(utilsoidc.DefaultJwksHandlerBehavior(t, &anotherSigningPrivateKey.PublicKey))
|
oidcServer.JwksHandler().EXPECT().KeySet().AnyTimes().DoAndReturn(utilsoidc.DefaultJwksHandlerBehavior(t, &anotherSigningPrivateKey.PublicKey))
|
||||||
|
|
||||||
return oidcServer, apiServer, signingPrivateKey, caCertContent, caFilePath
|
return oidcServer, apiServer, signingPrivateKey, caCertContent, caFilePath
|
||||||
@ -296,11 +280,69 @@ jwt:
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, singleTestRunner(useAuthenticationConfig, rsaGenerateKey, tt))
|
||||||
fn := func(t *testing.T, issuerURL, caCert string) string { return "" }
|
}
|
||||||
if useAuthenticationConfig {
|
|
||||||
fn = func(t *testing.T, issuerURL, caCert string) string {
|
for _, tt := range []singleTest[*ecdsa.PrivateKey, *ecdsa.PublicKey]{
|
||||||
return fmt.Sprintf(`
|
{
|
||||||
|
name: "ID token is ok",
|
||||||
|
configureInfrastructure: configureTestInfrastructure[*ecdsa.PrivateKey, *ecdsa.PublicKey],
|
||||||
|
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *ecdsa.PrivateKey) {
|
||||||
|
idTokenLifetime := time.Second * 1200
|
||||||
|
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
||||||
|
t,
|
||||||
|
signingPrivateKey,
|
||||||
|
map[string]interface{}{
|
||||||
|
"iss": oidcServer.URL(),
|
||||||
|
"sub": defaultOIDCClaimedUsername,
|
||||||
|
"aud": defaultOIDCClientID,
|
||||||
|
"exp": time.Now().Add(idTokenLifetime).Unix(),
|
||||||
|
},
|
||||||
|
defaultStubAccessToken,
|
||||||
|
defaultStubRefreshToken,
|
||||||
|
))
|
||||||
|
},
|
||||||
|
configureClient: configureClientFetchingOIDCCredentials,
|
||||||
|
assertErrFn: func(t *testing.T, errorToCheck error) {
|
||||||
|
assert.NoError(t, errorToCheck)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tt.name, singleTestRunner(useAuthenticationConfig, ecdsaGenerateKey, tt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type singleTest[K utilsoidc.JosePrivateKey, L utilsoidc.JosePublicKey] struct {
|
||||||
|
name string
|
||||||
|
configureInfrastructure func(t *testing.T, fn authenticationConfigFunc, keyFunc func(t *testing.T) (K, L)) (
|
||||||
|
oidcServer *utilsoidc.TestServer,
|
||||||
|
apiServer *kubeapiserverapptesting.TestServer,
|
||||||
|
signingPrivateKey K,
|
||||||
|
caCertContent []byte,
|
||||||
|
caFilePath string,
|
||||||
|
)
|
||||||
|
configureOIDCServerBehaviour func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey K)
|
||||||
|
configureClient func(
|
||||||
|
t *testing.T,
|
||||||
|
restCfg *rest.Config,
|
||||||
|
caCert []byte,
|
||||||
|
certPath,
|
||||||
|
oidcServerURL,
|
||||||
|
oidcServerTokenURL string,
|
||||||
|
) kubernetes.Interface
|
||||||
|
assertErrFn func(t *testing.T, errorToCheck error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func singleTestRunner[K utilsoidc.JosePrivateKey, L utilsoidc.JosePublicKey](
|
||||||
|
useAuthenticationConfig bool,
|
||||||
|
keyFunc func(t *testing.T) (K, L),
|
||||||
|
tt singleTest[K, L],
|
||||||
|
) func(t *testing.T) {
|
||||||
|
return func(t *testing.T) {
|
||||||
|
fn := func(t *testing.T, issuerURL, caCert string) string { return "" }
|
||||||
|
if useAuthenticationConfig {
|
||||||
|
fn = func(t *testing.T, issuerURL, caCert string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
apiVersion: apiserver.config.k8s.io/v1alpha1
|
apiVersion: apiserver.config.k8s.io/v1alpha1
|
||||||
kind: AuthenticationConfiguration
|
kind: AuthenticationConfiguration
|
||||||
jwt:
|
jwt:
|
||||||
@ -315,31 +357,32 @@ jwt:
|
|||||||
claim: sub
|
claim: sub
|
||||||
prefix: %s
|
prefix: %s
|
||||||
`, issuerURL, defaultOIDCClientID, indentCertificateAuthority(caCert), defaultOIDCUsernamePrefix)
|
`, issuerURL, defaultOIDCClientID, indentCertificateAuthority(caCert), defaultOIDCUsernamePrefix)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
oidcServer, apiServer, signingPrivateKey, caCert, certPath := tt.configureInfrastructure(t, fn)
|
}
|
||||||
|
oidcServer, apiServer, signingPrivateKey, caCert, certPath := tt.configureInfrastructure(t, fn, keyFunc)
|
||||||
|
|
||||||
tt.configureOIDCServerBehaviour(t, oidcServer, signingPrivateKey)
|
tt.configureOIDCServerBehaviour(t, oidcServer, signingPrivateKey)
|
||||||
|
|
||||||
tokenURL, err := oidcServer.TokenURL()
|
tokenURL, err := oidcServer.TokenURL()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
client := tt.configureClient(t, apiServer.ClientConfig, caCert, certPath, oidcServer.URL(), tokenURL)
|
client := tt.configureClient(t, apiServer.ClientConfig, caCert, certPath, oidcServer.URL(), tokenURL)
|
||||||
|
|
||||||
ctx := testContext(t)
|
ctx := testContext(t)
|
||||||
_, err = client.CoreV1().Pods(defaultNamespace).List(ctx, metav1.ListOptions{})
|
_, err = client.CoreV1().Pods(defaultNamespace).List(ctx, metav1.ListOptions{})
|
||||||
|
|
||||||
tt.assertErrFn(t, err)
|
tt.assertErrFn(t, err)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdatingRefreshTokenInCaseOfExpiredIDToken(t *testing.T) {
|
func TestUpdatingRefreshTokenInCaseOfExpiredIDToken(t *testing.T) {
|
||||||
var tests = []struct {
|
type testRun[K utilsoidc.JosePrivateKey] struct {
|
||||||
name string
|
name string
|
||||||
configureUpdatingTokenBehaviour func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey)
|
configureUpdatingTokenBehaviour func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey K)
|
||||||
assertErrFn func(t *testing.T, errorToCheck error)
|
assertErrFn func(t *testing.T, errorToCheck error)
|
||||||
}{
|
}
|
||||||
|
|
||||||
|
var tests = []testRun[*rsa.PrivateKey]{
|
||||||
{
|
{
|
||||||
name: "cache returns stale client if refresh token is not updated in config",
|
name: "cache returns stale client if refresh token is not updated in config",
|
||||||
configureUpdatingTokenBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
configureUpdatingTokenBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
||||||
@ -369,7 +412,7 @@ func TestUpdatingRefreshTokenInCaseOfExpiredIDToken(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
oidcServer, apiServer, signingPrivateKey, caCert, certPath := configureTestInfrastructure(t, func(t *testing.T, _, _ string) string { return "" })
|
oidcServer, apiServer, signingPrivateKey, caCert, certPath := configureTestInfrastructure(t, func(t *testing.T, _, _ string) string { return "" }, rsaGenerateKey)
|
||||||
|
|
||||||
tokenURL, err := oidcServer.TokenURL()
|
tokenURL, err := oidcServer.TokenURL()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -399,17 +442,17 @@ func TestUpdatingRefreshTokenInCaseOfExpiredIDToken(t *testing.T) {
|
|||||||
func TestStructuredAuthenticationConfigCEL(t *testing.T) {
|
func TestStructuredAuthenticationConfigCEL(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StructuredAuthenticationConfiguration, true)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StructuredAuthenticationConfiguration, true)()
|
||||||
|
|
||||||
tests := []struct {
|
type testRun[K utilsoidc.JosePrivateKey, L utilsoidc.JosePublicKey] struct {
|
||||||
name string
|
name string
|
||||||
authConfigFn authenticationConfigFunc
|
authConfigFn authenticationConfigFunc
|
||||||
configureInfrastructure func(t *testing.T, fn authenticationConfigFunc) (
|
configureInfrastructure func(t *testing.T, fn authenticationConfigFunc, keyFunc func(t *testing.T) (K, L)) (
|
||||||
oidcServer *utilsoidc.TestServer,
|
oidcServer *utilsoidc.TestServer,
|
||||||
apiServer *kubeapiserverapptesting.TestServer,
|
apiServer *kubeapiserverapptesting.TestServer,
|
||||||
signingPrivateKey *rsa.PrivateKey,
|
signingPrivateKey *rsa.PrivateKey,
|
||||||
caCertContent []byte,
|
caCertContent []byte,
|
||||||
caFilePath string,
|
caFilePath string,
|
||||||
)
|
)
|
||||||
configureOIDCServerBehaviour func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey)
|
configureOIDCServerBehaviour func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey K)
|
||||||
configureClient func(
|
configureClient func(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
restCfg *rest.Config,
|
restCfg *rest.Config,
|
||||||
@ -420,7 +463,9 @@ func TestStructuredAuthenticationConfigCEL(t *testing.T) {
|
|||||||
) kubernetes.Interface
|
) kubernetes.Interface
|
||||||
assertErrFn func(t *testing.T, errorToCheck error)
|
assertErrFn func(t *testing.T, errorToCheck error)
|
||||||
wantUser *authenticationv1.UserInfo
|
wantUser *authenticationv1.UserInfo
|
||||||
}{
|
}
|
||||||
|
|
||||||
|
tests := []testRun[*rsa.PrivateKey, *rsa.PublicKey]{
|
||||||
{
|
{
|
||||||
name: "username CEL expression is ok",
|
name: "username CEL expression is ok",
|
||||||
authConfigFn: func(t *testing.T, issuerURL, caCert string) string {
|
authConfigFn: func(t *testing.T, issuerURL, caCert string) string {
|
||||||
@ -441,7 +486,7 @@ jwt:
|
|||||||
expression: "'k8s-' + claims.sub"
|
expression: "'k8s-' + claims.sub"
|
||||||
`, issuerURL, defaultOIDCClientID, indentCertificateAuthority(caCert))
|
`, issuerURL, defaultOIDCClientID, indentCertificateAuthority(caCert))
|
||||||
},
|
},
|
||||||
configureInfrastructure: configureTestInfrastructure,
|
configureInfrastructure: configureTestInfrastructure[*rsa.PrivateKey, *rsa.PublicKey],
|
||||||
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
||||||
idTokenLifetime := time.Second * 1200
|
idTokenLifetime := time.Second * 1200
|
||||||
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
||||||
@ -488,7 +533,7 @@ jwt:
|
|||||||
expression: '(claims.roles.split(",") + claims.other_roles.split(",")).map(role, "prefix:" + role)'
|
expression: '(claims.roles.split(",") + claims.other_roles.split(",")).map(role, "prefix:" + role)'
|
||||||
`, issuerURL, defaultOIDCClientID, indentCertificateAuthority(caCert))
|
`, issuerURL, defaultOIDCClientID, indentCertificateAuthority(caCert))
|
||||||
},
|
},
|
||||||
configureInfrastructure: configureTestInfrastructure,
|
configureInfrastructure: configureTestInfrastructure[*rsa.PrivateKey, *rsa.PublicKey],
|
||||||
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
||||||
idTokenLifetime := time.Second * 1200
|
idTokenLifetime := time.Second * 1200
|
||||||
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
||||||
@ -538,7 +583,7 @@ jwt:
|
|||||||
message: "the hd claim must be set to example.com"
|
message: "the hd claim must be set to example.com"
|
||||||
`, issuerURL, defaultOIDCClientID, indentCertificateAuthority(caCert))
|
`, issuerURL, defaultOIDCClientID, indentCertificateAuthority(caCert))
|
||||||
},
|
},
|
||||||
configureInfrastructure: configureTestInfrastructure,
|
configureInfrastructure: configureTestInfrastructure[*rsa.PrivateKey, *rsa.PublicKey],
|
||||||
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
||||||
idTokenLifetime := time.Second * 1200
|
idTokenLifetime := time.Second * 1200
|
||||||
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
||||||
@ -588,7 +633,7 @@ jwt:
|
|||||||
message: "example.org/foo must be bar and example.org/baz must be qux"
|
message: "example.org/foo must be bar and example.org/baz must be qux"
|
||||||
`, issuerURL, defaultOIDCClientID, indentCertificateAuthority(caCert))
|
`, issuerURL, defaultOIDCClientID, indentCertificateAuthority(caCert))
|
||||||
},
|
},
|
||||||
configureInfrastructure: configureTestInfrastructure,
|
configureInfrastructure: configureTestInfrastructure[*rsa.PrivateKey, *rsa.PublicKey],
|
||||||
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
||||||
idTokenLifetime := time.Second * 1200
|
idTokenLifetime := time.Second * 1200
|
||||||
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
||||||
@ -640,7 +685,7 @@ jwt:
|
|||||||
expression: "claims.uid"
|
expression: "claims.uid"
|
||||||
`, issuerURL, defaultOIDCClientID, indentCertificateAuthority(caCert))
|
`, issuerURL, defaultOIDCClientID, indentCertificateAuthority(caCert))
|
||||||
},
|
},
|
||||||
configureInfrastructure: configureTestInfrastructure,
|
configureInfrastructure: configureTestInfrastructure[*rsa.PrivateKey, *rsa.PublicKey],
|
||||||
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
||||||
idTokenLifetime := time.Second * 1200
|
idTokenLifetime := time.Second * 1200
|
||||||
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
||||||
@ -692,7 +737,7 @@ jwt:
|
|||||||
message: "groups cannot used reserved system: prefix"
|
message: "groups cannot used reserved system: prefix"
|
||||||
`, issuerURL, defaultOIDCClientID, indentCertificateAuthority(caCert))
|
`, issuerURL, defaultOIDCClientID, indentCertificateAuthority(caCert))
|
||||||
},
|
},
|
||||||
configureInfrastructure: configureTestInfrastructure,
|
configureInfrastructure: configureTestInfrastructure[*rsa.PrivateKey, *rsa.PublicKey],
|
||||||
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
||||||
idTokenLifetime := time.Second * 1200
|
idTokenLifetime := time.Second * 1200
|
||||||
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
||||||
@ -720,7 +765,7 @@ jwt:
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
oidcServer, apiServer, signingPrivateKey, caCert, certPath := tt.configureInfrastructure(t, tt.authConfigFn)
|
oidcServer, apiServer, signingPrivateKey, caCert, certPath := tt.configureInfrastructure(t, tt.authConfigFn, rsaGenerateKey)
|
||||||
|
|
||||||
tt.configureOIDCServerBehaviour(t, oidcServer, signingPrivateKey)
|
tt.configureOIDCServerBehaviour(t, oidcServer, signingPrivateKey)
|
||||||
|
|
||||||
@ -743,10 +788,28 @@ jwt:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureTestInfrastructure(t *testing.T, fn authenticationConfigFunc) (
|
func rsaGenerateKey(t *testing.T) (*rsa.PrivateKey, *rsa.PublicKey) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
privateKey, err := rsa.GenerateKey(rand.Reader, rsaKeyBitSize)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return privateKey, &privateKey.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func ecdsaGenerateKey(t *testing.T) (*ecdsa.PrivateKey, *ecdsa.PublicKey) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return privateKey, &privateKey.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func configureTestInfrastructure[K utilsoidc.JosePrivateKey, L utilsoidc.JosePublicKey](t *testing.T, fn authenticationConfigFunc, keyFunc func(t *testing.T) (K, L)) (
|
||||||
oidcServer *utilsoidc.TestServer,
|
oidcServer *utilsoidc.TestServer,
|
||||||
apiServer *kubeapiserverapptesting.TestServer,
|
apiServer *kubeapiserverapptesting.TestServer,
|
||||||
signingPrivateKey *rsa.PrivateKey,
|
signingPrivateKey K,
|
||||||
caCertContent []byte,
|
caCertContent []byte,
|
||||||
caFilePath string,
|
caFilePath string,
|
||||||
) {
|
) {
|
||||||
@ -754,19 +817,18 @@ func configureTestInfrastructure(t *testing.T, fn authenticationConfigFunc) (
|
|||||||
|
|
||||||
caCertContent, _, caFilePath, caKeyFilePath := generateCert(t)
|
caCertContent, _, caFilePath, caKeyFilePath := generateCert(t)
|
||||||
|
|
||||||
signingPrivateKey, err := rsa.GenerateKey(rand.Reader, rsaKeyBitSize)
|
signingPrivateKey, publicKey := keyFunc(t)
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
oidcServer = utilsoidc.BuildAndRunTestServer(t, caFilePath, caKeyFilePath)
|
oidcServer = utilsoidc.BuildAndRunTestServer(t, caFilePath, caKeyFilePath)
|
||||||
|
|
||||||
authenticationConfig := fn(t, oidcServer.URL(), string(caCertContent))
|
authenticationConfig := fn(t, oidcServer.URL(), string(caCertContent))
|
||||||
if len(authenticationConfig) > 0 {
|
if len(authenticationConfig) > 0 {
|
||||||
apiServer = startTestAPIServerForOIDC(t, "", "", "", authenticationConfig)
|
apiServer = startTestAPIServerForOIDC(t, "", "", "", authenticationConfig, publicKey)
|
||||||
} else {
|
} else {
|
||||||
apiServer = startTestAPIServerForOIDC(t, oidcServer.URL(), defaultOIDCClientID, caFilePath, "")
|
apiServer = startTestAPIServerForOIDC(t, oidcServer.URL(), defaultOIDCClientID, caFilePath, "", publicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
oidcServer.JwksHandler().EXPECT().KeySet().AnyTimes().DoAndReturn(utilsoidc.DefaultJwksHandlerBehavior(t, &signingPrivateKey.PublicKey))
|
oidcServer.JwksHandler().EXPECT().KeySet().AnyTimes().DoAndReturn(utilsoidc.DefaultJwksHandlerBehavior(t, publicKey))
|
||||||
|
|
||||||
adminClient := kubernetes.NewForConfigOrDie(apiServer.ClientConfig)
|
adminClient := kubernetes.NewForConfigOrDie(apiServer.ClientConfig)
|
||||||
configureRBAC(t, adminClient, defaultRole, defaultRoleBinding)
|
configureRBAC(t, adminClient, defaultRole, defaultRoleBinding)
|
||||||
@ -815,7 +877,7 @@ func configureClientConfigForOIDC(t *testing.T, config *rest.Config, clientID, c
|
|||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
func startTestAPIServerForOIDC(t *testing.T, oidcURL, oidcClientID, oidcCAFilePath, authenticationConfigYAML string) *kubeapiserverapptesting.TestServer {
|
func startTestAPIServerForOIDC[L utilsoidc.JosePublicKey](t *testing.T, oidcURL, oidcClientID, oidcCAFilePath, authenticationConfigYAML string, publicKey L) *kubeapiserverapptesting.TestServer {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
var customFlags []string
|
var customFlags []string
|
||||||
@ -828,6 +890,7 @@ func startTestAPIServerForOIDC(t *testing.T, oidcURL, oidcClientID, oidcCAFilePa
|
|||||||
fmt.Sprintf("--oidc-ca-file=%s", oidcCAFilePath),
|
fmt.Sprintf("--oidc-ca-file=%s", oidcCAFilePath),
|
||||||
fmt.Sprintf("--oidc-username-prefix=%s", defaultOIDCUsernamePrefix),
|
fmt.Sprintf("--oidc-username-prefix=%s", defaultOIDCUsernamePrefix),
|
||||||
}
|
}
|
||||||
|
customFlags = append(customFlags, maybeSetSigningAlgs(publicKey)...)
|
||||||
}
|
}
|
||||||
customFlags = append(customFlags, "--authorization-mode=RBAC")
|
customFlags = append(customFlags, "--authorization-mode=RBAC")
|
||||||
|
|
||||||
@ -844,6 +907,18 @@ func startTestAPIServerForOIDC(t *testing.T, oidcURL, oidcClientID, oidcCAFilePa
|
|||||||
return &server
|
return &server
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func maybeSetSigningAlgs[K utilsoidc.JoseKey](key K) []string {
|
||||||
|
alg := utilsoidc.GetSignatureAlgorithm(key)
|
||||||
|
if alg == jose.RS256 && randomBool() {
|
||||||
|
return nil // check the default case of RS256 by not always setting the flag
|
||||||
|
}
|
||||||
|
return []string{
|
||||||
|
fmt.Sprintf("--oidc-signing-algs=%s", alg), // all other algs need to be manually set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomBool() bool { return utilrand.Int()%2 == 1 }
|
||||||
|
|
||||||
func fetchOIDCCredentials(t *testing.T, oidcTokenURL string, caCertContent []byte) (idToken, refreshToken string) {
|
func fetchOIDCCredentials(t *testing.T, oidcTokenURL string, caCertContent []byte) (idToken, refreshToken string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ package oidc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
@ -170,17 +171,21 @@ func BuildAndRunTestServer(t *testing.T, caPath, caKeyPath string) *TestServer {
|
|||||||
return oidcServer
|
return oidcServer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type JosePrivateKey interface {
|
||||||
|
*rsa.PrivateKey | *ecdsa.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
// TokenHandlerBehaviorReturningPredefinedJWT describes the scenario when signed JWT token is being created.
|
// TokenHandlerBehaviorReturningPredefinedJWT describes the scenario when signed JWT token is being created.
|
||||||
// This behavior should being applied to the MockTokenHandler.
|
// This behavior should being applied to the MockTokenHandler.
|
||||||
func TokenHandlerBehaviorReturningPredefinedJWT(
|
func TokenHandlerBehaviorReturningPredefinedJWT[K JosePrivateKey](
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
rsaPrivateKey *rsa.PrivateKey,
|
privateKey K,
|
||||||
claims map[string]interface{}, accessToken, refreshToken string,
|
claims map[string]interface{}, accessToken, refreshToken string,
|
||||||
) func() (Token, error) {
|
) func() (Token, error) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
return func() (Token, error) {
|
return func() (Token, error) {
|
||||||
signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.RS256, Key: rsaPrivateKey}, nil)
|
signer, err := jose.NewSigner(jose.SigningKey{Algorithm: GetSignatureAlgorithm(privateKey), Key: privateKey}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
payloadJSON, err := json.Marshal(claims)
|
payloadJSON, err := json.Marshal(claims)
|
||||||
@ -199,13 +204,17 @@ func TokenHandlerBehaviorReturningPredefinedJWT(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type JosePublicKey interface {
|
||||||
|
*rsa.PublicKey | *ecdsa.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
// DefaultJwksHandlerBehavior describes the scenario when JSON Web Key Set token is being returned.
|
// DefaultJwksHandlerBehavior describes the scenario when JSON Web Key Set token is being returned.
|
||||||
// This behavior should being applied to the MockJWKsHandler.
|
// This behavior should being applied to the MockJWKsHandler.
|
||||||
func DefaultJwksHandlerBehavior(t *testing.T, verificationPublicKey *rsa.PublicKey) func() jose.JSONWebKeySet {
|
func DefaultJwksHandlerBehavior[K JosePublicKey](t *testing.T, verificationPublicKey K) func() jose.JSONWebKeySet {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
return func() jose.JSONWebKeySet {
|
return func() jose.JSONWebKeySet {
|
||||||
key := jose.JSONWebKey{Key: verificationPublicKey, Use: "sig", Algorithm: string(jose.RS256)}
|
key := jose.JSONWebKey{Key: verificationPublicKey, Use: "sig", Algorithm: string(GetSignatureAlgorithm(verificationPublicKey))}
|
||||||
|
|
||||||
thumbprint, err := key.Thumbprint(crypto.SHA256)
|
thumbprint, err := key.Thumbprint(crypto.SHA256)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -216,3 +225,16 @@ func DefaultJwksHandlerBehavior(t *testing.T, verificationPublicKey *rsa.PublicK
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type JoseKey interface{ JosePrivateKey | JosePublicKey }
|
||||||
|
|
||||||
|
func GetSignatureAlgorithm[K JoseKey](key K) jose.SignatureAlgorithm {
|
||||||
|
switch any(key).(type) {
|
||||||
|
case *rsa.PrivateKey, *rsa.PublicKey:
|
||||||
|
return jose.RS256
|
||||||
|
case *ecdsa.PrivateKey, *ecdsa.PublicKey:
|
||||||
|
return jose.ES256
|
||||||
|
default:
|
||||||
|
panic("unknown key type") // should be impossible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user