Backfill test for token endpoint error when JWK is not yet available
Signed-off-by: Ryan Richard <richardry@vmware.com>
This commit is contained in:
parent
e0b6133bf1
commit
648fa4b9ba
@ -50,7 +50,7 @@ func (s *dynamicOpenIDConnectECDSAStrategy) GenerateIDToken(
|
|||||||
_, activeJwk := s.jwksProvider.GetJWKS(s.fositeConfig.IDTokenIssuer)
|
_, activeJwk := s.jwksProvider.GetJWKS(s.fositeConfig.IDTokenIssuer)
|
||||||
if activeJwk == nil {
|
if activeJwk == nil {
|
||||||
plog.Debug("no JWK found for issuer", "issuer", s.fositeConfig.IDTokenIssuer)
|
plog.Debug("no JWK found for issuer", "issuer", s.fositeConfig.IDTokenIssuer)
|
||||||
return "", constable.Error("no JWK found for issuer")
|
return "", fosite.ErrTemporarilyUnavailable.WithCause(constable.Error("no JWK found for issuer"))
|
||||||
}
|
}
|
||||||
key, ok := activeJwk.Key.(*ecdsa.PrivateKey)
|
key, ok := activeJwk.Key.(*ecdsa.PrivateKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -65,7 +65,7 @@ func (s *dynamicOpenIDConnectECDSAStrategy) GenerateIDToken(
|
|||||||
"actualType",
|
"actualType",
|
||||||
actualType,
|
actualType,
|
||||||
)
|
)
|
||||||
return "", constable.Error("JWK must be of type ecdsa")
|
return "", fosite.ErrServerError.WithCause(constable.Error("JWK must be of type ecdsa"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return compose.NewOpenIDConnectECDSAStrategy(s.fositeConfig, key).GenerateIDToken(ctx, requester)
|
return compose.NewOpenIDConnectECDSAStrategy(s.fositeConfig, key).GenerateIDToken(ctx, requester)
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
|
"errors"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -42,7 +43,8 @@ func TestDynamicOpenIDConnectECDSAStrategy(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
issuer string
|
issuer string
|
||||||
jwksProvider func(jwks.DynamicJWKSProvider)
|
jwksProvider func(jwks.DynamicJWKSProvider)
|
||||||
wantError string
|
wantErrorType *fosite.RFC6749Error
|
||||||
|
wantErrorCause string
|
||||||
wantSigningJWK *jose.JSONWebKey
|
wantSigningJWK *jose.JSONWebKey
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@ -63,9 +65,10 @@ func TestDynamicOpenIDConnectECDSAStrategy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "jwks provider does not contain signing key for issuer",
|
name: "jwks provider does not contain signing key for issuer",
|
||||||
issuer: goodIssuer,
|
issuer: goodIssuer,
|
||||||
wantError: "no JWK found for issuer",
|
wantErrorType: fosite.ErrTemporarilyUnavailable,
|
||||||
|
wantErrorCause: "no JWK found for issuer",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "jwks provider contains signing key of wrong type for issuer",
|
name: "jwks provider contains signing key of wrong type for issuer",
|
||||||
@ -80,7 +83,8 @@ func TestDynamicOpenIDConnectECDSAStrategy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
wantError: "JWK must be of type ecdsa",
|
wantErrorType: fosite.ErrServerError,
|
||||||
|
wantErrorCause: "JWK must be of type ecdsa",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@ -111,8 +115,9 @@ func TestDynamicOpenIDConnectECDSAStrategy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
idToken, err := s.GenerateIDToken(context.Background(), requester)
|
idToken, err := s.GenerateIDToken(context.Background(), requester)
|
||||||
if test.wantError != "" {
|
if test.wantErrorType != nil {
|
||||||
require.EqualError(t, err, test.wantError)
|
require.True(t, errors.Is(err, test.wantErrorType))
|
||||||
|
require.EqualError(t, err.(*fosite.RFC6749Error).Cause(), test.wantErrorCause)
|
||||||
} else {
|
} else {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -183,6 +183,15 @@ var (
|
|||||||
"status_code": 400
|
"status_code": 400
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
fositeTemporarilyUnavailableErrorBody = here.Doc(`
|
||||||
|
{
|
||||||
|
"error": "temporarily_unavailable",
|
||||||
|
"error_description": "The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server",
|
||||||
|
"error_verbose": "The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server",
|
||||||
|
"status_code": 503
|
||||||
|
}
|
||||||
|
`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTokenEndpoint(t *testing.T) {
|
func TestTokenEndpoint(t *testing.T) {
|
||||||
@ -214,7 +223,18 @@ func TestTokenEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
authCode string,
|
authCode string,
|
||||||
)
|
)
|
||||||
request func(r *http.Request, authCode string)
|
request func(r *http.Request, authCode string)
|
||||||
|
makeOathHelper func(
|
||||||
|
t *testing.T,
|
||||||
|
authRequest *http.Request,
|
||||||
|
store interface {
|
||||||
|
oauth2.TokenRevocationStorage
|
||||||
|
oauth2.CoreStorage
|
||||||
|
openid.OpenIDConnectRequestStorage
|
||||||
|
pkce.PKCERequestStorage
|
||||||
|
fosite.ClientManager
|
||||||
|
},
|
||||||
|
) (fosite.OAuth2Provider, string, *ecdsa.PrivateKey)
|
||||||
|
|
||||||
wantStatus int
|
wantStatus int
|
||||||
wantBodyFields []string
|
wantBodyFields []string
|
||||||
@ -381,6 +401,12 @@ func TestTokenEndpoint(t *testing.T) {
|
|||||||
wantStatus: http.StatusBadRequest,
|
wantStatus: http.StatusBadRequest,
|
||||||
wantExactBody: fositeWrongPKCEVerifierErrorBody,
|
wantExactBody: fositeWrongPKCEVerifierErrorBody,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "private signing key for JWTs has not yet been provided by the controller who is responsible for dynamically providing it",
|
||||||
|
makeOathHelper: makeOauthHelperWithNilPrivateJWTSigningKey,
|
||||||
|
wantStatus: http.StatusServiceUnavailable,
|
||||||
|
wantExactBody: fositeTemporarilyUnavailableErrorBody,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
test := test
|
test := test
|
||||||
@ -393,8 +419,16 @@ func TestTokenEndpoint(t *testing.T) {
|
|||||||
client := fake.NewSimpleClientset()
|
client := fake.NewSimpleClientset()
|
||||||
secrets := client.CoreV1().Secrets("some-namespace")
|
secrets := client.CoreV1().Secrets("some-namespace")
|
||||||
|
|
||||||
|
var oauthHelper fosite.OAuth2Provider
|
||||||
|
var authCode string
|
||||||
|
var jwtSigningKey *ecdsa.PrivateKey
|
||||||
|
|
||||||
oauthStore := oidc.NewKubeStorage(secrets)
|
oauthStore := oidc.NewKubeStorage(secrets)
|
||||||
oauthHelper, authCode, jwtSigningKey := makeHappyOauthHelper(t, authRequest, oauthStore)
|
if test.makeOathHelper != nil {
|
||||||
|
oauthHelper, authCode, jwtSigningKey = test.makeOathHelper(t, authRequest, oauthStore)
|
||||||
|
} else {
|
||||||
|
oauthHelper, authCode, jwtSigningKey = makeHappyOauthHelper(t, authRequest, oauthStore)
|
||||||
|
}
|
||||||
|
|
||||||
if test.storage != nil {
|
if test.storage != nil {
|
||||||
test.storage(t, oauthStore, authCode)
|
test.storage(t, oauthStore, authCode)
|
||||||
@ -595,7 +629,30 @@ func makeHappyOauthHelper(
|
|||||||
|
|
||||||
jwtSigningKey, jwkProvider := generateJWTSigningKeyAndJWKSProvider(t, goodIssuer)
|
jwtSigningKey, jwkProvider := generateJWTSigningKeyAndJWKSProvider(t, goodIssuer)
|
||||||
oauthHelper := oidc.FositeOauth2Helper(store, goodIssuer, []byte(hmacSecret), jwkProvider)
|
oauthHelper := oidc.FositeOauth2Helper(store, goodIssuer, []byte(hmacSecret), jwkProvider)
|
||||||
|
authResponder := simulateAuthEndpointHavingAlreadyRun(t, authRequest, oauthHelper)
|
||||||
|
return oauthHelper, authResponder.GetCode(), jwtSigningKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeOauthHelperWithNilPrivateJWTSigningKey(
|
||||||
|
t *testing.T,
|
||||||
|
authRequest *http.Request,
|
||||||
|
store interface {
|
||||||
|
oauth2.TokenRevocationStorage
|
||||||
|
oauth2.CoreStorage
|
||||||
|
openid.OpenIDConnectRequestStorage
|
||||||
|
pkce.PKCERequestStorage
|
||||||
|
fosite.ClientManager
|
||||||
|
},
|
||||||
|
) (fosite.OAuth2Provider, string, *ecdsa.PrivateKey) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
jwkProvider := jwks.NewDynamicJWKSProvider() // empty provider which contains no signing key for this issuer
|
||||||
|
oauthHelper := oidc.FositeOauth2Helper(store, goodIssuer, []byte(hmacSecret), jwkProvider)
|
||||||
|
authResponder := simulateAuthEndpointHavingAlreadyRun(t, authRequest, oauthHelper)
|
||||||
|
return oauthHelper, authResponder.GetCode(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func simulateAuthEndpointHavingAlreadyRun(t *testing.T, authRequest *http.Request, oauthHelper fosite.OAuth2Provider) fosite.AuthorizeResponder {
|
||||||
// Simulate the auth endpoint running so Fosite code will fill the store with realistic values.
|
// Simulate the auth endpoint running so Fosite code will fill the store with realistic values.
|
||||||
//
|
//
|
||||||
// We only set the fields in the session that Fosite wants us to set.
|
// We only set the fields in the session that Fosite wants us to set.
|
||||||
@ -616,8 +673,7 @@ func makeHappyOauthHelper(
|
|||||||
}
|
}
|
||||||
authResponder, err := oauthHelper.NewAuthorizeResponse(ctx, authRequester, session)
|
authResponder, err := oauthHelper.NewAuthorizeResponse(ctx, authRequester, session)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
return authResponder
|
||||||
return oauthHelper, authResponder.GetCode(), jwtSigningKey
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateJWTSigningKeyAndJWKSProvider(t *testing.T, issuer string) (*ecdsa.PrivateKey, jwks.DynamicJWKSProvider) {
|
func generateJWTSigningKeyAndJWKSProvider(t *testing.T, issuer string) (*ecdsa.PrivateKey, jwks.DynamicJWKSProvider) {
|
||||||
|
@ -68,7 +68,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
return proxyURL, nil
|
return proxyURL, nil
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
oidcHttpClientContext := oidc.ClientContext(ctx, httpClient)
|
oidcHTTPClientContext := oidc.ClientContext(ctx, httpClient)
|
||||||
|
|
||||||
// Use the CA to issue a TLS server cert.
|
// Use the CA to issue a TLS server cert.
|
||||||
t.Logf("issuing test certificate")
|
t.Logf("issuing test certificate")
|
||||||
@ -111,7 +111,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
// Perform OIDC discovery for our downstream.
|
// Perform OIDC discovery for our downstream.
|
||||||
var discovery *oidc.Provider
|
var discovery *oidc.Provider
|
||||||
assert.Eventually(t, func() bool {
|
assert.Eventually(t, func() bool {
|
||||||
discovery, err = oidc.NewProvider(oidcHttpClientContext, downstream.Spec.Issuer)
|
discovery, err = oidc.NewProvider(oidcHTTPClientContext, downstream.Spec.Issuer)
|
||||||
return err == nil
|
return err == nil
|
||||||
}, 30*time.Second, 200*time.Millisecond)
|
}, 30*time.Second, 200*time.Millisecond)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -164,7 +164,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
require.NotEmpty(t, authcode)
|
require.NotEmpty(t, authcode)
|
||||||
|
|
||||||
// Call the token endpoint to get tokens.
|
// Call the token endpoint to get tokens.
|
||||||
tokenResponse, err := downstreamOAuth2Config.Exchange(oidcHttpClientContext, authcode, pkceParam.Verifier())
|
tokenResponse, err := downstreamOAuth2Config.Exchange(oidcHTTPClientContext, authcode, pkceParam.Verifier())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Verify the ID Token.
|
// Verify the ID Token.
|
||||||
|
Loading…
Reference in New Issue
Block a user