2021-04-09 00:28:01 +00:00
|
|
|
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
2020-12-03 20:34:58 +00:00
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
|
|
package oidc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-12-04 01:16:08 +00:00
|
|
|
"crypto/ecdsa"
|
2020-12-04 14:05:39 +00:00
|
|
|
"reflect"
|
2020-12-04 01:16:08 +00:00
|
|
|
|
2020-12-03 20:34:58 +00:00
|
|
|
"github.com/ory/fosite"
|
|
|
|
"github.com/ory/fosite/compose"
|
|
|
|
"github.com/ory/fosite/handler/openid"
|
|
|
|
|
2021-04-09 00:28:01 +00:00
|
|
|
"go.pinniped.dev/internal/constable"
|
2020-12-03 20:34:58 +00:00
|
|
|
"go.pinniped.dev/internal/oidc/jwks"
|
2021-04-09 00:28:01 +00:00
|
|
|
"go.pinniped.dev/internal/plog"
|
2020-12-03 20:34:58 +00:00
|
|
|
)
|
|
|
|
|
2020-12-04 15:06:55 +00:00
|
|
|
// 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
|
2020-12-16 22:27:09 +00:00
|
|
|
// loading FederationDomain's and signing keys run in parallel, and thus the signing key might not be
|
|
|
|
// ready when an FederationDomain is otherwise ready.
|
2020-12-04 15:06:55 +00:00
|
|
|
//
|
2020-12-16 22:27:09 +00:00
|
|
|
// 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.
|
2020-12-03 20:34:58 +00:00
|
|
|
type dynamicOpenIDConnectECDSAStrategy struct {
|
|
|
|
fositeConfig *compose.Config
|
|
|
|
jwksProvider jwks.DynamicJWKSProvider
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ openid.OpenIDConnectTokenStrategy = &dynamicOpenIDConnectECDSAStrategy{}
|
|
|
|
|
|
|
|
func newDynamicOpenIDConnectECDSAStrategy(
|
|
|
|
fositeConfig *compose.Config,
|
|
|
|
jwksProvider jwks.DynamicJWKSProvider,
|
|
|
|
) *dynamicOpenIDConnectECDSAStrategy {
|
|
|
|
return &dynamicOpenIDConnectECDSAStrategy{
|
|
|
|
fositeConfig: fositeConfig,
|
|
|
|
jwksProvider: jwksProvider,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *dynamicOpenIDConnectECDSAStrategy) GenerateIDToken(
|
|
|
|
ctx context.Context,
|
|
|
|
requester fosite.Requester,
|
|
|
|
) (string, error) {
|
2020-12-04 01:16:08 +00:00
|
|
|
_, activeJwk := s.jwksProvider.GetJWKS(s.fositeConfig.IDTokenIssuer)
|
|
|
|
if activeJwk == nil {
|
2020-12-04 14:05:39 +00:00
|
|
|
plog.Debug("no JWK found for issuer", "issuer", s.fositeConfig.IDTokenIssuer)
|
2020-12-17 20:09:19 +00:00
|
|
|
return "", fosite.ErrTemporarilyUnavailable.WithWrap(constable.Error("no JWK found for issuer"))
|
2020-12-04 01:16:08 +00:00
|
|
|
}
|
|
|
|
key, ok := activeJwk.Key.(*ecdsa.PrivateKey)
|
|
|
|
if !ok {
|
2020-12-04 14:05:39 +00:00
|
|
|
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,
|
|
|
|
)
|
2020-12-17 20:09:19 +00:00
|
|
|
return "", fosite.ErrServerError.WithWrap(constable.Error("JWK must be of type ecdsa"))
|
2020-12-04 01:16:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return compose.NewOpenIDConnectECDSAStrategy(s.fositeConfig, key).GenerateIDToken(ctx, requester)
|
2020-12-03 20:34:58 +00:00
|
|
|
}
|