reorganize federation domain packages to be more intuitive

Co-authored-by: Benjamin A. Petersen <ben@benjaminapetersen.me>
This commit is contained in:
Ryan Richard 2023-06-22 15:12:33 -07:00
parent 3160b5bad1
commit 86c791b8a6
101 changed files with 401 additions and 377 deletions
internal
controller
federationdomain
clientregistry
csp
csrftoken
downstreamsession
dynamiccodec
dynamictlscertprovider
dynamicupstreamprovider
endpoints
endpointsmanager
federationdomainproviders
formposthtml
idplister
oidc
oidcclientvalidator
resolvedprovider
storage
strategy
timeouts
upstreamprovider
fositestorage
mocks
supervisor/server
testutil
upstreamldap
upstreamoidc
pkg/oidcclient
proposals/1113_ldap-ad-web-ui
site/content/docs/reference
test/integration

@ -26,7 +26,7 @@ import (
"go.pinniped.dev/internal/controller/conditionsutil"
"go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/upstreamldap"
)

@ -29,9 +29,9 @@ import (
"go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/endpointaddr"
"go.pinniped.dev/internal/federationdomain/dynamicupstreamprovider"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/mocks/mockldapconn"
"go.pinniped.dev/internal/oidc/provider"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/internal/upstreamldap"
)
@ -2010,7 +2010,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
pinnipedInformers := pinnipedinformers.NewSharedInformerFactory(fakePinnipedClient, 0)
fakeKubeClient := fake.NewSimpleClientset(tt.inputSecrets...)
kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0)
cache := provider.NewDynamicUpstreamIDPProvider()
cache := dynamicupstreamprovider.NewDynamicUpstreamIDPProvider()
cache.SetActiveDirectoryIdentityProviders([]upstreamprovider.UpstreamLDAPIdentityProviderI{
upstreamldap.New(upstreamldap.ProviderConfig{Name: "initial-entry"}),
})

@ -25,8 +25,8 @@ import (
"go.pinniped.dev/internal/celtransformer"
pinnipedcontroller "go.pinniped.dev/internal/controller"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/federationdomain/federationdomainproviders"
"go.pinniped.dev/internal/idtransform"
"go.pinniped.dev/internal/oidc/provider/federationdomainproviders"
"go.pinniped.dev/internal/plog"
)

@ -27,8 +27,8 @@ import (
pinnipedfake "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/fake"
pinnipedinformers "go.pinniped.dev/generated/latest/client/supervisor/informers/externalversions"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/federationdomain/federationdomainproviders"
"go.pinniped.dev/internal/here"
"go.pinniped.dev/internal/oidc/provider/federationdomainproviders"
"go.pinniped.dev/internal/testutil"
)

@ -20,7 +20,7 @@ import (
"go.pinniped.dev/internal/controller/conditionsutil"
"go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/upstreamldap"
)

@ -28,9 +28,9 @@ import (
"go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/endpointaddr"
"go.pinniped.dev/internal/federationdomain/dynamicupstreamprovider"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/mocks/mockldapconn"
"go.pinniped.dev/internal/oidc/provider"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/internal/upstreamldap"
)
@ -1139,7 +1139,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) {
pinnipedInformers := pinnipedinformers.NewSharedInformerFactory(fakePinnipedClient, 0)
fakeKubeClient := fake.NewSimpleClientset(tt.inputSecrets...)
kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0)
cache := provider.NewDynamicUpstreamIDPProvider()
cache := dynamicupstreamprovider.NewDynamicUpstreamIDPProvider()
cache.SetLDAPIdentityProviders([]upstreamprovider.UpstreamLDAPIdentityProviderI{
upstreamldap.New(upstreamldap.ProviderConfig{Name: "initial-entry"}),
})

@ -21,7 +21,7 @@ import (
pinnipedcontroller "go.pinniped.dev/internal/controller"
"go.pinniped.dev/internal/controller/conditionsutil"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/oidc/oidcclientvalidator"
"go.pinniped.dev/internal/federationdomain/oidcclientvalidator"
"go.pinniped.dev/internal/oidcclientsecretstorage"
"go.pinniped.dev/internal/plog"
)

@ -34,8 +34,8 @@ import (
"go.pinniped.dev/internal/controller/conditionsutil"
"go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/net/phttp"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/upstreamoidc"
)
@ -94,7 +94,7 @@ type UpstreamOIDCIdentityProviderICache interface {
SetOIDCIdentityProviders([]upstreamprovider.UpstreamOIDCIdentityProviderI)
}
// lruValidatorCache caches the *oidc.Provider associated with a particular issuer/TLS configuration.
// lruValidatorCache caches the *coreosoidc.Provider associated with a particular issuer/TLS configuration.
type lruValidatorCache struct{ cache *cache.Expiring }
type lruValidatorCacheEntry struct {

@ -28,8 +28,8 @@ import (
pinnipedinformers "go.pinniped.dev/generated/latest/client/supervisor/informers/externalversions"
"go.pinniped.dev/internal/certauthority"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/oidc/provider"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/federationdomain/dynamicupstreamprovider"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/internal/testutil/oidctestutil"
@ -81,7 +81,7 @@ func TestOIDCUpstreamWatcherControllerFilterSecret(t *testing.T) {
pinnipedInformers := pinnipedinformers.NewSharedInformerFactory(fakePinnipedClient, 0)
fakeKubeClient := fake.NewSimpleClientset()
kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0)
cache := provider.NewDynamicUpstreamIDPProvider()
cache := dynamicupstreamprovider.NewDynamicUpstreamIDPProvider()
cache.SetOIDCIdentityProviders([]upstreamprovider.UpstreamOIDCIdentityProviderI{
&upstreamoidc.ProviderConfig{Name: "initial-entry"},
})
@ -1416,7 +1416,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + testIs
fakeKubeClient := fake.NewSimpleClientset(tt.inputSecrets...)
kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0)
testLog := testlogger.NewLegacy(t) //nolint:staticcheck // old test with lots of log statements
cache := provider.NewDynamicUpstreamIDPProvider()
cache := dynamicupstreamprovider.NewDynamicUpstreamIDPProvider()
cache.SetOIDCIdentityProviders([]upstreamprovider.UpstreamOIDCIdentityProviderI{
&upstreamoidc.ProviderConfig{Name: "initial-entry"},
})

@ -16,7 +16,7 @@ import (
"go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1"
"go.pinniped.dev/internal/constable"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/upstreamldap"
)

@ -21,13 +21,13 @@ import (
pinnipedcontroller "go.pinniped.dev/internal/controller"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/crud"
"go.pinniped.dev/internal/federationdomain/dynamicupstreamprovider"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/fositestorage/accesstoken"
"go.pinniped.dev/internal/fositestorage/authorizationcode"
"go.pinniped.dev/internal/fositestorage/openidconnect"
"go.pinniped.dev/internal/fositestorage/pkce"
"go.pinniped.dev/internal/fositestorage/refreshtoken"
"go.pinniped.dev/internal/oidc/provider"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/psession"
)
@ -144,7 +144,7 @@ func (c *garbageCollectorController) Sync(ctx controllerlib.Context) error {
// cleaning them out of etcd storage.
fourHoursAgo := frozenClock.Now().Add(-4 * time.Hour)
nowIsLessThanFourHoursBeyondSecretGCTime := garbageCollectAfterTime.After(fourHoursAgo)
if errors.As(revokeErr, &provider.RetryableRevocationError{}) && nowIsLessThanFourHoursBeyondSecretGCTime {
if errors.As(revokeErr, &dynamicupstreamprovider.RetryableRevocationError{}) && nowIsLessThanFourHoursBeyondSecretGCTime {
// Hasn't been very long since secret expired, so skip deletion to try revocation again later.
plog.Trace("garbage collector keeping Secret to retry upstream OIDC token revocation later", logKV(secret)...)
continue

@ -25,12 +25,12 @@ import (
clocktesting "k8s.io/utils/clock/testing"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/federationdomain/clientregistry"
"go.pinniped.dev/internal/federationdomain/dynamicupstreamprovider"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/fositestorage/accesstoken"
"go.pinniped.dev/internal/fositestorage/authorizationcode"
"go.pinniped.dev/internal/fositestorage/refreshtoken"
"go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/oidc/provider"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/internal/testutil/oidctestutil"
@ -138,7 +138,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
// Defer starting the informers until the last possible moment so that the
// nested Before's can keep adding things to the informer caches.
var startInformersAndController = func(idpCache provider.DynamicUpstreamIDPProvider) {
var startInformersAndController = func(idpCache dynamicupstreamprovider.DynamicUpstreamIDPProvider) {
// Set this at the last second to allow for injection of server override.
subject = GarbageCollectorController(
idpCache,
@ -774,7 +774,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
WithName("upstream-oidc-provider-name").
WithResourceUID("upstream-oidc-provider-uid").
// make the upstream revocation fail in a retryable way
WithRevokeTokenError(provider.NewRetryableRevocationError(errors.New("some retryable upstream revocation error")))
WithRevokeTokenError(dynamicupstreamprovider.NewRetryableRevocationError(errors.New("some retryable upstream revocation error")))
idpListerBuilder := oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(happyOIDCUpstream.Build())
startInformersAndController(idpListerBuilder.BuildDynamicUpstreamIDPProvider())

@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Package clientregistry defines Pinniped's OAuth2/OIDC clients.
@ -18,7 +18,7 @@ import (
configv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1"
oidcapi "go.pinniped.dev/generated/latest/apis/supervisor/oidc"
supervisorclient "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/typed/config/v1alpha1"
"go.pinniped.dev/internal/oidc/oidcclientvalidator"
"go.pinniped.dev/internal/federationdomain/oidcclientvalidator"
"go.pinniped.dev/internal/oidcclientsecretstorage"
"go.pinniped.dev/internal/plog"
)

@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package clientregistry
@ -21,7 +21,7 @@ import (
configv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1"
supervisorfake "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/fake"
"go.pinniped.dev/internal/oidc/oidcclientvalidator"
"go.pinniped.dev/internal/federationdomain/oidcclientvalidator"
"go.pinniped.dev/internal/oidcclientsecretstorage"
"go.pinniped.dev/internal/testutil"
)

@ -1,4 +1,4 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2022-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Package csp defines helpers related to HTML Content Security Policies.

@ -1,4 +1,4 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2022-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package csp

@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package csrftoken

@ -1,4 +1,4 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package csrftoken

@ -20,9 +20,9 @@ import (
oidcapi "go.pinniped.dev/generated/latest/apis/supervisor/oidc"
"go.pinniped.dev/internal/authenticators"
"go.pinniped.dev/internal/constable"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/idtransform"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/pkg/oidcclient/oidctypes"

@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Package dynamiccodec provides a type that can encode information using a just-in-time signing and
@ -10,7 +10,7 @@ import (
"github.com/gorilla/securecookie"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/federationdomain/oidc"
)
var _ oidc.Codec = &Codec{}

@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package dynamiccodec

@ -1,7 +1,7 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package provider
package dynamictlscertprovider
import (
"crypto/tls"

@ -1,13 +1,13 @@
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package provider
package dynamicupstreamprovider
import (
"fmt"
"sync"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
)
type DynamicUpstreamIDPProvider interface {

@ -16,17 +16,17 @@ import (
"golang.org/x/oauth2"
oidcapi "go.pinniped.dev/generated/latest/apis/supervisor/oidc"
"go.pinniped.dev/internal/federationdomain/csrftoken"
"go.pinniped.dev/internal/federationdomain/downstreamsession"
"go.pinniped.dev/internal/federationdomain/endpoints/login"
"go.pinniped.dev/internal/federationdomain/federationdomainproviders"
"go.pinniped.dev/internal/federationdomain/formposthtml"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/federationdomain/resolvedprovider"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/httputil/httperr"
"go.pinniped.dev/internal/httputil/securityheader"
"go.pinniped.dev/internal/idtransform"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/oidc/csrftoken"
"go.pinniped.dev/internal/oidc/downstreamsession"
"go.pinniped.dev/internal/oidc/login"
"go.pinniped.dev/internal/oidc/provider/federationdomainproviders"
"go.pinniped.dev/internal/oidc/provider/formposthtml"
"go.pinniped.dev/internal/oidc/provider/resolvedprovider"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/pkg/oidcclient/nonce"

@ -30,11 +30,12 @@ import (
supervisorfake "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/fake"
"go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/typed/config/v1alpha1"
"go.pinniped.dev/internal/authenticators"
"go.pinniped.dev/internal/federationdomain/csrftoken"
"go.pinniped.dev/internal/federationdomain/endpoints/jwks"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/federationdomain/oidcclientvalidator"
"go.pinniped.dev/internal/federationdomain/storage"
"go.pinniped.dev/internal/here"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/oidc/csrftoken"
"go.pinniped.dev/internal/oidc/jwks"
"go.pinniped.dev/internal/oidc/oidcclientvalidator"
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/internal/testutil/oidctestutil"
@ -232,18 +233,18 @@ func TestAuthorizationEndpoint(t *testing.T) {
jwksProviderIsUnused := jwks.NewDynamicJWKSProvider()
timeoutsConfiguration := oidc.DefaultOIDCTimeoutsConfiguration()
createOauthHelperWithRealStorage := func(secretsClient v1.SecretInterface, oidcClientsClient v1alpha1.OIDCClientInterface) (fosite.OAuth2Provider, *oidc.KubeStorage) {
createOauthHelperWithRealStorage := func(secretsClient v1.SecretInterface, oidcClientsClient v1alpha1.OIDCClientInterface) (fosite.OAuth2Provider, *storage.KubeStorage) {
// Configure fosite the same way that the production code would when using Kube storage.
// Inject this into our test subject at the last second so we get a fresh storage for every test.
// Use lower minimum required bcrypt cost than we would use in production to keep unit the tests fast.
kubeOauthStore := oidc.NewKubeStorage(secretsClient, oidcClientsClient, timeoutsConfiguration, bcrypt.MinCost)
kubeOauthStore := storage.NewKubeStorage(secretsClient, oidcClientsClient, timeoutsConfiguration, bcrypt.MinCost)
return oidc.FositeOauth2Helper(kubeOauthStore, downstreamIssuer, hmacSecretFunc, jwksProviderIsUnused, timeoutsConfiguration), kubeOauthStore
}
createOauthHelperWithNullStorage := func(secretsClient v1.SecretInterface, oidcClientsClient v1alpha1.OIDCClientInterface) (fosite.OAuth2Provider, *oidc.NullStorage) {
createOauthHelperWithNullStorage := func(secretsClient v1.SecretInterface, oidcClientsClient v1alpha1.OIDCClientInterface) (fosite.OAuth2Provider, *storage.NullStorage) {
// Configure fosite the same way that the production code would, using NullStorage to turn off storage.
// Use lower minimum required bcrypt cost than we would use in production to keep unit the tests fast.
nullOauthStore := oidc.NewNullStorage(secretsClient, oidcClientsClient, bcrypt.MinCost)
nullOauthStore := storage.NewNullStorage(secretsClient, oidcClientsClient, bcrypt.MinCost)
return oidc.FositeOauth2Helper(nullOauthStore, downstreamIssuer, hmacSecretFunc, jwksProviderIsUnused, timeoutsConfiguration), nullOauthStore
}
@ -3175,7 +3176,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
},
}
runOneTestCase := func(t *testing.T, test testCase, subject http.Handler, kubeOauthStore *oidc.KubeStorage, supervisorClient *supervisorfake.Clientset, kubeClient *fake.Clientset, secretsClient v1.SecretInterface) {
runOneTestCase := func(t *testing.T, test testCase, subject http.Handler, kubeOauthStore *storage.KubeStorage, supervisorClient *supervisorfake.Clientset, kubeClient *fake.Clientset, secretsClient v1.SecretInterface) {
if test.kubeResources != nil {
test.kubeResources(t, supervisorClient, kubeClient)
}

@ -10,12 +10,12 @@ import (
"github.com/ory/fosite"
"go.pinniped.dev/internal/federationdomain/downstreamsession"
"go.pinniped.dev/internal/federationdomain/federationdomainproviders"
"go.pinniped.dev/internal/federationdomain/formposthtml"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/httputil/httperr"
"go.pinniped.dev/internal/httputil/securityheader"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/oidc/downstreamsession"
"go.pinniped.dev/internal/oidc/provider/federationdomainproviders"
"go.pinniped.dev/internal/oidc/provider/formposthtml"
"go.pinniped.dev/internal/plog"
)

@ -21,9 +21,10 @@ import (
configv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1"
supervisorfake "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/fake"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/oidc/jwks"
"go.pinniped.dev/internal/oidc/oidcclientvalidator"
"go.pinniped.dev/internal/federationdomain/endpoints/jwks"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/federationdomain/oidcclientvalidator"
"go.pinniped.dev/internal/federationdomain/storage"
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/internal/testutil/oidctestutil"
@ -1451,7 +1452,7 @@ func TestCallbackEndpoint(t *testing.T) {
// Inject this into our test subject at the last second so we get a fresh storage for every test.
timeoutsConfiguration := oidc.DefaultOIDCTimeoutsConfiguration()
// Use lower minimum required bcrypt cost than we would use in production to keep unit the tests fast.
oauthStore := oidc.NewKubeStorage(secrets, oidcClientsClient, timeoutsConfiguration, bcrypt.MinCost)
oauthStore := storage.NewKubeStorage(secrets, oidcClientsClient, timeoutsConfiguration, bcrypt.MinCost)
hmacSecretFunc := func() []byte { return []byte("some secret - must have at least 32 bytes") }
require.GreaterOrEqual(t, len(hmacSecretFunc()), 32, "fosite requires that hmac secrets have at least 32 bytes")
jwksProviderIsUnused := jwks.NewDynamicJWKSProvider()

@ -11,7 +11,7 @@ import (
"go.pinniped.dev/generated/latest/apis/supervisor/idpdiscovery/v1alpha1"
oidcapi "go.pinniped.dev/generated/latest/apis/supervisor/oidc"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/federationdomain/oidc"
)
// Metadata holds all fields (that we care about) from the OpenID Provider Metadata section in the

@ -10,8 +10,8 @@ import (
"github.com/stretchr/testify/require"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/here"
"go.pinniped.dev/internal/oidc"
)
func TestDiscovery(t *testing.T) {

@ -11,7 +11,7 @@ import (
"sort"
"go.pinniped.dev/generated/latest/apis/supervisor/idpdiscovery/v1alpha1"
"go.pinniped.dev/internal/oidc/provider/federationdomainproviders"
"go.pinniped.dev/internal/federationdomain/federationdomainproviders"
)
// NewHandler returns an http.Handler that serves the upstream IDP discovery endpoint.

@ -10,8 +10,8 @@ import (
"github.com/stretchr/testify/require"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/here"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/testutil/oidctestutil"
)

@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package jwks

@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Package discovery provides a handler for the OIDC discovery endpoint.

@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package jwks

@ -1,4 +1,4 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2022-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package login
@ -6,8 +6,8 @@ package login
import (
"net/http"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/oidc/login/loginhtml"
"go.pinniped.dev/internal/federationdomain/endpoints/login/loginhtml"
"go.pinniped.dev/internal/federationdomain/oidc"
)
const (

@ -10,9 +10,9 @@ import (
"github.com/stretchr/testify/require"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/oidc/idplister"
"go.pinniped.dev/internal/oidc/login/loginhtml"
"go.pinniped.dev/internal/federationdomain/endpoints/login/loginhtml"
"go.pinniped.dev/internal/federationdomain/idplister"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/testutil"
)

@ -1,4 +1,4 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2022-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package login
@ -8,11 +8,11 @@ import (
"net/url"
idpdiscoveryv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idpdiscovery/v1alpha1"
"go.pinniped.dev/internal/federationdomain/endpoints/login/loginhtml"
"go.pinniped.dev/internal/federationdomain/formposthtml"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/httputil/httperr"
"go.pinniped.dev/internal/httputil/securityheader"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/oidc/login/loginhtml"
"go.pinniped.dev/internal/oidc/provider/formposthtml"
"go.pinniped.dev/internal/plog"
)

@ -1,4 +1,4 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2022-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package login
@ -13,8 +13,8 @@ import (
"github.com/gorilla/securecookie"
"github.com/stretchr/testify/require"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/httputil/httperr"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/internal/testutil/oidctestutil"
)

@ -1,4 +1,4 @@
/* Copyright 2022 the Pinniped contributors. All Rights Reserved. */
/* Copyright 2022-2023 the Pinniped contributors. All Rights Reserved. */
/* SPDX-License-Identifier: Apache-2.0 */
html {

@ -1,5 +1,5 @@
<!--
Copyright 2022 the Pinniped contributors. All Rights Reserved.
Copyright 2022-2023 the Pinniped contributors. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
Notes:

@ -1,4 +1,4 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2022-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Package loginhtml defines HTML templates used by the Supervisor.
@ -11,7 +11,7 @@ import (
"github.com/tdewolff/minify/v2/minify"
"go.pinniped.dev/internal/oidc/provider/csp"
"go.pinniped.dev/internal/federationdomain/csp"
)
//nolint:gochecknoglobals // This package uses globals to ensure that all parsing and minifying happens at init.

@ -1,4 +1,4 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2022-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package loginhtml
@ -8,9 +8,9 @@ import (
"fmt"
"testing"
"go.pinniped.dev/internal/testutil"
"github.com/stretchr/testify/require"
"go.pinniped.dev/internal/testutil"
)
var (

@ -9,10 +9,10 @@ import (
"github.com/ory/fosite"
"go.pinniped.dev/internal/federationdomain/downstreamsession"
"go.pinniped.dev/internal/federationdomain/federationdomainproviders"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/httputil/httperr"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/oidc/downstreamsession"
"go.pinniped.dev/internal/oidc/provider/federationdomainproviders"
"go.pinniped.dev/internal/plog"
)

@ -20,9 +20,10 @@ import (
configv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1"
supervisorfake "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/fake"
"go.pinniped.dev/internal/authenticators"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/oidc/jwks"
"go.pinniped.dev/internal/oidc/oidcclientvalidator"
"go.pinniped.dev/internal/federationdomain/endpoints/jwks"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/federationdomain/oidcclientvalidator"
"go.pinniped.dev/internal/federationdomain/storage"
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/internal/testutil/oidctestutil"
@ -978,7 +979,7 @@ func TestPostLoginEndpoint(t *testing.T) {
// Inject this into our test subject at the last second so we get a fresh storage for every test.
timeoutsConfiguration := oidc.DefaultOIDCTimeoutsConfiguration()
// Use lower minimum required bcrypt cost than we would use in production to keep unit the tests fast.
kubeOauthStore := oidc.NewKubeStorage(secretsClient, oidcClientsClient, timeoutsConfiguration, bcrypt.MinCost)
kubeOauthStore := storage.NewKubeStorage(secretsClient, oidcClientsClient, timeoutsConfiguration, bcrypt.MinCost)
hmacSecretFunc := func() []byte { return []byte("some secret - must have at least 32 bytes") }
require.GreaterOrEqual(t, len(hmacSecretFunc()), 32, "fosite requires that hmac secrets have at least 32 bytes")
jwksProviderIsUnused := jwks.NewDynamicJWKSProvider()

@ -18,13 +18,13 @@ import (
"k8s.io/utils/strings/slices"
oidcapi "go.pinniped.dev/generated/latest/apis/supervisor/oidc"
"go.pinniped.dev/internal/federationdomain/downstreamsession"
"go.pinniped.dev/internal/federationdomain/federationdomainproviders"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/federationdomain/resolvedprovider"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/httputil/httperr"
"go.pinniped.dev/internal/idtransform"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/oidc/downstreamsession"
"go.pinniped.dev/internal/oidc/provider/federationdomainproviders"
"go.pinniped.dev/internal/oidc/provider/resolvedprovider"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/psession"
)

@ -24,7 +24,7 @@ import (
"github.com/ory/fosite"
fositeoauth2 "github.com/ory/fosite/handler/oauth2"
"github.com/ory/fosite/handler/openid"
"github.com/ory/fosite/handler/pkce"
fositepkce "github.com/ory/fosite/handler/pkce"
"github.com/ory/fosite/token/jwt"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
@ -42,19 +42,20 @@ import (
configv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1"
supervisorfake "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/fake"
"go.pinniped.dev/internal/crud"
"go.pinniped.dev/internal/federationdomain/clientregistry"
"go.pinniped.dev/internal/federationdomain/endpoints/jwks"
"go.pinniped.dev/internal/federationdomain/federationdomainproviders"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/federationdomain/oidcclientvalidator"
"go.pinniped.dev/internal/federationdomain/storage"
"go.pinniped.dev/internal/fositestorage/accesstoken"
"go.pinniped.dev/internal/fositestorage/authorizationcode"
"go.pinniped.dev/internal/fositestorage/openidconnect"
storagepkce "go.pinniped.dev/internal/fositestorage/pkce"
"go.pinniped.dev/internal/fositestorage/pkce"
"go.pinniped.dev/internal/fositestorage/refreshtoken"
"go.pinniped.dev/internal/fositestoragei"
"go.pinniped.dev/internal/here"
"go.pinniped.dev/internal/httputil/httperr"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/oidc/jwks"
"go.pinniped.dev/internal/oidc/oidcclientvalidator"
"go.pinniped.dev/internal/oidc/provider/federationdomainproviders"
"go.pinniped.dev/internal/oidcclientsecretstorage"
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/internal/testutil"
@ -955,7 +956,7 @@ func TestTokenEndpointWhenAuthcodeIsUsedTwice(t *testing.T) {
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: openidconnect.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: accesstoken.TypeLabelValue}, 0)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: refreshtoken.TypeLabelValue}, 0)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: storagepkce.TypeLabelValue}, 0)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: pkce.TypeLabelValue}, 0)
// Assert the number of all secrets, excluding any OIDCClient's storage secret, since those are not related to session storage.
testutil.RequireNumberOfSecretsExcludingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: oidcclientsecretstorage.TypeLabelValue}, 2)
})
@ -1005,7 +1006,7 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
authcodeExchange authcodeExchangeInputs
modifyRequestParams func(t *testing.T, params url.Values)
modifyRequestHeaders func(r *http.Request)
modifyStorage func(t *testing.T, storage *oidc.KubeStorage, secrets v1.SecretInterface, pendingRequest *http.Request)
modifyStorage func(t *testing.T, storage *storage.KubeStorage, secrets v1.SecretInterface, pendingRequest *http.Request)
requestedAudience string
kubeResources func(t *testing.T, supervisorClient *supervisorfake.Clientset, kubeClient *fake.Clientset)
@ -1452,7 +1453,7 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
name: "valid access token, but it was already deleted from storage",
authcodeExchange: doValidAuthCodeExchange,
requestedAudience: "some-workload-cluster",
modifyStorage: func(t *testing.T, storage *oidc.KubeStorage, secrets v1.SecretInterface, pendingRequest *http.Request) {
modifyStorage: func(t *testing.T, storage *storage.KubeStorage, secrets v1.SecretInterface, pendingRequest *http.Request) {
parts := strings.Split(pendingRequest.Form.Get("subject_token"), ".")
require.Len(t, parts, 2)
require.NoError(t, storage.DeleteAccessTokenSession(context.Background(), parts[1]))
@ -1465,7 +1466,7 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
name: "valid access token, but it has already expired",
authcodeExchange: doValidAuthCodeExchange,
requestedAudience: "some-workload-cluster",
modifyStorage: func(t *testing.T, storage *oidc.KubeStorage, secrets v1.SecretInterface, pendingRequest *http.Request) {
modifyStorage: func(t *testing.T, storage *storage.KubeStorage, secrets v1.SecretInterface, pendingRequest *http.Request) {
// The fosite storage APIs don't offer a way to update an access token, so we will instead find the underlying
// storage Secret and update it in a more manual way. First get the access token's signature.
parts := strings.Split(pendingRequest.Form.Get("subject_token"), ".")
@ -1955,7 +1956,7 @@ func TestRefreshGrant(t *testing.T) {
kubeResources func(t *testing.T, supervisorClient *supervisorfake.Clientset, kubeClient *fake.Clientset)
authcodeExchange authcodeExchangeInputs
refreshRequest refreshRequestInputs
modifyRefreshTokenStorage func(t *testing.T, oauthStore *oidc.KubeStorage, secrets v1.SecretInterface, refreshToken string)
modifyRefreshTokenStorage func(t *testing.T, oauthStore *storage.KubeStorage, secrets v1.SecretInterface, refreshToken string)
}{
{
name: "happy path refresh grant with openid scope granted (id token returned)",
@ -2869,7 +2870,7 @@ func TestRefreshGrant(t *testing.T) {
name: "when a valid refresh token is sent in the refresh request, but the token has already expired",
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(upstreamOIDCIdentityProviderBuilder().Build()),
authcodeExchange: happyAuthcodeExchangeInputsForOIDCUpstream,
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *oidc.KubeStorage, secrets v1.SecretInterface, refreshToken string) {
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *storage.KubeStorage, secrets v1.SecretInterface, refreshToken string) {
// The fosite storage APIs don't offer a way to update a refresh token, so we will instead find the underlying
// storage Secret and update it in a more manual way. First get the refresh token's signature.
refreshTokenSignature := getFositeDataSignature(t, refreshToken)
@ -3832,7 +3833,7 @@ func TestRefreshGrant(t *testing.T) {
Build(),
),
authcodeExchange: happyAuthcodeExchangeInputsForLDAPUpstream,
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *oidc.KubeStorage, secrets v1.SecretInterface, refreshToken string) {
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *storage.KubeStorage, secrets v1.SecretInterface, refreshToken string) {
refreshTokenSignature := getFositeDataSignature(t, refreshToken)
firstRequester, err := oauthStore.GetRefreshTokenSession(context.Background(), refreshTokenSignature, nil)
require.NoError(t, err)
@ -3870,7 +3871,7 @@ func TestRefreshGrant(t *testing.T) {
happyLDAPCustomSessionData,
),
},
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *oidc.KubeStorage, secrets v1.SecretInterface, refreshToken string) {
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *storage.KubeStorage, secrets v1.SecretInterface, refreshToken string) {
refreshTokenSignature := getFositeDataSignature(t, refreshToken)
firstRequester, err := oauthStore.GetRefreshTokenSession(context.Background(), refreshTokenSignature, nil)
require.NoError(t, err)
@ -3908,7 +3909,7 @@ func TestRefreshGrant(t *testing.T) {
happyLDAPCustomSessionData,
),
},
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *oidc.KubeStorage, secrets v1.SecretInterface, refreshToken string) {
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *storage.KubeStorage, secrets v1.SecretInterface, refreshToken string) {
refreshTokenSignature := getFositeDataSignature(t, refreshToken)
firstRequester, err := oauthStore.GetRefreshTokenSession(context.Background(), refreshTokenSignature, nil)
require.NoError(t, err)
@ -3988,7 +3989,7 @@ func TestRefreshGrant(t *testing.T) {
Build(),
),
authcodeExchange: happyAuthcodeExchangeInputsForLDAPUpstream,
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *oidc.KubeStorage, secrets v1.SecretInterface, refreshToken string) {
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *storage.KubeStorage, secrets v1.SecretInterface, refreshToken string) {
refreshTokenSignature := getFositeDataSignature(t, refreshToken)
firstRequester, err := oauthStore.GetRefreshTokenSession(context.Background(), refreshTokenSignature, nil)
require.NoError(t, err)
@ -4178,7 +4179,7 @@ func exchangeAuthcodeForTokens(
authCode string,
jwtSigningKey *ecdsa.PrivateKey,
secrets v1.SecretInterface,
oauthStore *oidc.KubeStorage,
oauthStore *storage.KubeStorage,
) {
authRequest := deepCopyRequestForm(happyAuthRequest)
if test.modifyAuthRequest != nil {
@ -4196,7 +4197,7 @@ func exchangeAuthcodeForTokens(
var oauthHelper fosite.OAuth2Provider
// Use lower minimum required bcrypt cost than we would use in production to keep unit the tests fast.
oauthStore = oidc.NewKubeStorage(secrets, oidcClientsClient, oidc.DefaultOIDCTimeoutsConfiguration(), bcrypt.MinCost)
oauthStore = storage.NewKubeStorage(secrets, oidcClientsClient, oidc.DefaultOIDCTimeoutsConfiguration(), bcrypt.MinCost)
if test.makeJwksSigningKeyAndProvider == nil {
test.makeJwksSigningKeyAndProvider = generateJWTSigningKeyAndJWKSProvider
@ -4214,7 +4215,7 @@ func exchangeAuthcodeForTokens(
}
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: authorizationcode.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: storagepkce.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: pkce.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: openidconnect.TypeLabelValue}, expectedNumberOfIDSessionsStored)
// Assert the number of all secrets, excluding any OIDCClient's storage secret, since those are not related to session storage.
testutil.RequireNumberOfSecretsExcludingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: oidcclientsecretstorage.TypeLabelValue}, 2+expectedNumberOfIDSessionsStored)
@ -4259,7 +4260,7 @@ func requireTokenEndpointBehavior(
wantNonceValueInIDToken bool,
tokenEndpointResponse *httptest.ResponseRecorder,
authCode string,
oauthStore *oidc.KubeStorage,
oauthStore *storage.KubeStorage,
jwtSigningKey *ecdsa.PrivateKey,
secrets v1.SecretInterface,
requestTime time.Time,
@ -4298,7 +4299,7 @@ func requireTokenEndpointBehavior(
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: authorizationcode.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: accesstoken.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: storagepkce.TypeLabelValue}, 0)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: pkce.TypeLabelValue}, 0)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: refreshtoken.TypeLabelValue}, expectedNumberOfRefreshTokenSessionsStored)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: openidconnect.TypeLabelValue}, expectedNumberOfIDSessionsStored)
// Assert the number of all secrets, excluding any OIDCClient's storage secret, since those are not related to session storage.
@ -4650,7 +4651,7 @@ func requireInvalidAccessTokenStorage(
func requireInvalidPKCEStorage(
t *testing.T,
code string,
storage pkce.PKCERequestStorage,
storage fositepkce.PKCERequestStorage,
) {
t.Helper()

@ -1,7 +1,7 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package oidc
package tokenexchange
import (
"context"
@ -27,8 +27,8 @@ type stsParams struct {
requestedAudience string
}
func TokenExchangeFactory(config fosite.Configurator, storage interface{}, strategy interface{}) interface{} {
return &TokenExchangeHandler{
func HandlerFactory(config fosite.Configurator, storage interface{}, strategy interface{}) interface{} {
return &tokenExchangeHandler{
idTokenStrategy: strategy.(openid.OpenIDConnectTokenStrategy),
accessTokenStrategy: strategy.(oauth2.AccessTokenStrategy),
accessTokenStorage: storage.(oauth2.AccessTokenStorage),
@ -36,23 +36,23 @@ func TokenExchangeFactory(config fosite.Configurator, storage interface{}, strat
}
}
type TokenExchangeHandler struct {
type tokenExchangeHandler struct {
idTokenStrategy openid.OpenIDConnectTokenStrategy
accessTokenStrategy oauth2.AccessTokenStrategy
accessTokenStorage oauth2.AccessTokenStorage
fositeConfig fosite.Configurator
}
var _ fosite.TokenEndpointHandler = (*TokenExchangeHandler)(nil)
var _ fosite.TokenEndpointHandler = (*tokenExchangeHandler)(nil)
func (t *TokenExchangeHandler) HandleTokenEndpointRequest(ctx context.Context, requester fosite.AccessRequester) error {
func (t *tokenExchangeHandler) HandleTokenEndpointRequest(ctx context.Context, requester fosite.AccessRequester) error {
if !t.CanHandleTokenEndpointRequest(ctx, requester) {
return errors.WithStack(fosite.ErrUnknownRequest)
}
return nil
}
func (t *TokenExchangeHandler) PopulateTokenEndpointResponse(ctx context.Context, requester fosite.AccessRequester, responder fosite.AccessResponder) error {
func (t *tokenExchangeHandler) PopulateTokenEndpointResponse(ctx context.Context, requester fosite.AccessRequester, responder fosite.AccessResponder) error {
// Skip this request if it's for a different grant type.
if err := t.HandleTokenEndpointRequest(ctx, requester); err != nil {
return errors.WithStack(err)
@ -108,7 +108,7 @@ func (t *TokenExchangeHandler) PopulateTokenEndpointResponse(ctx context.Context
return nil
}
func (t *TokenExchangeHandler) mintJWT(ctx context.Context, requester fosite.Requester, audience string) (string, error) {
func (t *tokenExchangeHandler) mintJWT(ctx context.Context, requester fosite.Requester, audience string) (string, error) {
downscoped := fosite.NewAccessRequest(requester.GetSession())
downscoped.Client.(*fosite.DefaultClient).ID = audience
@ -119,7 +119,7 @@ func (t *TokenExchangeHandler) mintJWT(ctx context.Context, requester fosite.Req
return t.idTokenStrategy.GenerateIDToken(ctx, idTokenLifespan, downscoped)
}
func (t *TokenExchangeHandler) validateSession(requester fosite.Requester) error {
func (t *tokenExchangeHandler) validateSession(requester fosite.Requester) error {
pSession, ok := requester.GetSession().(*psession.PinnipedSession)
if !ok {
// This shouldn't really happen.
@ -135,7 +135,7 @@ func (t *TokenExchangeHandler) validateSession(requester fosite.Requester) error
return nil
}
func (t *TokenExchangeHandler) validateParams(params url.Values) (*stsParams, error) {
func (t *tokenExchangeHandler) validateParams(params url.Values) (*stsParams, error) {
var result stsParams
// Validate some required parameters.
@ -189,7 +189,7 @@ func (t *TokenExchangeHandler) validateParams(params url.Values) (*stsParams, er
return &result, nil
}
func (t *TokenExchangeHandler) validateAccessToken(ctx context.Context, requester fosite.AccessRequester, accessToken string) (fosite.Requester, error) {
func (t *tokenExchangeHandler) validateAccessToken(ctx context.Context, requester fosite.AccessRequester, accessToken string) (fosite.Requester, error) {
// Look up the access token's stored session data.
signature := t.accessTokenStrategy.AccessTokenSignature(ctx, accessToken)
originalRequester, err := t.accessTokenStorage.GetAccessTokenSession(ctx, signature, requester.GetSession())
@ -204,10 +204,10 @@ func (t *TokenExchangeHandler) validateAccessToken(ctx context.Context, requeste
return originalRequester, nil
}
func (t *TokenExchangeHandler) CanSkipClientAuth(_ context.Context, _ fosite.AccessRequester) bool {
func (t *tokenExchangeHandler) CanSkipClientAuth(_ context.Context, _ fosite.AccessRequester) bool {
return false
}
func (t *TokenExchangeHandler) CanHandleTokenEndpointRequest(_ context.Context, requester fosite.AccessRequester) bool {
func (t *tokenExchangeHandler) CanHandleTokenEndpointRequest(_ context.Context, requester fosite.AccessRequester) bool {
return requester.GetGrantTypes().ExactOne(oidcapi.GrantTypeTokenExchange)
}

@ -1,9 +1,10 @@
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package manager
package endpointsmanager
import (
"go.pinniped.dev/internal/httputil/requestutil"
"net/http"
"strings"
"sync"
@ -11,20 +12,20 @@ import (
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
"go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/typed/config/v1alpha1"
"go.pinniped.dev/internal/httputil/requestutil"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/oidc/auth"
"go.pinniped.dev/internal/oidc/callback"
"go.pinniped.dev/internal/oidc/csrftoken"
"go.pinniped.dev/internal/oidc/discovery"
"go.pinniped.dev/internal/oidc/dynamiccodec"
"go.pinniped.dev/internal/oidc/idpdiscovery"
"go.pinniped.dev/internal/oidc/idplister"
"go.pinniped.dev/internal/oidc/jwks"
"go.pinniped.dev/internal/oidc/login"
"go.pinniped.dev/internal/oidc/oidcclientvalidator"
"go.pinniped.dev/internal/oidc/provider/federationdomainproviders"
"go.pinniped.dev/internal/oidc/token"
"go.pinniped.dev/internal/federationdomain/csrftoken"
"go.pinniped.dev/internal/federationdomain/dynamiccodec"
"go.pinniped.dev/internal/federationdomain/endpoints/auth"
"go.pinniped.dev/internal/federationdomain/endpoints/callback"
"go.pinniped.dev/internal/federationdomain/endpoints/discovery"
"go.pinniped.dev/internal/federationdomain/endpoints/idpdiscovery"
"go.pinniped.dev/internal/federationdomain/endpoints/jwks"
"go.pinniped.dev/internal/federationdomain/endpoints/login"
"go.pinniped.dev/internal/federationdomain/endpoints/token"
"go.pinniped.dev/internal/federationdomain/federationdomainproviders"
"go.pinniped.dev/internal/federationdomain/idplister"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/federationdomain/oidcclientvalidator"
"go.pinniped.dev/internal/federationdomain/storage"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/secret"
"go.pinniped.dev/pkg/oidcclient/nonce"
@ -101,7 +102,7 @@ func (m *Manager) SetFederationDomains(federationDomains ...*federationdomainpro
// Use NullStorage for the authorize endpoint because we do not actually want to store anything until
// the upstream callback endpoint is called later.
oauthHelperWithNullStorage := oidc.FositeOauth2Helper(
oidc.NewNullStorage(m.secretsClient, m.oidcClientsClient, oidcclientvalidator.DefaultMinBcryptCost),
storage.NewNullStorage(m.secretsClient, m.oidcClientsClient, oidcclientvalidator.DefaultMinBcryptCost),
issuerURL,
tokenHMACKeyGetter,
nil,
@ -110,7 +111,7 @@ func (m *Manager) SetFederationDomains(federationDomains ...*federationdomainpro
// 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, m.oidcClientsClient, timeoutsConfiguration, oidcclientvalidator.DefaultMinBcryptCost),
storage.NewKubeStorage(m.secretsClient, m.oidcClientsClient, timeoutsConfiguration, oidcclientvalidator.DefaultMinBcryptCost),
issuerURL,
tokenHMACKeyGetter,
m.dynamicJWKSProvider,

@ -1,7 +1,7 @@
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package manager
package endpointsmanager
import (
"crypto/ecdsa"
@ -20,12 +20,12 @@ import (
"k8s.io/client-go/kubernetes/fake"
supervisorfake "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/fake"
"go.pinniped.dev/internal/federationdomain/endpoints/discovery"
"go.pinniped.dev/internal/federationdomain/endpoints/jwks"
"go.pinniped.dev/internal/federationdomain/federationdomainproviders"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/here"
"go.pinniped.dev/internal/idtransform"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/oidc/discovery"
"go.pinniped.dev/internal/oidc/jwks"
"go.pinniped.dev/internal/oidc/provider/federationdomainproviders"
"go.pinniped.dev/internal/secret"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/internal/testutil/oidctestutil"

@ -9,9 +9,9 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"go.pinniped.dev/internal/federationdomain/idplister"
"go.pinniped.dev/internal/federationdomain/resolvedprovider"
"go.pinniped.dev/internal/idtransform"
"go.pinniped.dev/internal/oidc/idplister"
"go.pinniped.dev/internal/oidc/provider/resolvedprovider"
"go.pinniped.dev/internal/psession"
)

@ -1,4 +1,4 @@
/* Copyright 2021 the Pinniped contributors. All Rights Reserved. */
/* Copyright 2021-2023 the Pinniped contributors. All Rights Reserved. */
/* SPDX-License-Identifier: Apache-2.0 */
body {

@ -1,5 +1,5 @@
<!--
Copyright 2021 the Pinniped contributors. All Rights Reserved.
Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
--><!DOCTYPE html>
<html lang="en">

@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
window.onload = () => {

@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Package formposthtml defines HTML templates used by the Supervisor.
@ -11,7 +11,7 @@ import (
"github.com/tdewolff/minify/v2/minify"
"go.pinniped.dev/internal/oidc/provider/csp"
"go.pinniped.dev/internal/federationdomain/csp"
)
//nolint:gochecknoglobals // This package uses globals to ensure that all parsing and minifying happens at init.

@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package formposthtml

@ -4,7 +4,7 @@
package idplister
import (
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
)
type UpstreamOIDCIdentityProvidersLister interface {

@ -1,7 +1,8 @@
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Package oidc contains common OIDC functionality needed by Pinniped.
// Package oidc contains common OIDC functionality needed by FederationDomains to implement
// downstream OIDC functionality.
package oidc
import (
@ -18,10 +19,13 @@ import (
errorsx "github.com/pkg/errors"
oidcapi "go.pinniped.dev/generated/latest/apis/supervisor/oidc"
"go.pinniped.dev/internal/federationdomain/csrftoken"
"go.pinniped.dev/internal/federationdomain/endpoints/jwks"
"go.pinniped.dev/internal/federationdomain/endpoints/tokenexchange"
"go.pinniped.dev/internal/federationdomain/formposthtml"
"go.pinniped.dev/internal/federationdomain/strategy"
"go.pinniped.dev/internal/federationdomain/timeouts"
"go.pinniped.dev/internal/httputil/httperr"
"go.pinniped.dev/internal/oidc/csrftoken"
"go.pinniped.dev/internal/oidc/jwks"
"go.pinniped.dev/internal/oidc/provider/formposthtml"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/pkg/oidcclient/nonce"
@ -99,81 +103,13 @@ type UpstreamStateParamData struct {
FormatVersion string `json:"v"`
}
type TimeoutsConfiguration struct {
// The length of time that our state param that we encrypt and pass to the upstream OIDC IDP should be considered
// valid. If a state param generated by the authorize endpoint is sent to the callback endpoint after this much
// time has passed, then the callback endpoint should reject it. This allows us to set a limit on how long
// the end user has to finish their login with the upstream IDP, including the time that it takes to fumble
// with password manager and two-factor authenticator apps, and also accounting for taking a coffee break while
// the browser is sitting at the upstream IDP's login page.
UpstreamStateParamLifespan time.Duration
// How long an authcode issued by the callback endpoint is valid. This determines how much time the end user
// has to come back to exchange the authcode for tokens at the token endpoint.
AuthorizeCodeLifespan time.Duration
// The lifetime of an downstream access token issued by the token endpoint. Access tokens should generally
// be fairly short-lived.
AccessTokenLifespan time.Duration
// The lifetime of an downstream ID token issued by the token endpoint. This should generally be the same
// as the AccessTokenLifespan, or longer if it would be useful for the user's proof of identity to be valid
// for longer than their proof of authorization.
IDTokenLifespan time.Duration
// The lifetime of an downstream refresh token issued by the token endpoint. This should generally be
// significantly longer than the access token lifetime, so it can be used to refresh the access token
// multiple times. Once the refresh token expires, the user's session is over and they will need
// to start a new authorization request, which will require them to log in again with the upstream IDP
// in their web browser.
RefreshTokenLifespan time.Duration
// AuthorizationCodeSessionStorageLifetime is the length of time after which an authcode is allowed to be garbage
// collected from storage. Authcodes are kept in storage after they are redeemed to allow the system to mark the
// authcode as already used, so it can reject any future uses of the same authcode with special case handling which
// include revoking the access and refresh tokens associated with the session. Therefore, this should be
// significantly longer than the AuthorizeCodeLifespan, and there is probably no reason to make it longer than
// the sum of the AuthorizeCodeLifespan and the RefreshTokenLifespan.
AuthorizationCodeSessionStorageLifetime time.Duration
// PKCESessionStorageLifetime is the length of time after which PKCE data is allowed to be garbage collected from
// storage. PKCE sessions are closely related to authorization code sessions. After the authcode is successfully
// redeemed, the PKCE session is explicitly deleted. After the authcode expires, the PKCE session is no longer needed,
// but it is not explicitly deleted. Therefore, this can be just slightly longer than the AuthorizeCodeLifespan. We'll
// avoid making it exactly the same as AuthorizeCodeLifespan to avoid any chance of the garbage collector deleting it
// while it is being used.
PKCESessionStorageLifetime time.Duration
// OIDCSessionStorageLifetime is the length of time after which the OIDC session data related to an authcode
// is allowed to be garbage collected from storage. Due to a bug in an underlying library, these are not explicitly
// deleted. Similar to the PKCE session, they are not needed anymore after the corresponding authcode has expired.
// Therefore, this can be just slightly longer than the AuthorizeCodeLifespan. We'll avoid making it exactly the same
// as AuthorizeCodeLifespan to avoid any chance of the garbage collector deleting it while it is being used.
OIDCSessionStorageLifetime time.Duration
// AccessTokenSessionStorageLifetime is the length of time after which an access token's session data is allowed
// to be garbage collected from storage. These must exist in storage for as long as the refresh token is valid
// or else the refresh flow will not work properly. So this must be longer than RefreshTokenLifespan.
AccessTokenSessionStorageLifetime time.Duration
// RefreshTokenSessionStorageLifetime is the length of time after which a refresh token's session data is allowed
// to be garbage collected from storage. These must exist in storage for as long as the refresh token is valid.
// Therefore, this can be just slightly longer than the RefreshTokenLifespan. We'll avoid making it exactly the same
// as RefreshTokenLifespan to avoid any chance of the garbage collector deleting it while it is being used.
// If an expired token is still stored when the user tries to refresh it, then they will get a more specific
// error message telling them that the token is expired, rather than a more generic error that is returned
// when the token does not exist. If this is desirable, then the RefreshTokenSessionStorageLifetime can be made
// to be significantly larger than RefreshTokenLifespan, at the cost of slower cleanup.
RefreshTokenSessionStorageLifetime time.Duration
}
// Get the defaults for the Supervisor server.
func DefaultOIDCTimeoutsConfiguration() TimeoutsConfiguration {
func DefaultOIDCTimeoutsConfiguration() timeouts.Configuration {
accessTokenLifespan := 2 * time.Minute
authorizationCodeLifespan := 10 * time.Minute
refreshTokenLifespan := 9 * time.Hour
return TimeoutsConfiguration{
return timeouts.Configuration{
UpstreamStateParamLifespan: 90 * time.Minute,
AuthorizeCodeLifespan: authorizationCodeLifespan,
AccessTokenLifespan: accessTokenLifespan,
@ -192,7 +128,7 @@ func FositeOauth2Helper(
issuer string,
hmacSecretOfLengthAtLeast32Func func() []byte,
jwksProvider jwks.DynamicJWKSProvider,
timeoutsConfiguration TimeoutsConfiguration,
timeoutsConfiguration timeouts.Configuration,
) fosite.OAuth2Provider {
isRedirectURISecureStrict := func(_ context.Context, uri *url.URL) bool {
return fosite.IsRedirectURISecureStrict(uri)
@ -234,15 +170,15 @@ func FositeOauth2Helper(
oauthStore,
&compose.CommonStrategy{
// Note that Fosite requires the HMAC secret to be at least 32 bytes.
CoreStrategy: newDynamicOauth2HMACStrategy(oauthConfig, hmacSecretOfLengthAtLeast32Func),
OpenIDConnectTokenStrategy: newDynamicOpenIDConnectECDSAStrategy(oauthConfig, jwksProvider),
CoreStrategy: strategy.NewDynamicOauth2HMACStrategy(oauthConfig, hmacSecretOfLengthAtLeast32Func),
OpenIDConnectTokenStrategy: strategy.NewDynamicOpenIDConnectECDSAStrategy(oauthConfig, jwksProvider),
},
compose.OAuth2AuthorizeExplicitFactory,
compose.OAuth2RefreshTokenGrantFactory,
compose.OpenIDConnectExplicitFactory,
compose.OpenIDConnectRefreshFactory,
compose.OAuth2PKCEFactory,
TokenExchangeFactory, // handle the "urn:ietf:params:oauth:grant-type:token-exchange" grant type
tokenexchange.HandlerFactory, // handle the "urn:ietf:params:oauth:grant-type:token-exchange" grant type
)
return oAuth2Provider

@ -1,8 +1,11 @@
// Copyright 2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package resolvedprovider
import (
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/idtransform"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/psession"
)

@ -1,7 +1,7 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package oidc
package storage
import (
"context"

@ -1,7 +1,7 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package oidc
package storage
import (
"context"
@ -14,13 +14,14 @@ import (
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
"go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/typed/config/v1alpha1"
"go.pinniped.dev/internal/federationdomain/clientregistry"
"go.pinniped.dev/internal/federationdomain/timeouts"
"go.pinniped.dev/internal/fositestorage/accesstoken"
"go.pinniped.dev/internal/fositestorage/authorizationcode"
"go.pinniped.dev/internal/fositestorage/openidconnect"
"go.pinniped.dev/internal/fositestorage/pkce"
"go.pinniped.dev/internal/fositestorage/refreshtoken"
"go.pinniped.dev/internal/fositestoragei"
"go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/oidcclientsecretstorage"
)
@ -38,7 +39,7 @@ var _ fositestoragei.AllFositeStorage = &KubeStorage{}
func NewKubeStorage(
secrets corev1client.SecretInterface,
oidcClientsClient v1alpha1.OIDCClientInterface,
timeoutsConfiguration TimeoutsConfiguration,
timeoutsConfiguration timeouts.Configuration,
minBcryptCost int,
) *KubeStorage {
nowFunc := time.Now

@ -1,7 +1,7 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package oidc
package storage
import (
"context"
@ -11,8 +11,8 @@ import (
"go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/typed/config/v1alpha1"
"go.pinniped.dev/internal/constable"
"go.pinniped.dev/internal/federationdomain/clientregistry"
"go.pinniped.dev/internal/fositestoragei"
"go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/oidcclientsecretstorage"
)

@ -1,7 +1,7 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package oidc
package strategy
import (
"context"
@ -11,6 +11,8 @@ import (
"github.com/ory/fosite/compose"
"github.com/ory/fosite/handler/oauth2"
errorsx "github.com/pkg/errors"
"go.pinniped.dev/internal/federationdomain/storage"
)
const (
@ -24,7 +26,7 @@ const (
oryAuthcodePrefix = "ory_ac_"
)
// dynamicOauth2HMACStrategy is an oauth2.CoreStrategy that can dynamically load an HMAC key to sign
// DynamicOauth2HMACStrategy is an oauth2.CoreStrategy that can dynamically load an HMAC key to sign
// stuff (access tokens, refresh tokens, and auth codes). 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.
@ -37,18 +39,18 @@ const (
// out of context, such as when accidentally committed to a GitHub repo. After we implemented the
// custom prefix feature, fosite later added the same feature, but did not make the prefix customizable.
// Therefore, this code has been updated to replace the fosite prefix with our custom prefix.
type dynamicOauth2HMACStrategy struct {
type DynamicOauth2HMACStrategy struct {
fositeConfig *fosite.Config
keyFunc func() []byte
}
var _ oauth2.CoreStrategy = &dynamicOauth2HMACStrategy{}
var _ oauth2.CoreStrategy = &DynamicOauth2HMACStrategy{}
func newDynamicOauth2HMACStrategy(
func NewDynamicOauth2HMACStrategy(
fositeConfig *fosite.Config,
keyFunc func() []byte,
) *dynamicOauth2HMACStrategy {
return &dynamicOauth2HMACStrategy{
) *DynamicOauth2HMACStrategy {
return &DynamicOauth2HMACStrategy{
fositeConfig: fositeConfig,
keyFunc: keyFunc,
}
@ -58,11 +60,11 @@ func replacePrefix(s, prefixToReplace, newPrefix string) string {
return newPrefix + strings.TrimPrefix(s, prefixToReplace)
}
func (s *dynamicOauth2HMACStrategy) AccessTokenSignature(ctx context.Context, token string) string {
func (s *DynamicOauth2HMACStrategy) AccessTokenSignature(ctx context.Context, token string) string {
return s.delegate().AccessTokenSignature(ctx, token)
}
func (s *dynamicOauth2HMACStrategy) GenerateAccessToken(
func (s *DynamicOauth2HMACStrategy) GenerateAccessToken(
ctx context.Context,
requester fosite.Requester,
) (string, string, error) {
@ -78,7 +80,7 @@ func (s *dynamicOauth2HMACStrategy) GenerateAccessToken(
return token, sig, err
}
func (s *dynamicOauth2HMACStrategy) ValidateAccessToken(
func (s *DynamicOauth2HMACStrategy) ValidateAccessToken(
ctx context.Context,
requester fosite.Requester,
token string,
@ -90,11 +92,11 @@ func (s *dynamicOauth2HMACStrategy) ValidateAccessToken(
return s.delegate().ValidateAccessToken(ctx, requester, replacePrefix(token, pinAccessTokenPrefix, oryAccessTokenPrefix))
}
func (s *dynamicOauth2HMACStrategy) RefreshTokenSignature(ctx context.Context, token string) string {
func (s *DynamicOauth2HMACStrategy) RefreshTokenSignature(ctx context.Context, token string) string {
return s.delegate().RefreshTokenSignature(ctx, token)
}
func (s *dynamicOauth2HMACStrategy) GenerateRefreshToken(
func (s *DynamicOauth2HMACStrategy) GenerateRefreshToken(
ctx context.Context,
requester fosite.Requester,
) (string, string, error) {
@ -110,7 +112,7 @@ func (s *dynamicOauth2HMACStrategy) GenerateRefreshToken(
return token, sig, err
}
func (s *dynamicOauth2HMACStrategy) ValidateRefreshToken(
func (s *DynamicOauth2HMACStrategy) ValidateRefreshToken(
ctx context.Context,
requester fosite.Requester,
token string,
@ -122,11 +124,11 @@ func (s *dynamicOauth2HMACStrategy) ValidateRefreshToken(
return s.delegate().ValidateRefreshToken(ctx, requester, replacePrefix(token, pinRefreshTokenPrefix, oryRefreshTokenPrefix))
}
func (s *dynamicOauth2HMACStrategy) AuthorizeCodeSignature(ctx context.Context, token string) string {
func (s *DynamicOauth2HMACStrategy) AuthorizeCodeSignature(ctx context.Context, token string) string {
return s.delegate().AuthorizeCodeSignature(ctx, token)
}
func (s *dynamicOauth2HMACStrategy) GenerateAuthorizeCode(
func (s *DynamicOauth2HMACStrategy) GenerateAuthorizeCode(
ctx context.Context,
requester fosite.Requester,
) (string, string, error) {
@ -142,7 +144,7 @@ func (s *dynamicOauth2HMACStrategy) GenerateAuthorizeCode(
return authcode, sig, err
}
func (s *dynamicOauth2HMACStrategy) ValidateAuthorizeCode(
func (s *DynamicOauth2HMACStrategy) ValidateAuthorizeCode(
ctx context.Context,
requester fosite.Requester,
token string,
@ -154,6 +156,6 @@ func (s *dynamicOauth2HMACStrategy) ValidateAuthorizeCode(
return s.delegate().ValidateAuthorizeCode(ctx, requester, replacePrefix(token, pinAuthcodePrefix, oryAuthcodePrefix))
}
func (s *dynamicOauth2HMACStrategy) delegate() *oauth2.HMACSHAStrategy {
return compose.NewOAuth2HMACStrategy(NewDynamicGlobalSecretConfig(s.fositeConfig, s.keyFunc))
func (s *DynamicOauth2HMACStrategy) delegate() *oauth2.HMACSHAStrategy {
return compose.NewOAuth2HMACStrategy(storage.NewDynamicGlobalSecretConfig(s.fositeConfig, s.keyFunc))
}

@ -1,7 +1,7 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2022-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package oidc
package strategy
import (
"context"
@ -15,7 +15,7 @@ import (
)
func TestDynamicOauth2HMACStrategy_Signatures(t *testing.T) {
s := newDynamicOauth2HMACStrategy(
s := NewDynamicOauth2HMACStrategy(
&fosite.Config{}, // defaults are good enough for this unit test
func() []byte { return []byte("12345678901234567890123456789012") }, // 32 character secret key
)
@ -57,12 +57,12 @@ func TestDynamicOauth2HMACStrategy_Signatures(t *testing.T) {
}
func TestDynamicOauth2HMACStrategy_Generate(t *testing.T) {
s := newDynamicOauth2HMACStrategy(
s := NewDynamicOauth2HMACStrategy(
&fosite.Config{}, // defaults are good enough for this unit test
func() []byte { return []byte("12345678901234567890123456789012") }, // 32 character secret key
)
generateTokenErrorCausingStrategy := newDynamicOauth2HMACStrategy(
generateTokenErrorCausingStrategy := NewDynamicOauth2HMACStrategy(
&fosite.Config{},
func() []byte { return []byte("too_short_causes_error") }, // secret key is below required 32 characters
)
@ -134,7 +134,7 @@ func TestDynamicOauth2HMACStrategy_Generate(t *testing.T) {
}
func TestDynamicOauth2HMACStrategy_Validate(t *testing.T) {
s := newDynamicOauth2HMACStrategy(
s := NewDynamicOauth2HMACStrategy(
&fosite.Config{}, // defaults are good enough for this unit test
func() []byte { return []byte("12345678901234567890123456789012") }, // 32 character secret key
)

@ -1,7 +1,7 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package oidc
package strategy
import (
"context"
@ -14,11 +14,11 @@ import (
"github.com/ory/fosite/handler/openid"
"go.pinniped.dev/internal/constable"
"go.pinniped.dev/internal/oidc/jwks"
"go.pinniped.dev/internal/federationdomain/endpoints/jwks"
"go.pinniped.dev/internal/plog"
)
// dynamicOpenIDConnectECDSAStrategy is an openid.OpenIDConnectTokenStrategy that can dynamically
// 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.
@ -26,24 +26,24 @@ import (
// 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 {
type DynamicOpenIDConnectECDSAStrategy struct {
fositeConfig *fosite.Config
jwksProvider jwks.DynamicJWKSProvider
}
var _ openid.OpenIDConnectTokenStrategy = &dynamicOpenIDConnectECDSAStrategy{}
var _ openid.OpenIDConnectTokenStrategy = &DynamicOpenIDConnectECDSAStrategy{}
func newDynamicOpenIDConnectECDSAStrategy(
func NewDynamicOpenIDConnectECDSAStrategy(
fositeConfig *fosite.Config,
jwksProvider jwks.DynamicJWKSProvider,
) *dynamicOpenIDConnectECDSAStrategy {
return &dynamicOpenIDConnectECDSAStrategy{
) *DynamicOpenIDConnectECDSAStrategy {
return &DynamicOpenIDConnectECDSAStrategy{
fositeConfig: fositeConfig,
jwksProvider: jwksProvider,
}
}
func (s *dynamicOpenIDConnectECDSAStrategy) GenerateIDToken(
func (s *DynamicOpenIDConnectECDSAStrategy) GenerateIDToken(
ctx context.Context,
lifespan time.Duration,
requester fosite.Requester,

@ -1,7 +1,7 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package oidc
package strategy
import (
"context"
@ -20,7 +20,7 @@ import (
"github.com/stretchr/testify/require"
"gopkg.in/square/go-jose.v2"
"go.pinniped.dev/internal/oidc/jwks"
"go.pinniped.dev/internal/federationdomain/endpoints/jwks"
"go.pinniped.dev/internal/testutil/oidctestutil"
)
@ -94,7 +94,7 @@ func TestDynamicOpenIDConnectECDSAStrategy(t *testing.T) {
if test.jwksProvider != nil {
test.jwksProvider(jwksProvider)
}
s := newDynamicOpenIDConnectECDSAStrategy(
s := NewDynamicOpenIDConnectECDSAStrategy(
&fosite.Config{IDTokenIssuer: test.issuer},
jwksProvider,
)

@ -0,0 +1,74 @@
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package timeouts
import "time"
type Configuration struct {
// The length of time that our state param that we encrypt and pass to the upstream OIDC IDP should be considered
// valid. If a state param generated by the authorize endpoint is sent to the callback endpoint after this much
// time has passed, then the callback endpoint should reject it. This allows us to set a limit on how long
// the end user has to finish their login with the upstream IDP, including the time that it takes to fumble
// with password manager and two-factor authenticator apps, and also accounting for taking a coffee break while
// the browser is sitting at the upstream IDP's login page.
UpstreamStateParamLifespan time.Duration
// How long an authcode issued by the callback endpoint is valid. This determines how much time the end user
// has to come back to exchange the authcode for tokens at the token endpoint.
AuthorizeCodeLifespan time.Duration
// The lifetime of an downstream access token issued by the token endpoint. Access tokens should generally
// be fairly short-lived.
AccessTokenLifespan time.Duration
// The lifetime of an downstream ID token issued by the token endpoint. This should generally be the same
// as the AccessTokenLifespan, or longer if it would be useful for the user's proof of identity to be valid
// for longer than their proof of authorization.
IDTokenLifespan time.Duration
// The lifetime of an downstream refresh token issued by the token endpoint. This should generally be
// significantly longer than the access token lifetime, so it can be used to refresh the access token
// multiple times. Once the refresh token expires, the user's session is over and they will need
// to start a new authorization request, which will require them to log in again with the upstream IDP
// in their web browser.
RefreshTokenLifespan time.Duration
// AuthorizationCodeSessionStorageLifetime is the length of time after which an authcode is allowed to be garbage
// collected from storage. Authcodes are kept in storage after they are redeemed to allow the system to mark the
// authcode as already used, so it can reject any future uses of the same authcode with special case handling which
// include revoking the access and refresh tokens associated with the session. Therefore, this should be
// significantly longer than the AuthorizeCodeLifespan, and there is probably no reason to make it longer than
// the sum of the AuthorizeCodeLifespan and the RefreshTokenLifespan.
AuthorizationCodeSessionStorageLifetime time.Duration
// PKCESessionStorageLifetime is the length of time after which PKCE data is allowed to be garbage collected from
// storage. PKCE sessions are closely related to authorization code sessions. After the authcode is successfully
// redeemed, the PKCE session is explicitly deleted. After the authcode expires, the PKCE session is no longer needed,
// but it is not explicitly deleted. Therefore, this can be just slightly longer than the AuthorizeCodeLifespan. We'll
// avoid making it exactly the same as AuthorizeCodeLifespan to avoid any chance of the garbage collector deleting it
// while it is being used.
PKCESessionStorageLifetime time.Duration
// OIDCSessionStorageLifetime is the length of time after which the OIDC session data related to an authcode
// is allowed to be garbage collected from storage. Due to a bug in an underlying library, these are not explicitly
// deleted. Similar to the PKCE session, they are not needed anymore after the corresponding authcode has expired.
// Therefore, this can be just slightly longer than the AuthorizeCodeLifespan. We'll avoid making it exactly the same
// as AuthorizeCodeLifespan to avoid any chance of the garbage collector deleting it while it is being used.
OIDCSessionStorageLifetime time.Duration
// AccessTokenSessionStorageLifetime is the length of time after which an access token's session data is allowed
// to be garbage collected from storage. These must exist in storage for as long as the refresh token is valid
// or else the refresh flow will not work properly. So this must be longer than RefreshTokenLifespan.
AccessTokenSessionStorageLifetime time.Duration
// RefreshTokenSessionStorageLifetime is the length of time after which a refresh token's session data is allowed
// to be garbage collected from storage. These must exist in storage for as long as the refresh token is valid.
// Therefore, this can be just slightly longer than the RefreshTokenLifespan. We'll avoid making it exactly the same
// as RefreshTokenLifespan to avoid any chance of the garbage collector deleting it while it is being used.
// If an expired token is still stored when the user tries to refresh it, then they will get a more specific
// error message telling them that the token is expired, rather than a more generic error that is returned
// when the token does not exist. If this is desirable, then the RefreshTokenSessionStorageLifetime can be made
// to be significantly larger than RefreshTokenLifespan, at the cost of slower cleanup.
RefreshTokenSessionStorageLifetime time.Duration
}

@ -16,8 +16,8 @@ import (
"go.pinniped.dev/internal/constable"
"go.pinniped.dev/internal/crud"
"go.pinniped.dev/internal/federationdomain/clientregistry"
"go.pinniped.dev/internal/fositestorage"
"go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/psession"
)

@ -22,7 +22,7 @@ import (
coretesting "k8s.io/client-go/testing"
clocktesting "k8s.io/utils/clock/testing"
"go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/federationdomain/clientregistry"
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/internal/testutil"
)

@ -17,8 +17,8 @@ import (
"go.pinniped.dev/internal/constable"
"go.pinniped.dev/internal/crud"
"go.pinniped.dev/internal/federationdomain/clientregistry"
"go.pinniped.dev/internal/fositestorage"
"go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/psession"
)

@ -34,8 +34,8 @@ import (
kubetesting "k8s.io/client-go/testing"
clocktesting "k8s.io/utils/clock/testing"
"go.pinniped.dev/internal/federationdomain/clientregistry"
"go.pinniped.dev/internal/fositestorage"
"go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/internal/testutil"
)

@ -1,4 +1,4 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package fositestorage
@ -7,7 +7,7 @@ import (
"github.com/ory/fosite"
"go.pinniped.dev/internal/constable"
"go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/federationdomain/clientregistry"
"go.pinniped.dev/internal/psession"
)

@ -16,8 +16,8 @@ import (
"go.pinniped.dev/internal/constable"
"go.pinniped.dev/internal/crud"
"go.pinniped.dev/internal/federationdomain/clientregistry"
"go.pinniped.dev/internal/fositestorage"
"go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/psession"
)

@ -21,7 +21,7 @@ import (
coretesting "k8s.io/client-go/testing"
clocktesting "k8s.io/utils/clock/testing"
"go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/federationdomain/clientregistry"
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/internal/testutil"
)

@ -15,8 +15,8 @@ import (
"go.pinniped.dev/internal/constable"
"go.pinniped.dev/internal/crud"
"go.pinniped.dev/internal/federationdomain/clientregistry"
"go.pinniped.dev/internal/fositestorage"
"go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/psession"
)

@ -21,7 +21,7 @@ import (
coretesting "k8s.io/client-go/testing"
clocktesting "k8s.io/utils/clock/testing"
"go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/federationdomain/clientregistry"
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/internal/testutil"
)

@ -16,8 +16,8 @@ import (
"go.pinniped.dev/internal/constable"
"go.pinniped.dev/internal/crud"
"go.pinniped.dev/internal/federationdomain/clientregistry"
"go.pinniped.dev/internal/fositestorage"
"go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/psession"
)

@ -22,7 +22,7 @@ import (
coretesting "k8s.io/client-go/testing"
clocktesting "k8s.io/utils/clock/testing"
"go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/federationdomain/clientregistry"
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/internal/testutil"
)

@ -1,6 +1,6 @@
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package issuermocks
//go:generate go run -v github.com/golang/mock/mockgen -destination=issuermocks.go -package=issuermocks -copyright_file=../../../hack/header.txt go.pinniped.dev/internal/issuer ClientCertIssuer
//go:generate go run -v github.com/golang/mock/mockgen -destination=issuermocks.go -package=issuermocks -copyright_file=../../../hack/header.txt go.pinniped.dev/internal/clientcertissuer ClientCertIssuer

@ -3,7 +3,7 @@
//
// Code generated by MockGen. DO NOT EDIT.
// Source: go.pinniped.dev/internal/issuer (interfaces: ClientCertIssuer)
// Source: go.pinniped.dev/internal/clientcertissuer (interfaces: ClientCertIssuer)
// Package issuermocks is a generated GoMock package.
package issuermocks

@ -3,4 +3,4 @@
package mockupstreamoidcidentityprovider
//go:generate go run -v github.com/golang/mock/mockgen -destination=mockupstreamoidcidentityprovider.go -package=mockupstreamoidcidentityprovider -copyright_file=../../../hack/header.txt go.pinniped.dev/internal/oidc/provider/upstreamprovider UpstreamOIDCIdentityProviderI
//go:generate go run -v github.com/golang/mock/mockgen -destination=mockupstreamoidcidentityprovider.go -package=mockupstreamoidcidentityprovider -copyright_file=../../../hack/header.txt go.pinniped.dev/internal/federationdomain/upstreamprovider UpstreamOIDCIdentityProviderI

@ -3,7 +3,7 @@
//
// Code generated by MockGen. DO NOT EDIT.
// Source: go.pinniped.dev/internal/oidc/provider/upstreamprovider (interfaces: UpstreamOIDCIdentityProviderI)
// Source: go.pinniped.dev/internal/federationdomain/upstreamprovider (interfaces: UpstreamOIDCIdentityProviderI)
// Package mockupstreamoidcidentityprovider is a generated GoMock package.
package mockupstreamoidcidentityprovider
@ -14,7 +14,7 @@ import (
reflect "reflect"
gomock "github.com/golang/mock/gomock"
upstreamprovider "go.pinniped.dev/internal/oidc/provider/upstreamprovider"
upstreamprovider "go.pinniped.dev/internal/federationdomain/upstreamprovider"
nonce "go.pinniped.dev/pkg/oidcclient/nonce"
oidctypes "go.pinniped.dev/pkg/oidcclient/oidctypes"
pkce "go.pinniped.dev/pkg/oidcclient/pkce"

@ -58,12 +58,13 @@ import (
"go.pinniped.dev/internal/deploymentref"
"go.pinniped.dev/internal/downward"
"go.pinniped.dev/internal/dynamiccert"
"go.pinniped.dev/internal/federationdomain/dynamictlscertprovider"
"go.pinniped.dev/internal/federationdomain/dynamicupstreamprovider"
"go.pinniped.dev/internal/federationdomain/endpoints/jwks"
"go.pinniped.dev/internal/federationdomain/endpointsmanager"
"go.pinniped.dev/internal/groupsuffix"
"go.pinniped.dev/internal/kubeclient"
"go.pinniped.dev/internal/leaderelection"
"go.pinniped.dev/internal/oidc/jwks"
"go.pinniped.dev/internal/oidc/provider"
"go.pinniped.dev/internal/oidc/provider/manager"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/pversion"
"go.pinniped.dev/internal/secret"
@ -129,10 +130,10 @@ func signalCtx() context.Context {
//nolint:funlen
func prepareControllers(
cfg *supervisor.Config,
issuerManager *manager.Manager,
issuerManager *endpointsmanager.Manager,
dynamicJWKSProvider jwks.DynamicJWKSProvider,
dynamicTLSCertProvider provider.DynamicTLSCertProvider,
dynamicUpstreamIDPProvider provider.DynamicUpstreamIDPProvider,
dynamicTLSCertProvider dynamictlscertprovider.DynamicTLSCertProvider,
dynamicUpstreamIDPProvider dynamicupstreamprovider.DynamicUpstreamIDPProvider,
dynamicServingCertProvider dynamiccert.Private,
secretCache *secret.Cache,
supervisorDeployment *appsv1.Deployment,
@ -436,12 +437,12 @@ func runSupervisor(ctx context.Context, podInfo *downward.PodInfo, cfg *supervis
dynamicServingCertProvider := dynamiccert.NewServingCert("supervisor-serving-cert")
dynamicJWKSProvider := jwks.NewDynamicJWKSProvider()
dynamicTLSCertProvider := provider.NewDynamicTLSCertProvider()
dynamicUpstreamIDPProvider := provider.NewDynamicUpstreamIDPProvider()
dynamicTLSCertProvider := dynamictlscertprovider.NewDynamicTLSCertProvider()
dynamicUpstreamIDPProvider := dynamicupstreamprovider.NewDynamicUpstreamIDPProvider()
secretCache := secret.Cache{}
// OIDC endpoints will be served by the oidProvidersManager, and any non-OIDC paths will fallback to the healthMux.
oidProvidersManager := manager.NewManager(
oidProvidersManager := endpointsmanager.NewManager(
healthMux,
dynamicJWKSProvider,
dynamicUpstreamIDPProvider,

@ -1,4 +1,4 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2022-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package testutil
@ -6,10 +6,10 @@ package testutil
import (
"testing"
"go.pinniped.dev/internal/oidc/oidcclientvalidator"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/bcrypt"
"go.pinniped.dev/internal/federationdomain/oidcclientvalidator"
)
func TestBcryptConstants(t *testing.T) {

@ -30,19 +30,19 @@ import (
"go.pinniped.dev/internal/authenticators"
"go.pinniped.dev/internal/crud"
"go.pinniped.dev/internal/federationdomain/dynamicupstreamprovider"
"go.pinniped.dev/internal/federationdomain/resolvedprovider"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/fositestorage/authorizationcode"
"go.pinniped.dev/internal/fositestorage/openidconnect"
pkce2 "go.pinniped.dev/internal/fositestorage/pkce"
"go.pinniped.dev/internal/fositestorage/pkce"
"go.pinniped.dev/internal/fositestoragei"
"go.pinniped.dev/internal/idtransform"
"go.pinniped.dev/internal/oidc/provider"
"go.pinniped.dev/internal/oidc/provider/resolvedprovider"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/pkg/oidcclient/nonce"
"go.pinniped.dev/pkg/oidcclient/oidctypes"
"go.pinniped.dev/pkg/oidcclient/pkce"
oidcpkce "go.pinniped.dev/pkg/oidcclient/pkce"
)
// Test helpers for the OIDC package.
@ -52,7 +52,7 @@ import (
type ExchangeAuthcodeAndValidateTokenArgs struct {
Ctx context.Context
Authcode string
PKCECodeVerifier pkce.Code
PKCECodeVerifier oidcpkce.Code
ExpectedIDTokenNonce nonce.Nonce
RedirectURI string
}
@ -267,7 +267,7 @@ type TestUpstreamOIDCIdentityProvider struct {
ExchangeAuthcodeAndValidateTokensFunc func(
ctx context.Context,
authcode string,
pkceCodeVerifier pkce.Code,
pkceCodeVerifier oidcpkce.Code,
expectedIDTokenNonce nonce.Nonce,
) (*oidctypes.Token, error)
@ -358,7 +358,7 @@ func (u *TestUpstreamOIDCIdentityProvider) PasswordCredentialsGrantAndValidateTo
func (u *TestUpstreamOIDCIdentityProvider) ExchangeAuthcodeAndValidateTokens(
ctx context.Context,
authcode string,
pkceCodeVerifier pkce.Code,
pkceCodeVerifier oidcpkce.Code,
expectedIDTokenNonce nonce.Nonce,
redirectURI string,
) (*oidctypes.Token, error) {
@ -595,8 +595,8 @@ func (b *UpstreamIDPListerBuilder) BuildFederationDomainIdentityProvidersListerF
}
}
func (b *UpstreamIDPListerBuilder) BuildDynamicUpstreamIDPProvider() provider.DynamicUpstreamIDPProvider {
idpProvider := provider.NewDynamicUpstreamIDPProvider()
func (b *UpstreamIDPListerBuilder) BuildDynamicUpstreamIDPProvider() dynamicupstreamprovider.DynamicUpstreamIDPProvider {
idpProvider := dynamicupstreamprovider.NewDynamicUpstreamIDPProvider()
oidcUpstreams := make([]upstreamprovider.UpstreamOIDCIdentityProviderI, len(b.upstreamOIDCIdentityProviders))
for i := range b.upstreamOIDCIdentityProviders {
@ -1038,7 +1038,7 @@ func (u *TestUpstreamOIDCIdentityProviderBuilder) Build() *TestUpstreamOIDCIdent
AdditionalClaimMappings: u.additionalClaimMappings,
DisplayNameForFederationDomain: u.displayNameForFederationDomain,
TransformsForFederationDomain: u.transformsForFederationDomain,
ExchangeAuthcodeAndValidateTokensFunc: func(ctx context.Context, authcode string, pkceCodeVerifier pkce.Code, expectedIDTokenNonce nonce.Nonce) (*oidctypes.Token, error) {
ExchangeAuthcodeAndValidateTokensFunc: func(ctx context.Context, authcode string, pkceCodeVerifier oidcpkce.Code, expectedIDTokenNonce nonce.Nonce) (*oidctypes.Token, error) {
if u.authcodeExchangeErr != nil {
return nil, u.authcodeExchangeErr
}
@ -1223,7 +1223,7 @@ func RequireAuthCodeRegexpMatch(
)
// One PKCE should have been stored.
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secretsClient, labels.Set{crud.SecretLabelKey: pkce2.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secretsClient, labels.Set{crud.SecretLabelKey: pkce.TypeLabelValue}, 1)
validatePKCEStorage(
t,

@ -27,8 +27,8 @@ import (
"go.pinniped.dev/internal/authenticators"
"go.pinniped.dev/internal/crypto/ptls"
"go.pinniped.dev/internal/endpointaddr"
"go.pinniped.dev/internal/oidc/downstreamsession"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/federationdomain/downstreamsession"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/plog"
)

@ -25,8 +25,8 @@ import (
"go.pinniped.dev/internal/certauthority"
"go.pinniped.dev/internal/crypto/ptls"
"go.pinniped.dev/internal/endpointaddr"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/mocks/mockldapconn"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/internal/testutil/tlsassertions"
"go.pinniped.dev/internal/testutil/tlsserver"

@ -21,9 +21,9 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
oidcapi "go.pinniped.dev/generated/latest/apis/supervisor/oidc"
"go.pinniped.dev/internal/federationdomain/dynamicupstreamprovider"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/httputil/httperr"
"go.pinniped.dev/internal/oidc/provider"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/pkg/oidcclient/nonce"
"go.pinniped.dev/pkg/oidcclient/oidctypes"
@ -221,7 +221,7 @@ func (p *ProviderConfig) tryRevokeToken(
if err != nil {
// Couldn't connect to the server or some similar error.
// Could be a temporary network problem, so it might be worth retrying.
return false, provider.NewRetryableRevocationError(err)
return false, dynamicupstreamprovider.NewRetryableRevocationError(err)
}
defer resp.Body.Close()
@ -271,7 +271,7 @@ func (p *ProviderConfig) tryRevokeToken(
// be caused by an underlying problem which could potentially become resolved in the near future. We'll be
// optimistic and call all 5xx errors retryable.
plog.Trace("RevokeToken() got unexpected error response from provider's revocation endpoint", "providerName", p.Name, "usedBasicAuth", useBasicAuth, "statusCode", status)
return false, provider.NewRetryableRevocationError(fmt.Errorf("server responded with status %d", status))
return false, dynamicupstreamprovider.NewRetryableRevocationError(fmt.Errorf("server responded with status %d", status))
default:
// Any other error is probably not due to failed client auth, and is probably not worth retrying later.
plog.Trace("RevokeToken() got unexpected error response from provider's revocation endpoint", "providerName", p.Name, "usedBasicAuth", useBasicAuth, "statusCode", status)

@ -23,9 +23,9 @@ import (
"gopkg.in/square/go-jose.v2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"go.pinniped.dev/internal/federationdomain/dynamicupstreamprovider"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/mocks/mockkeyset"
"go.pinniped.dev/internal/oidc/provider"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/pkg/oidcclient/nonce"
"go.pinniped.dev/pkg/oidcclient/oidctypes"
@ -715,8 +715,8 @@ func TestProviderConfig(t *testing.T) {
testutil.RequireErrorStringFromErr(t, err, tt.wantErr)
if tt.wantRetryableErrType {
require.ErrorAs(t, err, &provider.RetryableRevocationError{})
} else if errors.As(err, &provider.RetryableRevocationError{}) {
require.ErrorAs(t, err, &dynamicupstreamprovider.RetryableRevocationError{})
} else if errors.As(err, &dynamicupstreamprovider.RetryableRevocationError{}) {
// There is no NotErrorAs() assertion available in the current version of testify, so do the equivalent.
require.Fail(t, "error should not be As RetryableRevocationError")
}

@ -30,10 +30,10 @@ import (
"k8s.io/utils/strings/slices"
oidcapi "go.pinniped.dev/generated/latest/apis/supervisor/oidc"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/httputil/httperr"
"go.pinniped.dev/internal/httputil/securityheader"
"go.pinniped.dev/internal/net/phttp"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/upstreamoidc"
"go.pinniped.dev/pkg/oidcclient/nonce"

@ -28,11 +28,11 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/httputil/httperr"
"go.pinniped.dev/internal/httputil/roundtripper"
"go.pinniped.dev/internal/mocks/mockupstreamoidcidentityprovider"
"go.pinniped.dev/internal/net/phttp"
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/internal/testutil/testlogger"

@ -160,7 +160,7 @@ Once dynamic clients are implemented:
#### New Dependencies
This should be kept to a very simple HTML page with minimal, clean CSS styling.
Javascript should be avoided.
The styling should match the [form post html page](https://github.com/vmware-tanzu/pinniped/tree/main/internal/oidc/provider/formposthtml)
The styling should match the [form post html page](https://github.com/vmware-tanzu/pinniped/tree/main/internal/federationdomain/formposthtml)
as much as possible, we should reuse some of the existing css and add to it to keep the style consistent.
#### Observability Considerations

@ -192,28 +192,28 @@ The Supervisor's endpoints are:
Each FederationDomain's endpoints are mounted under the path of the FederationDomain's `spec.issuer`,
if the `spec.issuer` URL has a path component specified. If the issuer has no path, then they are mounted under `/`.
These per-FederationDomain endpoint are all mounted by the code in
[internal/oidc/provider/manager/manager.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/oidc/provider/manager/manager.go).
[internal/federationdomain/endpointsmanager/manager.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/federationdomain/endpointsmanager/manager.go).
The per-FederationDomain endpoints are:
- `<issuer_path>/.well-known/openid-configuration` is the standard OIDC discovery endpoint, which can be used to discover all the other endpoints listed here.
See [internal/oidc/discovery/discovery_handler.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/oidc/discovery/discovery_handler.go).
See [internal/federationdomain/endpoints/discovery/discovery_handler.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/federationdomain/endpoints/discovery/discovery_handler.go).
- `<issuer_path>/jwks.json` is the standard OIDC JWKS discovery endpoint.
See [internal/oidc/jwks/jwks_handler.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/oidc/jwks/jwks_handler.go).
See [internal/federationdomain/endpoints/jwks/jwks_handler.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/federationdomain/endpoints/jwks/jwks_handler.go).
- `<issuer_path>/oauth2/authorize` is the standard OIDC authorize endpoint.
See [internal/oidc/auth/auth_handler.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/oidc/auth/auth_handler.go).
See [internal/federationdomain/endpoints/auth/auth_handler.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/federationdomain/endpoints/auth/auth_handler.go).
- `<issuer_path>/oauth2/token` is the standard OIDC token endpoint.
See [internal/oidc/token/token_handler.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/oidc/token/token_handler.go).
See [internal/federationdomain/endpoints/token/token_handler.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/federationdomain/endpoints/token/token_handler.go).
The token endpoint can handle the standard OIDC `authorization_code` and `refresh_token` grant types, and has also been
extended in [internal/oidc/token_exchange.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/oidc/token_exchange.go)
extended in [internal/federationdomain/endpoints/tokenexchange/token_exchange.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/federationdomain/endpoints/tokenexchange/token_exchange.go)
to handle an additional grant type for [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token exchanges to
reduce the applicable scope (technically, the `aud` claim) of ID tokens.
- `<issuer_path>/callback` is a special endpoint that is used as the redirect URL when performing an OIDC authcode flow against an upstream OIDC identity provider as configured by an OIDCIdentityProvider custom resource.
See [internal/oidc/callback/callback_handler.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/oidc/callback/callback_handler.go).
See [internal/federationdomain/endpoints/callback/callback_handler.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/federationdomain/endpoints/callback/callback_handler.go).
- `<issuer_path>/v1alpha1/pinniped_identity_providers` is a custom discovery endpoint for clients to learn about available upstream identity providers.
See [internal/oidc/idpdiscovery/idp_discovery_handler.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/oidc/idpdiscovery/idp_discovery_handler.go).
See [internal/federationdomain/endpoints/idpdiscovery/idp_discovery_handler.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/federationdomain/endpoints/idpdiscovery/idp_discovery_handler.go).
- `<issuer_path>/login` is a login UI page to support the optional browser-based login flow for LDAP and Active Directory identity providers.
See [internal/oidc/login/login_handler.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/oidc/login/login_handler.go).
See [internal/federationdomain/endpoints/login/login_handler.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/federationdomain/endpoints/login/login_handler.go).
The OIDC specifications implemented by the Supervisor can be found at [openid.net](https://openid.net/connect).

@ -19,8 +19,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.pinniped.dev/internal/federationdomain/formposthtml"
"go.pinniped.dev/internal/httputil/securityheader"
"go.pinniped.dev/internal/oidc/provider/formposthtml"
"go.pinniped.dev/test/testlib"
"go.pinniped.dev/test/testlib/browsertest"
)

@ -31,8 +31,9 @@ import (
configv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1"
idpv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1"
"go.pinniped.dev/internal/certauthority"
"go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/oidc/oidcclientvalidator"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/federationdomain/oidcclientvalidator"
"go.pinniped.dev/internal/federationdomain/storage"
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/pkg/oidcclient/nonce"
@ -2240,7 +2241,7 @@ func testSupervisorLogin(
// First use the latest downstream refresh token to look up the corresponding session in the Supervisor's storage.
supervisorSecretsClient := testlib.NewKubernetesClientset(t).CoreV1().Secrets(env.SupervisorNamespace)
supervisorOIDCClientsClient := testlib.NewSupervisorClientset(t).ConfigV1alpha1().OIDCClients(env.SupervisorNamespace)
oauthStore := oidc.NewKubeStorage(supervisorSecretsClient, supervisorOIDCClientsClient, oidc.DefaultOIDCTimeoutsConfiguration(), oidcclientvalidator.DefaultMinBcryptCost)
oauthStore := storage.NewKubeStorage(supervisorSecretsClient, supervisorOIDCClientsClient, oidc.DefaultOIDCTimeoutsConfiguration(), oidcclientvalidator.DefaultMinBcryptCost)
storedRefreshSession, err := oauthStore.GetRefreshTokenSession(ctx, signatureOfLatestRefreshToken, nil)
require.NoError(t, err)
@ -2302,7 +2303,7 @@ func testSupervisorLogin(
// First use the latest downstream refresh token to look up the corresponding session in the Supervisor's storage.
supervisorSecretsClient := testlib.NewKubernetesClientset(t).CoreV1().Secrets(env.SupervisorNamespace)
supervisorOIDCClientsClient := testlib.NewSupervisorClientset(t).ConfigV1alpha1().OIDCClients(env.SupervisorNamespace)
oauthStore := oidc.NewKubeStorage(supervisorSecretsClient, supervisorOIDCClientsClient, oidc.DefaultOIDCTimeoutsConfiguration(), oidcclientvalidator.DefaultMinBcryptCost)
oauthStore := storage.NewKubeStorage(supervisorSecretsClient, supervisorOIDCClientsClient, oidc.DefaultOIDCTimeoutsConfiguration(), oidcclientvalidator.DefaultMinBcryptCost)
storedRefreshSession, err := oauthStore.GetRefreshTokenSession(ctx, signatureOfLatestRefreshToken, nil)
require.NoError(t, err)

@ -1,4 +1,4 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package integration
@ -17,8 +17,8 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"go.pinniped.dev/internal/federationdomain/clientregistry"
"go.pinniped.dev/internal/fositestorage/authorizationcode"
"go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/test/testlib"
)

Some files were not shown because too many files have changed in this diff Show More