ContainerImage.Pinniped/internal/oidc/dynamic_open_id_connect_ecd...

79 lines
2.4 KiB
Go

// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package oidc
import (
"context"
"crypto/ecdsa"
"reflect"
"time"
"github.com/ory/fosite"
"github.com/ory/fosite/compose"
"github.com/ory/fosite/handler/openid"
"go.pinniped.dev/internal/constable"
"go.pinniped.dev/internal/oidc/jwks"
"go.pinniped.dev/internal/plog"
)
// dynamicOpenIDConnectECDSAStrategy is an openid.OpenIDConnectTokenStrategy that can dynamically
// load a signing key to issue ID tokens. We want this dynamic capability since our controllers for
// loading FederationDomain's and signing keys run in parallel, and thus the signing key might not be
// ready when an FederationDomain is otherwise ready.
//
// If we ever update FederationDomain's to hold their signing key, we might not need this type, since we
// could have an invariant that routes to an FederationDomain's endpoints are only wired up if an
// FederationDomain has a valid signing key.
type dynamicOpenIDConnectECDSAStrategy struct {
fositeConfig *fosite.Config
jwksProvider jwks.DynamicJWKSProvider
}
var _ openid.OpenIDConnectTokenStrategy = &dynamicOpenIDConnectECDSAStrategy{}
func newDynamicOpenIDConnectECDSAStrategy(
fositeConfig *fosite.Config,
jwksProvider jwks.DynamicJWKSProvider,
) *dynamicOpenIDConnectECDSAStrategy {
return &dynamicOpenIDConnectECDSAStrategy{
fositeConfig: fositeConfig,
jwksProvider: jwksProvider,
}
}
func (s *dynamicOpenIDConnectECDSAStrategy) GenerateIDToken(
ctx context.Context,
lifespan time.Duration,
requester fosite.Requester,
) (string, error) {
_, activeJwk := s.jwksProvider.GetJWKS(s.fositeConfig.IDTokenIssuer)
if activeJwk == nil {
plog.Debug("no JWK found for issuer", "issuer", s.fositeConfig.IDTokenIssuer)
return "", fosite.ErrTemporarilyUnavailable.WithWrap(constable.Error("no JWK found for issuer"))
}
key, ok := activeJwk.Key.(*ecdsa.PrivateKey)
if !ok {
actualType := "nil"
if t := reflect.TypeOf(activeJwk.Key); t != nil {
actualType = t.String()
}
plog.Debug(
"JWK must be of type ecdsa",
"issuer",
s.fositeConfig.IDTokenIssuer,
"actualType",
actualType,
)
return "", fosite.ErrServerError.WithWrap(constable.Error("JWK must be of type ecdsa"))
}
keyGetter := func(context.Context) (interface{}, error) {
return key, nil
}
strategy := compose.NewOpenIDConnectStrategy(keyGetter, s.fositeConfig)
return strategy.GenerateIDToken(ctx, lifespan, requester)
}