Passing signing key through to the token endpoint
This commit is contained in:
parent
58237d0e7d
commit
0bb2b10b3b
@ -5,6 +5,9 @@ package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
|
||||
"go.pinniped.dev/internal/constable"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/fosite/compose"
|
||||
@ -15,7 +18,6 @@ import (
|
||||
|
||||
// TODO: doc me.
|
||||
type dynamicOpenIDConnectECDSAStrategy struct {
|
||||
issuer string
|
||||
fositeConfig *compose.Config
|
||||
jwksProvider jwks.DynamicJWKSProvider
|
||||
}
|
||||
@ -23,12 +25,10 @@ type dynamicOpenIDConnectECDSAStrategy struct {
|
||||
var _ openid.OpenIDConnectTokenStrategy = &dynamicOpenIDConnectECDSAStrategy{}
|
||||
|
||||
func newDynamicOpenIDConnectECDSAStrategy(
|
||||
issuer string,
|
||||
fositeConfig *compose.Config,
|
||||
jwksProvider jwks.DynamicJWKSProvider,
|
||||
) *dynamicOpenIDConnectECDSAStrategy {
|
||||
return &dynamicOpenIDConnectECDSAStrategy{
|
||||
issuer: issuer,
|
||||
fositeConfig: fositeConfig,
|
||||
jwksProvider: jwksProvider,
|
||||
}
|
||||
@ -38,5 +38,15 @@ func (s *dynamicOpenIDConnectECDSAStrategy) GenerateIDToken(
|
||||
ctx context.Context,
|
||||
requester fosite.Requester,
|
||||
) (string, error) {
|
||||
return "", nil
|
||||
_, activeJwk := s.jwksProvider.GetJWKS(s.fositeConfig.IDTokenIssuer)
|
||||
if activeJwk == nil {
|
||||
return "", constable.Error("No JWK found for issuer")
|
||||
}
|
||||
key, ok := activeJwk.Key.(*ecdsa.PrivateKey)
|
||||
if !ok {
|
||||
return "", constable.Error("JWK must be of type ecdsa")
|
||||
}
|
||||
|
||||
// todo write story/issue about caching this strategy
|
||||
return compose.NewOpenIDConnectECDSAStrategy(s.fositeConfig, key).GenerateIDToken(ctx, requester)
|
||||
}
|
||||
|
@ -16,15 +16,20 @@ import (
|
||||
coreosoidc "github.com/coreos/go-oidc"
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/fosite/compose"
|
||||
"github.com/ory/fosite/handler/openid"
|
||||
"github.com/ory/fosite/token/jwt"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.pinniped.dev/internal/oidc/jwks"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"go.pinniped.dev/internal/oidc/jwks"
|
||||
)
|
||||
|
||||
func TestDynamicOpenIDConnectECDSAStrategy(t *testing.T) {
|
||||
const (
|
||||
goodIssuer = "https://some-good-issuer.com"
|
||||
clientID = "some-client-id"
|
||||
goodIssuer = "https://some-good-issuer.com"
|
||||
clientID = "some-client-id"
|
||||
goodSubject = "some-subject"
|
||||
goodUsername = "some-username"
|
||||
)
|
||||
|
||||
ecPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
@ -60,7 +65,7 @@ func TestDynamicOpenIDConnectECDSAStrategy(t *testing.T) {
|
||||
{
|
||||
name: "jwks provider does not contain signing key for issuer",
|
||||
issuer: goodIssuer,
|
||||
wantError: "some unkonwn key error",
|
||||
wantError: "No JWK found for issuer",
|
||||
},
|
||||
{
|
||||
name: "jwks provider contains signing key of wrong type for issuer",
|
||||
@ -75,7 +80,7 @@ func TestDynamicOpenIDConnectECDSAStrategy(t *testing.T) {
|
||||
},
|
||||
)
|
||||
},
|
||||
wantError: "some invalid key type error",
|
||||
wantError: "JWK must be of type ecdsa",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
@ -85,13 +90,22 @@ func TestDynamicOpenIDConnectECDSAStrategy(t *testing.T) {
|
||||
if test.jwksProvider != nil {
|
||||
test.jwksProvider(jwksProvider)
|
||||
}
|
||||
s := newDynamicOpenIDConnectECDSAStrategy(test.issuer, &compose.Config{}, jwksProvider)
|
||||
s := newDynamicOpenIDConnectECDSAStrategy(
|
||||
&compose.Config{IDTokenIssuer: test.issuer},
|
||||
jwksProvider,
|
||||
)
|
||||
|
||||
requester := &fosite.Request{
|
||||
Client: &fosite.DefaultClient{
|
||||
ID: clientID,
|
||||
},
|
||||
// Session: fositeopenid.DefaultSession{},
|
||||
Session: &openid.DefaultSession{
|
||||
Claims: &jwt.IDTokenClaims{
|
||||
Subject: goodSubject,
|
||||
},
|
||||
Subject: goodSubject,
|
||||
Username: goodUsername,
|
||||
},
|
||||
}
|
||||
idToken, err := s.GenerateIDToken(context.Background(), requester)
|
||||
if test.wantError != "" {
|
||||
|
@ -122,7 +122,7 @@ func FositeOauth2Helper(
|
||||
&compose.CommonStrategy{
|
||||
// Note that Fosite requires the HMAC secret to be at least 32 bytes.
|
||||
CoreStrategy: compose.NewOAuth2HMACStrategy(oauthConfig, hmacSecretOfLengthAtLeast32, nil),
|
||||
OpenIDConnectTokenStrategy: newDynamicOpenIDConnectECDSAStrategy(issuer, oauthConfig, jwksProvider),
|
||||
OpenIDConnectTokenStrategy: newDynamicOpenIDConnectECDSAStrategy(oauthConfig, jwksProvider),
|
||||
// OpenIDConnectTokenStrategy: compose.NewOpenIDConnectECDSAStrategy(oauthConfig, jwtSigningKey),
|
||||
},
|
||||
nil, // hasher, defaults to using BCrypt when nil. Used for hashing client secrets.
|
||||
|
@ -82,7 +82,7 @@ func (m *Manager) SetProviders(oidcProviders ...*provider.OIDCProvider) {
|
||||
oauthHelperWithNullStorage := oidc.FositeOauth2Helper(oidc.NullStorage{}, issuer, fositeHMACSecretForThisProvider, nil)
|
||||
|
||||
// For all the other endpoints, make another oauth helper with exactly the same settings except use real storage.
|
||||
oauthHelperWithKubeStorage := oidc.FositeOauth2Helper(oidc.NewKubeStorage(m.secretsClient), issuer, fositeHMACSecretForThisProvider, nil)
|
||||
oauthHelperWithKubeStorage := oidc.FositeOauth2Helper(oidc.NewKubeStorage(m.secretsClient), issuer, fositeHMACSecretForThisProvider, m.dynamicJWKSProvider)
|
||||
|
||||
// TODO use different codecs for the state and the cookie, because:
|
||||
// 1. we would like to state to have an embedded expiration date while the cookie does not need that
|
||||
|
@ -159,7 +159,7 @@ func TestManager(t *testing.T) {
|
||||
return actualLocationQueryParams.Get("code")
|
||||
}
|
||||
|
||||
requireTokenRequestToBeHandled := func(requestIssuer, authCode string, jwks *jose.JSONWebKeySet) {
|
||||
requireTokenRequestToBeHandled := func(requestIssuer, authCode string, jwks *jose.JSONWebKeySet, jwkIssuer string) {
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
numberOfKubeActionsBeforeThisRequest := len(kubeClient.Actions())
|
||||
@ -194,7 +194,7 @@ func TestManager(t *testing.T) {
|
||||
|
||||
keySet := newStaticKeySet(privateKey.Public())
|
||||
verifyConfig := coreosoidc.Config{ClientID: downstreamClientID, SupportedSigningAlgs: []string{coreosoidc.ES256}}
|
||||
verifier := coreosoidc.NewVerifier(requestIssuer, keySet, &verifyConfig)
|
||||
verifier := coreosoidc.NewVerifier(jwkIssuer, keySet, &verifyConfig)
|
||||
_, err := verifier.Verify(context.Background(), idToken)
|
||||
r.NoError(err)
|
||||
|
||||
@ -326,16 +326,16 @@ func TestManager(t *testing.T) {
|
||||
downstreamAuthCode1 := requireCallbackRequestToBeHandled(issuer1, callbackRequestParams, csrfCookieValue)
|
||||
downstreamAuthCode2 := requireCallbackRequestToBeHandled(issuer2, callbackRequestParams, csrfCookieValue)
|
||||
|
||||
// // Hostnames are case-insensitive, so test that we can handle that.
|
||||
// Hostnames are case-insensitive, so test that we can handle that.
|
||||
downstreamAuthCode3 := requireCallbackRequestToBeHandled(issuer1DifferentCaseHostname, callbackRequestParams, csrfCookieValue)
|
||||
downstreamAuthCode4 := requireCallbackRequestToBeHandled(issuer2DifferentCaseHostname, callbackRequestParams, csrfCookieValue)
|
||||
|
||||
requireTokenRequestToBeHandled(issuer1, downstreamAuthCode1, issuer1JWKS)
|
||||
requireTokenRequestToBeHandled(issuer2, downstreamAuthCode2, issuer2JWKS)
|
||||
requireTokenRequestToBeHandled(issuer1, downstreamAuthCode1, issuer1JWKS, issuer1)
|
||||
requireTokenRequestToBeHandled(issuer2, downstreamAuthCode2, issuer2JWKS, issuer2)
|
||||
|
||||
// Hostnames are case-insensitive, so test that we can handle that.
|
||||
requireTokenRequestToBeHandled(issuer1DifferentCaseHostname, downstreamAuthCode3, issuer1JWKS)
|
||||
requireTokenRequestToBeHandled(issuer2DifferentCaseHostname, downstreamAuthCode4, issuer2JWKS)
|
||||
requireTokenRequestToBeHandled(issuer1DifferentCaseHostname, downstreamAuthCode3, issuer1JWKS, issuer1)
|
||||
requireTokenRequestToBeHandled(issuer2DifferentCaseHostname, downstreamAuthCode4, issuer2JWKS, issuer2)
|
||||
}
|
||||
|
||||
when("given some valid providers via SetProviders()", func() {
|
||||
|
Loading…
Reference in New Issue
Block a user