Upgrade project Go dependencies
Most of the changes in this commit are because of these fosite PRs which changed behavior and/or APIs in fosite: - https://github.com/ory/fosite/pull/667 - https://github.com/ory/fosite/pull/679 (from me!) - https://github.com/ory/fosite/pull/675 - https://github.com/ory/fosite/pull/688 Due to the changes in fosite PR #688, we need to bump our storage version for anything which stores the DefaultSession struct as JSON.
This commit is contained in:
parent
d35306aa85
commit
e1a0367b03
55
go.mod
55
go.mod
@ -2,21 +2,6 @@ module go.pinniped.dev
|
||||
|
||||
go 1.17
|
||||
|
||||
// Unfortuntely, having any indirect dependency on github.com/oleiade/reflections@v1.0.0
|
||||
// seems to cause Dependabot to stop scanning our dependencies due to a checksum error for the package.
|
||||
// The cause of the checksum error is described in https://github.com/oleiade/reflections/issues/14.
|
||||
//
|
||||
// According to `go mod graph`, this dependency is (currently) coming from:
|
||||
// go.pinniped.dev -> github.com/ory/x@v0.0.212 -> github.com/ory/analytics-go/v4@v4.0.0 -> github.com/ory/x@v0.0.110 -> github.com/ory/fosite@v0.29.0 -> github.com/oleiade/reflections@v1.0.0
|
||||
// So the issue is that older versions of ory/x had a direct dependency on an old version of Fosite.
|
||||
// Newer versions of ory/x do not depend on fosite anymore. We can use a replace directive until none
|
||||
// of our indirect dependencies pull in any old versions of ory/x anymore.
|
||||
//
|
||||
// Whenever we upgrade fosite and ory/x, we can try removing this replace directive and running
|
||||
// `go mod download` to see if github.com/oleiade/reflections@v1.0.0 still appears in our go.sum.
|
||||
// As long as it does, we probably need to keep this replace directive.
|
||||
replace github.com/oleiade/reflections => github.com/oleiade/reflections v1.0.1
|
||||
|
||||
// bumping github.com/ory/x to higher than v0.0.297 breaks k8s.io/apiserver via go.opentelemetry.io/otel/semconv
|
||||
// force the use of an old version for now as it seems to allow a newer ory/x without breaking the apiserver lib.
|
||||
// all go.opentelemetry.io replace directives are copied from:
|
||||
@ -55,23 +40,23 @@ require (
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/joshlf/go-acl v0.0.0-20200411065538-eae00ae38531
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
|
||||
github.com/ory/fosite v0.42.2
|
||||
github.com/ory/fosite v0.44.0
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sclevine/agouti v3.0.0+incompatible
|
||||
github.com/sclevine/spec v1.4.0
|
||||
github.com/spf13/cobra v1.5.0
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/tdewolff/minify/v2 v2.12.2
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/tdewolff/minify/v2 v2.12.4
|
||||
go.uber.org/atomic v1.10.0
|
||||
go.uber.org/zap v1.23.0
|
||||
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0
|
||||
golang.org/x/net v0.0.0-20220923203811-8be639271d50
|
||||
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1
|
||||
golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7
|
||||
golang.org/x/term v0.0.0-20220919170432-7a66f970e087
|
||||
golang.org/x/text v0.3.7
|
||||
go.uber.org/zap v1.24.0
|
||||
golang.org/x/crypto v0.4.0
|
||||
golang.org/x/net v0.4.0
|
||||
golang.org/x/oauth2 v0.3.0
|
||||
golang.org/x/sync v0.1.0
|
||||
golang.org/x/term v0.3.0
|
||||
golang.org/x/text v0.5.0
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
k8s.io/api v0.25.2
|
||||
k8s.io/apiextensions-apiserver v0.25.2
|
||||
@ -79,11 +64,11 @@ require (
|
||||
k8s.io/apiserver v0.25.2
|
||||
k8s.io/client-go v0.25.2
|
||||
k8s.io/component-base v0.25.2
|
||||
k8s.io/gengo v0.0.0-20220913193501-391367153a38
|
||||
k8s.io/gengo v0.0.0-20221011193443-fad74ee6edd9
|
||||
k8s.io/klog/v2 v2.80.1
|
||||
k8s.io/kube-aggregator v0.25.2
|
||||
k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea
|
||||
k8s.io/utils v0.0.0-20220922133306-665eaaec4324
|
||||
k8s.io/kube-openapi v0.0.0-20221207184640-f3cff1453715
|
||||
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
)
|
||||
|
||||
@ -105,8 +90,11 @@ require (
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/cristalhq/jwt/v4 v4.0.2 // indirect
|
||||
github.com/dave/jennifer v1.4.0 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/ecordell/optgen v0.0.6 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/form3tech-oss/jwt-go v3.2.5+incompatible // indirect
|
||||
@ -123,9 +111,11 @@ require (
|
||||
github.com/google/gnostic v0.6.9 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/joshlf/testutil v0.0.0-20170608050642-b5d8aa79d93d // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
@ -138,6 +128,7 @@ require (
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/ory/go-acc v0.2.8 // indirect
|
||||
github.com/ory/go-convenience v0.1.0 // indirect
|
||||
github.com/ory/viper v1.7.5 // indirect
|
||||
@ -155,7 +146,7 @@ require (
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/subosito/gotenv v1.4.0 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.6.3 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.6.4 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.4 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.4 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.4 // indirect
|
||||
@ -172,7 +163,7 @@ require (
|
||||
go.opentelemetry.io/proto/otlp v0.15.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
|
||||
golang.org/x/sys v0.3.0 // indirect
|
||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
|
@ -263,7 +263,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
when("there are valid, expired authcode secrets which contain upstream refresh tokens", func() {
|
||||
it.Before(func() {
|
||||
activeOIDCAuthcodeSession := &authorizationcode.Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Active: true,
|
||||
Request: &fosite.Request{
|
||||
ID: "request-id-1",
|
||||
@ -308,7 +308,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
r.NoError(kubeClient.Tracker().Add(activeOIDCAuthcodeSessionSecret))
|
||||
|
||||
inactiveOIDCAuthcodeSession := &authorizationcode.Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Active: false,
|
||||
Request: &fosite.Request{
|
||||
ID: "request-id-2",
|
||||
@ -387,7 +387,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
when("there are valid, expired authcode secrets which contain upstream access tokens", func() {
|
||||
it.Before(func() {
|
||||
activeOIDCAuthcodeSession := &authorizationcode.Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Active: true,
|
||||
Request: &fosite.Request{
|
||||
ID: "request-id-1",
|
||||
@ -432,7 +432,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
r.NoError(kubeClient.Tracker().Add(activeOIDCAuthcodeSessionSecret))
|
||||
|
||||
inactiveOIDCAuthcodeSession := &authorizationcode.Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Active: false,
|
||||
Request: &fosite.Request{
|
||||
ID: "request-id-2",
|
||||
@ -511,7 +511,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
when("there is an invalid, expired authcode secret", func() {
|
||||
it.Before(func() {
|
||||
invalidOIDCAuthcodeSession := &authorizationcode.Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Active: true,
|
||||
Request: &fosite.Request{
|
||||
ID: "", // it is invalid for there to be a missing request ID
|
||||
@ -580,7 +580,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
when("there is a valid, expired authcode secret but its upstream name does not match any existing upstream", func() {
|
||||
it.Before(func() {
|
||||
wrongProviderNameOIDCAuthcodeSession := &authorizationcode.Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Active: true,
|
||||
Request: &fosite.Request{
|
||||
ID: "request-id-1",
|
||||
@ -651,7 +651,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
when("there is a valid, expired authcode secret but its upstream UID does not match any existing upstream", func() {
|
||||
it.Before(func() {
|
||||
wrongProviderNameOIDCAuthcodeSession := &authorizationcode.Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Active: true,
|
||||
Request: &fosite.Request{
|
||||
ID: "request-id-1",
|
||||
@ -722,7 +722,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
when("there is a valid, recently expired authcode secret but the upstream revocation fails", func() {
|
||||
it.Before(func() {
|
||||
activeOIDCAuthcodeSession := &authorizationcode.Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Active: true,
|
||||
Request: &fosite.Request{
|
||||
ID: "request-id-1",
|
||||
@ -827,7 +827,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
when("there is a valid, long-since expired authcode secret but the upstream revocation fails", func() {
|
||||
it.Before(func() {
|
||||
activeOIDCAuthcodeSession := &authorizationcode.Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Active: true,
|
||||
Request: &fosite.Request{
|
||||
ID: "request-id-1",
|
||||
@ -906,7 +906,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
when("there are valid, expired access token secrets which contain upstream refresh tokens", func() {
|
||||
it.Before(func() {
|
||||
offlineAccessGrantedOIDCAccessTokenSession := &accesstoken.Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Request: &fosite.Request{
|
||||
GrantedScope: fosite.Arguments{"scope1", "scope2", "offline_access"},
|
||||
ID: "request-id-1",
|
||||
@ -951,7 +951,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
r.NoError(kubeClient.Tracker().Add(offlineAccessGrantedOIDCAccessTokenSessionSecret))
|
||||
|
||||
offlineAccessNotGrantedOIDCAccessTokenSession := &accesstoken.Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Request: &fosite.Request{
|
||||
GrantedScope: fosite.Arguments{"scope1", "scope2"},
|
||||
ID: "request-id-2",
|
||||
@ -1030,7 +1030,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
when("there are valid, expired access token secrets which contain upstream access tokens", func() {
|
||||
it.Before(func() {
|
||||
offlineAccessGrantedOIDCAccessTokenSession := &accesstoken.Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Request: &fosite.Request{
|
||||
GrantedScope: fosite.Arguments{"scope1", "scope2", "offline_access"},
|
||||
ID: "request-id-1",
|
||||
@ -1075,7 +1075,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
r.NoError(kubeClient.Tracker().Add(offlineAccessGrantedOIDCAccessTokenSessionSecret))
|
||||
|
||||
offlineAccessNotGrantedOIDCAccessTokenSession := &accesstoken.Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Request: &fosite.Request{
|
||||
GrantedScope: fosite.Arguments{"scope1", "scope2"},
|
||||
ID: "request-id-2",
|
||||
@ -1154,7 +1154,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
when("there are valid, expired refresh secrets which contain upstream refresh tokens", func() {
|
||||
it.Before(func() {
|
||||
oidcRefreshSession := &refreshtoken.Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Request: &fosite.Request{
|
||||
ID: "request-id-1",
|
||||
Client: &clientregistry.Client{},
|
||||
@ -1231,7 +1231,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
when("there are valid, expired refresh secrets which contain upstream access tokens", func() {
|
||||
it.Before(func() {
|
||||
oidcRefreshSession := &refreshtoken.Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Request: &fosite.Request{
|
||||
ID: "request-id-1",
|
||||
Client: &clientregistry.Client{},
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/fosite/compose"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@ -42,7 +43,7 @@ func TestStorage(t *testing.T) {
|
||||
Tracker() coretesting.ObjectTracker
|
||||
}
|
||||
|
||||
hmac := compose.NewOAuth2HMACStrategy(&compose.Config{}, []byte("super-secret-32-byte-for-testing"), nil)
|
||||
hmac := compose.NewOAuth2HMACStrategy(&fosite.Config{GlobalSecret: []byte("super-secret-32-byte-for-testing")})
|
||||
// test data generation via:
|
||||
// code, signature, err := hmac.GenerateAuthorizeCode(ctx, nil)
|
||||
|
||||
@ -117,7 +118,7 @@ func TestStorage(t *testing.T) {
|
||||
resource: "access-tokens",
|
||||
mocks: nil,
|
||||
run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
|
||||
signature := hmac.AuthorizeCodeSignature(authorizationCode1)
|
||||
signature := hmac.AuthorizeCodeSignature(context.Background(), authorizationCode1)
|
||||
require.NotEmpty(t, signature)
|
||||
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
|
||||
|
||||
@ -288,7 +289,7 @@ func TestStorage(t *testing.T) {
|
||||
})
|
||||
},
|
||||
run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
|
||||
signature := hmac.AuthorizeCodeSignature(authorizationCode1)
|
||||
signature := hmac.AuthorizeCodeSignature(context.Background(), authorizationCode1)
|
||||
require.NotEmpty(t, signature)
|
||||
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
|
||||
|
||||
@ -424,7 +425,7 @@ func TestStorage(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
},
|
||||
run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
|
||||
signature := hmac.AuthorizeCodeSignature(authorizationCode2)
|
||||
signature := hmac.AuthorizeCodeSignature(context.Background(), authorizationCode2)
|
||||
require.NotEmpty(t, signature)
|
||||
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
|
||||
|
||||
@ -493,7 +494,7 @@ func TestStorage(t *testing.T) {
|
||||
})
|
||||
},
|
||||
run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
|
||||
signature := hmac.AuthorizeCodeSignature(authorizationCode3)
|
||||
signature := hmac.AuthorizeCodeSignature(context.Background(), authorizationCode3)
|
||||
require.NotEmpty(t, signature)
|
||||
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
|
||||
|
||||
@ -593,7 +594,7 @@ func TestStorage(t *testing.T) {
|
||||
})
|
||||
},
|
||||
run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
|
||||
signature := hmac.AuthorizeCodeSignature(authorizationCode3)
|
||||
signature := hmac.AuthorizeCodeSignature(context.Background(), authorizationCode3)
|
||||
require.NotEmpty(t, signature)
|
||||
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
|
||||
|
||||
@ -672,7 +673,7 @@ func TestStorage(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
},
|
||||
run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
|
||||
signature := hmac.AuthorizeCodeSignature(authorizationCode2)
|
||||
signature := hmac.AuthorizeCodeSignature(context.Background(), authorizationCode2)
|
||||
require.NotEmpty(t, signature)
|
||||
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
|
||||
|
||||
@ -934,7 +935,7 @@ func TestStorage(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
},
|
||||
run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
|
||||
signature := hmac.AuthorizeCodeSignature(authorizationCode3)
|
||||
signature := hmac.AuthorizeCodeSignature(context.Background(), authorizationCode3)
|
||||
require.NotEmpty(t, signature)
|
||||
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
|
||||
|
||||
@ -998,7 +999,7 @@ func TestStorage(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
},
|
||||
run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
|
||||
signature := hmac.AuthorizeCodeSignature(authorizationCode3)
|
||||
signature := hmac.AuthorizeCodeSignature(context.Background(), authorizationCode3)
|
||||
require.NotEmpty(t, signature)
|
||||
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
|
||||
|
||||
@ -1062,7 +1063,7 @@ func TestStorage(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
},
|
||||
run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
|
||||
signature := hmac.AuthorizeCodeSignature(authorizationCode3)
|
||||
signature := hmac.AuthorizeCodeSignature(context.Background(), authorizationCode3)
|
||||
require.NotEmpty(t, signature)
|
||||
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
|
||||
|
||||
@ -1126,7 +1127,7 @@ func TestStorage(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
},
|
||||
run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
|
||||
signature := hmac.AuthorizeCodeSignature(authorizationCode3)
|
||||
signature := hmac.AuthorizeCodeSignature(context.Background(), authorizationCode3)
|
||||
require.NotEmpty(t, signature)
|
||||
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
|
||||
|
||||
@ -1170,7 +1171,7 @@ func TestStorage(t *testing.T) {
|
||||
mocks: nil,
|
||||
lifetime: func() time.Duration { return 0 }, // 0 == infinity
|
||||
run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
|
||||
signature := hmac.AuthorizeCodeSignature(authorizationCode1)
|
||||
signature := hmac.AuthorizeCodeSignature(context.Background(), authorizationCode1)
|
||||
require.NotEmpty(t, signature)
|
||||
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
|
||||
|
||||
@ -1232,7 +1233,7 @@ func TestStorage(t *testing.T) {
|
||||
mocks: nil,
|
||||
lifetime: func() time.Duration { return 0 }, // 0 == infinity
|
||||
run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
|
||||
signature := hmac.AuthorizeCodeSignature(authorizationCode1)
|
||||
signature := hmac.AuthorizeCodeSignature(context.Background(), authorizationCode1)
|
||||
require.NotEmpty(t, signature)
|
||||
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
|
||||
|
||||
|
@ -30,7 +30,8 @@ const (
|
||||
// Version 1 was the initial release of storage.
|
||||
// Version 2 is when we switched to storing psession.PinnipedSession inside the fosite request.
|
||||
// Version 3 is when we added the Username field to the psession.CustomSessionData.
|
||||
accessTokenStorageVersion = "3"
|
||||
// Version 4 is when fosite added json tags to their openid.DefaultSession struct.
|
||||
accessTokenStorageVersion = "4"
|
||||
)
|
||||
|
||||
type RevocationStorage interface {
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/fosite/handler/openid"
|
||||
"github.com/ory/fosite/token/jwt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@ -53,7 +54,7 @@ func TestAccessTokenStorage(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"3"}`),
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"4"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/access-token",
|
||||
@ -122,7 +123,7 @@ func TestAccessTokenStorageRevocation(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"3"}`),
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"4"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/access-token",
|
||||
@ -195,7 +196,7 @@ func TestWrongVersion(t *testing.T) {
|
||||
|
||||
_, err = storage.GetAccessTokenSession(ctx, "fancy-signature", nil)
|
||||
|
||||
require.EqualError(t, err, "access token request data has wrong version: access token session for fancy-signature has version not-the-right-version instead of 3")
|
||||
require.EqualError(t, err, "access token request data has wrong version: access token session for fancy-signature has version not-the-right-version instead of 4")
|
||||
}
|
||||
|
||||
func TestNilSessionRequest(t *testing.T) {
|
||||
@ -213,7 +214,7 @@ func TestNilSessionRequest(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value","version":"3"}`),
|
||||
"pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value","version":"4"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/access-token",
|
||||
@ -297,13 +298,13 @@ func TestReadFromSecret(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token"}}}},"version":"3","active": true}`),
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","session":{"fosite":{"id_token_claims":{"jti": "xyz"},"headers":{"extra":{"myheader": "foo"}},"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token"}}}},"version":"4","active": true}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/access-token",
|
||||
},
|
||||
wantSession: &Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Request: &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
Client: &clientregistry.Client{},
|
||||
@ -311,6 +312,8 @@ func TestReadFromSecret(t *testing.T) {
|
||||
Fosite: &openid.DefaultSession{
|
||||
Username: "snorlax",
|
||||
Subject: "panda",
|
||||
Claims: &jwt.IDTokenClaims{JTI: "xyz"},
|
||||
Headers: &jwt.Headers{Extra: map[string]interface{}{"myheader": "foo"}},
|
||||
},
|
||||
Custom: &psession.CustomSessionData{
|
||||
Username: "fake-username",
|
||||
@ -359,7 +362,7 @@ func TestReadFromSecret(t *testing.T) {
|
||||
},
|
||||
Type: "storage.pinniped.dev/access-token",
|
||||
},
|
||||
wantErr: "access token request data has wrong version: access token session has version wrong-version-here instead of 3",
|
||||
wantErr: "access token request data has wrong version: access token session has version wrong-version-here instead of 4",
|
||||
},
|
||||
{
|
||||
name: "missing request",
|
||||
@ -372,7 +375,7 @@ func TestReadFromSecret(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"version":"3","active": true}`),
|
||||
"pinniped-storage-data": []byte(`{"version":"4","active": true}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/access-token",
|
||||
|
@ -31,7 +31,8 @@ const (
|
||||
// Version 1 was the initial release of storage.
|
||||
// Version 2 is when we switched to storing psession.PinnipedSession inside the fosite request.
|
||||
// Version 3 is when we added the Username field to the psession.CustomSessionData.
|
||||
authorizeCodeStorageVersion = "3"
|
||||
// Version 4 is when fosite added json tags to their openid.DefaultSession struct.
|
||||
authorizeCodeStorageVersion = "4"
|
||||
)
|
||||
|
||||
var _ oauth2.AuthorizeCodeStorage = &authorizeCodeStorage{}
|
||||
@ -314,25 +315,25 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
|
||||
},
|
||||
"session": {
|
||||
"fosite": {
|
||||
"Claims": {
|
||||
"JTI": "褗6巽ēđų蓼tùZ蛆鬣a\"ÙǞ0觢",
|
||||
"Issuer": "j¦鲶H股ƲLŋZ-{",
|
||||
"Subject": "ehpƧ蓟",
|
||||
"Audience": [
|
||||
"id_token_claims": {
|
||||
"jti": "褗6巽ēđų蓼tùZ蛆鬣a\"ÙǞ0觢",
|
||||
"iss": "j¦鲶H股ƲLŋZ-{",
|
||||
"sub": "ehpƧ蓟",
|
||||
"aud": [
|
||||
"驜Ŗ~ů崧軒q腟u尿宲!"
|
||||
],
|
||||
"Nonce": "ǎ^嫯R忑隯ƗƋ*L\u0026",
|
||||
"ExpiresAt": "1989-06-02T14:40:29.613836765Z",
|
||||
"IssuedAt": "2052-03-26T02:39:27.882495556Z",
|
||||
"RequestedAt": "2038-04-06T10:46:24.698586972Z",
|
||||
"AuthTime": "2003-01-05T11:30:18.206004879Z",
|
||||
"AccessTokenHash": "ğǫ\\aȊ4ț髄Al",
|
||||
"AuthenticationContextClassReference": "曓蓳n匟鯘磹*金爃鶴滱ůĮǐ_c3#",
|
||||
"AuthenticationMethodsReferences": [
|
||||
"nonce": "ǎ^嫯R忑隯ƗƋ*L\u0026",
|
||||
"exp": "1989-06-02T14:40:29.613836765Z",
|
||||
"iat": "2052-03-26T02:39:27.882495556Z",
|
||||
"rat": "2038-04-06T10:46:24.698586972Z",
|
||||
"auth_time": "2003-01-05T11:30:18.206004879Z",
|
||||
"at_hash": "ğǫ\\aȊ4ț髄Al",
|
||||
"acr": "曓蓳n匟鯘磹*金爃鶴滱ůĮǐ_c3#",
|
||||
"amr": [
|
||||
"装ƹýĸŴB岺Ð嫹Sx镯荫őł疂ư墫"
|
||||
],
|
||||
"CodeHash": "\u0026鶡",
|
||||
"Extra": {
|
||||
"c_hash": "\u0026鶡",
|
||||
"ext": {
|
||||
"rǓ\\BRë_g\"ʎ啴SƇMǃļū": {
|
||||
"4撎胬龯,t猟i\u0026\u0026Q@ǤǟǗ": [
|
||||
1239190737
|
||||
@ -347,8 +348,8 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
|
||||
"鑳绪": 2738428764
|
||||
}
|
||||
},
|
||||
"Headers": {
|
||||
"Extra": {
|
||||
"headers": {
|
||||
"extra": {
|
||||
"d謺錳4帳ŅǃĊ": 663773398,
|
||||
"Ř鸨EJ": {
|
||||
"Ǽǟ迍阊v\"豑觳翢砜": [
|
||||
@ -363,11 +364,11 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"ExpiresAt": {
|
||||
"expires_at": {
|
||||
"韁臯氃妪婝rȤ\"h丬鎒ơ娻}ɼƟ": "1970-04-27T04:31:30.902468229Z"
|
||||
},
|
||||
"Username": "髉龳ǽÙ",
|
||||
"Subject": "\u0026¥潝邎Ȗ莅ŝǔ盕戙鵮碡ʯiŬŽ"
|
||||
"username": "髉龳ǽÙ",
|
||||
"subject": "\u0026¥潝邎Ȗ莅ŝǔ盕戙鵮碡ʯiŬŽ"
|
||||
},
|
||||
"custom": {
|
||||
"username": "Ĝ眧Ĭ",
|
||||
@ -408,5 +409,5 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
|
||||
"筫MN\u0026錝D肁Ŷɽ蔒PR}Ųʓl{"
|
||||
]
|
||||
},
|
||||
"version": "3"
|
||||
"version": "4"
|
||||
}`
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/fosite/handler/oauth2"
|
||||
"github.com/ory/fosite/handler/openid"
|
||||
"github.com/ory/fosite/token/jwt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
@ -65,7 +66,7 @@ func TestAuthorizationCodeStorage(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"active":true,"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"3"}`),
|
||||
"pinniped-storage-data": []byte(`{"active":true,"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"4"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/authcode",
|
||||
@ -85,7 +86,7 @@ func TestAuthorizationCodeStorage(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"active":false,"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"3"}`),
|
||||
"pinniped-storage-data": []byte(`{"active":false,"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"4"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/authcode",
|
||||
@ -203,7 +204,7 @@ func TestWrongVersion(t *testing.T) {
|
||||
|
||||
_, err = storage.GetAuthorizeCodeSession(ctx, "fancy-signature", nil)
|
||||
|
||||
require.EqualError(t, err, "authorization request data has wrong version: authorization code session for fancy-signature has version not-the-right-version instead of 3")
|
||||
require.EqualError(t, err, "authorization request data has wrong version: authorization code session for fancy-signature has version not-the-right-version instead of 4")
|
||||
}
|
||||
|
||||
func TestNilSessionRequest(t *testing.T) {
|
||||
@ -218,7 +219,7 @@ func TestNilSessionRequest(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value", "version":"3", "active": true}`),
|
||||
"pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value", "version":"4", "active": true}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/authcode",
|
||||
@ -385,7 +386,7 @@ func TestFuzzAndJSONNewValidEmptyAuthorizeCodeSession(t *testing.T) {
|
||||
|
||||
// set these to match CreateAuthorizeCodeSession so that .JSONEq works
|
||||
validSession.Active = true
|
||||
validSession.Version = "3"
|
||||
validSession.Version = "4"
|
||||
|
||||
validSessionJSONBytes, err := json.MarshalIndent(validSession, "", "\t")
|
||||
require.NoError(t, err)
|
||||
@ -420,13 +421,13 @@ func TestReadFromSecret(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token"}}}},"version":"3","active": true}`),
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","session":{"fosite":{"id_token_claims":{"jti": "xyz"},"headers":{"extra":{"myheader": "foo"}},"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token"}}}},"version":"4","active": true}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/authcode",
|
||||
},
|
||||
wantSession: &Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Active: true,
|
||||
Request: &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
@ -435,6 +436,8 @@ func TestReadFromSecret(t *testing.T) {
|
||||
Fosite: &openid.DefaultSession{
|
||||
Username: "snorlax",
|
||||
Subject: "panda",
|
||||
Claims: &jwt.IDTokenClaims{JTI: "xyz"},
|
||||
Headers: &jwt.Headers{Extra: map[string]interface{}{"myheader": "foo"}},
|
||||
},
|
||||
Custom: &psession.CustomSessionData{
|
||||
Username: "fake-username",
|
||||
@ -483,7 +486,7 @@ func TestReadFromSecret(t *testing.T) {
|
||||
},
|
||||
Type: "storage.pinniped.dev/authcode",
|
||||
},
|
||||
wantErr: "authorization request data has wrong version: authorization code session has version wrong-version-here instead of 3",
|
||||
wantErr: "authorization request data has wrong version: authorization code session has version wrong-version-here instead of 4",
|
||||
},
|
||||
{
|
||||
name: "missing request",
|
||||
@ -496,7 +499,7 @@ func TestReadFromSecret(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"version":"3","active": true}`),
|
||||
"pinniped-storage-data": []byte(`{"version":"4","active": true}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/authcode",
|
||||
|
@ -31,7 +31,8 @@ const (
|
||||
// Version 1 was the initial release of storage.
|
||||
// Version 2 is when we switched to storing psession.PinnipedSession inside the fosite request.
|
||||
// Version 3 is when we added the Username field to the psession.CustomSessionData.
|
||||
oidcStorageVersion = "3"
|
||||
// Version 4 is when fosite added json tags to their openid.DefaultSession struct.
|
||||
oidcStorageVersion = "4"
|
||||
)
|
||||
|
||||
var _ openid.OpenIDConnectRequestStorage = &openIDConnectRequestStorage{}
|
||||
|
@ -52,7 +52,7 @@ func TestOpenIdConnectStorage(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"3"}`),
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"4"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/oidc",
|
||||
@ -137,7 +137,7 @@ func TestWrongVersion(t *testing.T) {
|
||||
|
||||
_, err = storage.GetOpenIDConnectSession(ctx, "fancy-code.fancy-signature", nil)
|
||||
|
||||
require.EqualError(t, err, "oidc request data has wrong version: oidc session for fancy-signature has version not-the-right-version instead of 3")
|
||||
require.EqualError(t, err, "oidc request data has wrong version: oidc session for fancy-signature has version not-the-right-version instead of 4")
|
||||
}
|
||||
|
||||
func TestNilSessionRequest(t *testing.T) {
|
||||
@ -152,7 +152,7 @@ func TestNilSessionRequest(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value","version":"3"}`),
|
||||
"pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value","version":"4"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/oidc",
|
||||
|
@ -29,7 +29,8 @@ const (
|
||||
// Version 1 was the initial release of storage.
|
||||
// Version 2 is when we switched to storing psession.PinnipedSession inside the fosite request.
|
||||
// Version 3 is when we added the Username field to the psession.CustomSessionData.
|
||||
pkceStorageVersion = "3"
|
||||
// Version 4 is when fosite added json tags to their openid.DefaultSession struct.
|
||||
pkceStorageVersion = "4"
|
||||
)
|
||||
|
||||
var _ pkce.PKCERequestStorage = &pkceStorage{}
|
||||
|
@ -52,7 +52,7 @@ func TestPKCEStorage(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"3"}`),
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"4"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/pkce",
|
||||
@ -140,7 +140,7 @@ func TestWrongVersion(t *testing.T) {
|
||||
|
||||
_, err = storage.GetPKCERequestSession(ctx, "fancy-signature", nil)
|
||||
|
||||
require.EqualError(t, err, "pkce request data has wrong version: pkce session for fancy-signature has version not-the-right-version instead of 3")
|
||||
require.EqualError(t, err, "pkce request data has wrong version: pkce session for fancy-signature has version not-the-right-version instead of 4")
|
||||
}
|
||||
|
||||
func TestNilSessionRequest(t *testing.T) {
|
||||
@ -158,7 +158,7 @@ func TestNilSessionRequest(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value","version":"3"}`),
|
||||
"pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value","version":"4"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/pkce",
|
||||
|
@ -30,7 +30,8 @@ const (
|
||||
// Version 1 was the initial release of storage.
|
||||
// Version 2 is when we switched to storing psession.PinnipedSession inside the fosite request.
|
||||
// Version 3 is when we added the Username field to the psession.CustomSessionData.
|
||||
refreshTokenStorageVersion = "3"
|
||||
// Version 4 is when fosite added json tags to their openid.DefaultSession struct.
|
||||
refreshTokenStorageVersion = "4"
|
||||
)
|
||||
|
||||
type RevocationStorage interface {
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/fosite/handler/openid"
|
||||
"github.com/ory/fosite/token/jwt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@ -52,7 +53,7 @@ func TestRefreshTokenStorage(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"3"}`),
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"4"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/refresh-token",
|
||||
@ -122,7 +123,7 @@ func TestRefreshTokenStorageRevocation(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"3"}`),
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"4"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/refresh-token",
|
||||
@ -177,7 +178,7 @@ func TestRefreshTokenStorageRevokeRefreshTokenMaybeGracePeriod(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"3"}`),
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"4"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/refresh-token",
|
||||
@ -251,7 +252,7 @@ func TestWrongVersion(t *testing.T) {
|
||||
|
||||
_, err = storage.GetRefreshTokenSession(ctx, "fancy-signature", nil)
|
||||
|
||||
require.EqualError(t, err, "refresh token request data has wrong version: refresh token session for fancy-signature has version not-the-right-version instead of 3")
|
||||
require.EqualError(t, err, "refresh token request data has wrong version: refresh token session for fancy-signature has version not-the-right-version instead of 4")
|
||||
}
|
||||
|
||||
func TestNilSessionRequest(t *testing.T) {
|
||||
@ -269,7 +270,7 @@ func TestNilSessionRequest(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value","version":"3"}`),
|
||||
"pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value","version":"4"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/refresh-token",
|
||||
@ -353,13 +354,13 @@ func TestReadFromSecret(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token"}}}},"version":"3","active": true}`),
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","session":{"fosite":{"id_token_claims":{"jti": "xyz"},"headers":{"extra":{"myheader": "foo"}},"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token"}}}},"version":"4","active": true}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/refresh-token",
|
||||
},
|
||||
wantSession: &Session{
|
||||
Version: "3",
|
||||
Version: "4",
|
||||
Request: &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
Client: &clientregistry.Client{},
|
||||
@ -367,6 +368,8 @@ func TestReadFromSecret(t *testing.T) {
|
||||
Fosite: &openid.DefaultSession{
|
||||
Username: "snorlax",
|
||||
Subject: "panda",
|
||||
Claims: &jwt.IDTokenClaims{JTI: "xyz"},
|
||||
Headers: &jwt.Headers{Extra: map[string]interface{}{"myheader": "foo"}},
|
||||
},
|
||||
Custom: &psession.CustomSessionData{
|
||||
Username: "fake-username",
|
||||
@ -392,7 +395,7 @@ func TestReadFromSecret(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1"},"version":"3","active": true}`),
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1"},"version":"4","active": true}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/not-refresh-token",
|
||||
@ -415,7 +418,7 @@ func TestReadFromSecret(t *testing.T) {
|
||||
},
|
||||
Type: "storage.pinniped.dev/refresh-token",
|
||||
},
|
||||
wantErr: "refresh token request data has wrong version: refresh token session has version wrong-version-here instead of 3",
|
||||
wantErr: "refresh token request data has wrong version: refresh token session has version wrong-version-here instead of 4",
|
||||
},
|
||||
{
|
||||
name: "missing request",
|
||||
@ -428,7 +431,7 @@ func TestReadFromSecret(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"version":"3","active": true}`),
|
||||
"pinniped-storage-data": []byte(`{"version":"4","active": true}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/refresh-token",
|
||||
|
@ -125,7 +125,7 @@ func handleAuthRequestForLDAPUpstreamCLIFlow(
|
||||
return nil
|
||||
}
|
||||
|
||||
if !requireStaticClientForUsernameAndPasswordHeaders(w, oauthHelper, authorizeRequester) {
|
||||
if !requireStaticClientForUsernameAndPasswordHeaders(r, w, oauthHelper, authorizeRequester) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@ func handleAuthRequestForLDAPUpstreamCLIFlow(
|
||||
return httperr.New(http.StatusBadGateway, "unexpected error during upstream authentication")
|
||||
}
|
||||
if !authenticated {
|
||||
oidc.WriteAuthorizeError(w, oauthHelper, authorizeRequester,
|
||||
oidc.WriteAuthorizeError(r, w, oauthHelper, authorizeRequester,
|
||||
fosite.ErrAccessDenied.WithHintf("Username/password not accepted by LDAP provider."), true)
|
||||
return nil
|
||||
}
|
||||
@ -203,7 +203,7 @@ func handleAuthRequestForOIDCUpstreamPasswordGrant(
|
||||
return nil
|
||||
}
|
||||
|
||||
if !requireStaticClientForUsernameAndPasswordHeaders(w, oauthHelper, authorizeRequester) {
|
||||
if !requireStaticClientForUsernameAndPasswordHeaders(r, w, oauthHelper, authorizeRequester) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -214,7 +214,7 @@ func handleAuthRequestForOIDCUpstreamPasswordGrant(
|
||||
|
||||
if !oidcUpstream.AllowsPasswordGrant() {
|
||||
// Return a user-friendly error for this case which is entirely within our control.
|
||||
oidc.WriteAuthorizeError(w, oauthHelper, authorizeRequester,
|
||||
oidc.WriteAuthorizeError(r, w, oauthHelper, authorizeRequester,
|
||||
fosite.ErrAccessDenied.WithHint(
|
||||
"Resource owner password credentials grant is not allowed for this upstream provider according to its configuration."), true)
|
||||
return nil
|
||||
@ -229,7 +229,7 @@ func handleAuthRequestForOIDCUpstreamPasswordGrant(
|
||||
// However, the exact response is undefined in the sense that there is no such thing as a password grant in
|
||||
// the OIDC spec, so we don't try too hard to read the upstream errors in this case. (E.g. Dex departs from the
|
||||
// spec and returns something other than an "invalid_grant" error for bad resource owner credentials.)
|
||||
oidc.WriteAuthorizeError(w, oauthHelper, authorizeRequester,
|
||||
oidc.WriteAuthorizeError(r, w, oauthHelper, authorizeRequester,
|
||||
fosite.ErrAccessDenied.WithDebug(err.Error()), true) // WithDebug hides the error from the client
|
||||
return nil
|
||||
}
|
||||
@ -237,7 +237,7 @@ func handleAuthRequestForOIDCUpstreamPasswordGrant(
|
||||
subject, username, groups, err := downstreamsession.GetDownstreamIdentityFromUpstreamIDToken(oidcUpstream, token.IDToken.Claims)
|
||||
if err != nil {
|
||||
// Return a user-friendly error for this case which is entirely within our control.
|
||||
oidc.WriteAuthorizeError(w, oauthHelper, authorizeRequester,
|
||||
oidc.WriteAuthorizeError(r, w, oauthHelper, authorizeRequester,
|
||||
fosite.ErrAccessDenied.WithHintf("Reason: %s.", err.Error()), true,
|
||||
)
|
||||
return nil
|
||||
@ -245,7 +245,7 @@ func handleAuthRequestForOIDCUpstreamPasswordGrant(
|
||||
|
||||
customSessionData, err := downstreamsession.MakeDownstreamOIDCCustomSessionData(oidcUpstream, token, username)
|
||||
if err != nil {
|
||||
oidc.WriteAuthorizeError(w, oauthHelper, authorizeRequester,
|
||||
oidc.WriteAuthorizeError(r, w, oauthHelper, authorizeRequester,
|
||||
fosite.ErrAccessDenied.WithHintf("Reason: %s.", err.Error()), true,
|
||||
)
|
||||
return nil
|
||||
@ -321,10 +321,10 @@ func handleAuthRequestForOIDCUpstreamBrowserFlow(
|
||||
return nil
|
||||
}
|
||||
|
||||
func requireStaticClientForUsernameAndPasswordHeaders(w http.ResponseWriter, oauthHelper fosite.OAuth2Provider, authorizeRequester fosite.AuthorizeRequester) bool {
|
||||
func requireStaticClientForUsernameAndPasswordHeaders(r *http.Request, w http.ResponseWriter, oauthHelper fosite.OAuth2Provider, authorizeRequester fosite.AuthorizeRequester) bool {
|
||||
isStaticClient := authorizeRequester.GetClient().GetID() == oidcapi.ClientIDPinnipedCLI
|
||||
if !isStaticClient {
|
||||
oidc.WriteAuthorizeError(w, oauthHelper, authorizeRequester,
|
||||
oidc.WriteAuthorizeError(r, w, oauthHelper, authorizeRequester,
|
||||
fosite.ErrAccessDenied.WithHintf("This client is not allowed to submit username or password headers to this endpoint."), true)
|
||||
}
|
||||
return isStaticClient
|
||||
@ -334,7 +334,7 @@ func requireNonEmptyUsernameAndPasswordHeaders(r *http.Request, w http.ResponseW
|
||||
username := r.Header.Get(oidcapi.AuthorizeUsernameHeaderName)
|
||||
password := r.Header.Get(oidcapi.AuthorizePasswordHeaderName)
|
||||
if username == "" || password == "" {
|
||||
oidc.WriteAuthorizeError(w, oauthHelper, authorizeRequester,
|
||||
oidc.WriteAuthorizeError(r, w, oauthHelper, authorizeRequester,
|
||||
fosite.ErrAccessDenied.WithHintf("Missing or blank username or password."), true)
|
||||
return "", "", false
|
||||
}
|
||||
@ -344,7 +344,7 @@ func requireNonEmptyUsernameAndPasswordHeaders(r *http.Request, w http.ResponseW
|
||||
func newAuthorizeRequest(r *http.Request, w http.ResponseWriter, oauthHelper fosite.OAuth2Provider, isBrowserless bool) (fosite.AuthorizeRequester, bool) {
|
||||
authorizeRequester, err := oauthHelper.NewAuthorizeRequest(r.Context(), r)
|
||||
if err != nil {
|
||||
oidc.WriteAuthorizeError(w, oauthHelper, authorizeRequester, err, isBrowserless)
|
||||
oidc.WriteAuthorizeError(r, w, oauthHelper, authorizeRequester, err, isBrowserless)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
@ -458,7 +458,7 @@ func handleBrowserFlowAuthRequest(
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
oidc.WriteAuthorizeError(w, oauthHelper, authorizeRequester, err, false)
|
||||
oidc.WriteAuthorizeError(r, w, oauthHelper, authorizeRequester, err, false)
|
||||
return nil, nil // already wrote the error response, don't return error
|
||||
}
|
||||
|
||||
@ -488,7 +488,7 @@ func handleBrowserFlowAuthRequest(
|
||||
|
||||
promptParam := r.Form.Get(promptParamName)
|
||||
if promptParam == promptParamNone && oidc.ScopeWasRequested(authorizeRequester, oidcapi.ScopeOpenID) {
|
||||
oidc.WriteAuthorizeError(w, oauthHelper, authorizeRequester, fosite.ErrLoginRequired, false)
|
||||
oidc.WriteAuthorizeError(r, w, oauthHelper, authorizeRequester, fosite.ErrLoginRequired, false)
|
||||
return nil, nil // already wrote the error response, don't return error
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ func NewHandler(
|
||||
return httperr.Wrap(http.StatusInternalServerError, "error while generating and saving authcode", err)
|
||||
}
|
||||
|
||||
oauthHelper.WriteAuthorizeResponse(w, authorizeRequester, authorizeResponder)
|
||||
oauthHelper.WriteAuthorizeResponse(r.Context(), w, authorizeRequester, authorizeResponder)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
63
internal/oidc/dynamic_global_secret_config.go
Normal file
63
internal/oidc/dynamic_global_secret_config.go
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hash"
|
||||
"time"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/fosite/compose"
|
||||
)
|
||||
|
||||
// DynamicGlobalSecretConfig is a wrapper around fosite.Config which allows us to always return dynamic secrets,
|
||||
// since those secrets can change at any time when they are loaded or reloaded by our controllers.
|
||||
type DynamicGlobalSecretConfig struct {
|
||||
fositeConfig *fosite.Config
|
||||
keyFunc func() []byte
|
||||
}
|
||||
|
||||
var _ compose.HMACSHAStrategyConfigurator = &DynamicGlobalSecretConfig{}
|
||||
|
||||
func NewDynamicGlobalSecretConfig(
|
||||
fositeConfig *fosite.Config,
|
||||
keyFunc func() []byte,
|
||||
) *DynamicGlobalSecretConfig {
|
||||
return &DynamicGlobalSecretConfig{
|
||||
fositeConfig: fositeConfig,
|
||||
keyFunc: keyFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DynamicGlobalSecretConfig) GetAccessTokenLifespan(ctx context.Context) time.Duration {
|
||||
return d.fositeConfig.GetAccessTokenLifespan(ctx)
|
||||
}
|
||||
|
||||
func (d *DynamicGlobalSecretConfig) GetRefreshTokenLifespan(ctx context.Context) time.Duration {
|
||||
return d.fositeConfig.GetRefreshTokenLifespan(ctx)
|
||||
}
|
||||
|
||||
func (d *DynamicGlobalSecretConfig) GetAuthorizeCodeLifespan(ctx context.Context) time.Duration {
|
||||
return d.fositeConfig.GetAuthorizeCodeLifespan(ctx)
|
||||
}
|
||||
|
||||
func (d *DynamicGlobalSecretConfig) GetTokenEntropy(ctx context.Context) int {
|
||||
return d.fositeConfig.GetTokenEntropy(ctx)
|
||||
}
|
||||
|
||||
func (d *DynamicGlobalSecretConfig) GetHMACHasher(ctx context.Context) func() hash.Hash {
|
||||
return d.fositeConfig.GetHMACHasher(ctx)
|
||||
}
|
||||
|
||||
func (d *DynamicGlobalSecretConfig) GetGlobalSecret(ctx context.Context) ([]byte, error) {
|
||||
// Always call keyFunc() without ever caching its value, because that is the whole point
|
||||
// of this type. We want the global secret to be dynamic.
|
||||
return d.keyFunc(), nil
|
||||
}
|
||||
|
||||
func (d *DynamicGlobalSecretConfig) GetRotatedGlobalSecrets(ctx context.Context) ([][]byte, error) {
|
||||
// We don't support having multiple global secrets yet, but when we do we will need to implement this.
|
||||
return nil, nil
|
||||
}
|
@ -14,9 +14,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
accessTokenPrefix = "pin_at_" // "Pinniped access token" abbreviated.
|
||||
refreshTokenPrefix = "pin_rt_" // "Pinniped refresh token" abbreviated.
|
||||
authcodePrefix = "pin_ac_" // "Pinniped authorization code" abbreviated.
|
||||
pinAccessTokenPrefix = "pin_at_" // "Pinniped access token" abbreviated.
|
||||
oryAccessTokenPrefix = "ory_at_"
|
||||
|
||||
pinRefreshTokenPrefix = "pin_rt_" // "Pinniped refresh token" abbreviated.
|
||||
oryRefreshTokenPrefix = "ory_rt_"
|
||||
|
||||
pinAuthcodePrefix = "pin_ac_" // "Pinniped authorization code" abbreviated.
|
||||
oryAuthcodePrefix = "ory_ac_"
|
||||
)
|
||||
|
||||
// dynamicOauth2HMACStrategy is an oauth2.CoreStrategy that can dynamically load an HMAC key to sign
|
||||
@ -29,16 +34,18 @@ const (
|
||||
// FederationDomain has a valid signing key.
|
||||
//
|
||||
// Tokens start with a custom prefix to make them identifiable as tokens when seen by a user
|
||||
// out of context, such as when accidentally committed to a GitHub repo.
|
||||
// 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 {
|
||||
fositeConfig *compose.Config
|
||||
fositeConfig *fosite.Config
|
||||
keyFunc func() []byte
|
||||
}
|
||||
|
||||
var _ oauth2.CoreStrategy = &dynamicOauth2HMACStrategy{}
|
||||
|
||||
func newDynamicOauth2HMACStrategy(
|
||||
fositeConfig *compose.Config,
|
||||
fositeConfig *fosite.Config,
|
||||
keyFunc func() []byte,
|
||||
) *dynamicOauth2HMACStrategy {
|
||||
return &dynamicOauth2HMACStrategy{
|
||||
@ -47,17 +54,26 @@ func newDynamicOauth2HMACStrategy(
|
||||
}
|
||||
}
|
||||
|
||||
func (s *dynamicOauth2HMACStrategy) AccessTokenSignature(token string) string {
|
||||
return s.delegate().AccessTokenSignature(token)
|
||||
func replacePrefix(s, prefixToReplace, newPrefix string) string {
|
||||
return newPrefix + strings.TrimPrefix(s, prefixToReplace)
|
||||
}
|
||||
|
||||
func (s *dynamicOauth2HMACStrategy) AccessTokenSignature(ctx context.Context, token string) string {
|
||||
return s.delegate().AccessTokenSignature(ctx, token)
|
||||
}
|
||||
|
||||
func (s *dynamicOauth2HMACStrategy) GenerateAccessToken(
|
||||
ctx context.Context,
|
||||
requester fosite.Requester,
|
||||
) (token string, signature string, err error) {
|
||||
) (string, string, error) {
|
||||
token, sig, err := s.delegate().GenerateAccessToken(ctx, requester)
|
||||
if err == nil {
|
||||
token = accessTokenPrefix + token
|
||||
if !strings.HasPrefix(token, oryAccessTokenPrefix) {
|
||||
// This would only happen if fosite changed how it generates tokens. Defensive programming here.
|
||||
return "", "", errorsx.WithStack(fosite.ErrInvalidTokenFormat.
|
||||
WithDebugf("Generated token does not have expected prefix"))
|
||||
}
|
||||
token = replacePrefix(token, oryAccessTokenPrefix, pinAccessTokenPrefix)
|
||||
}
|
||||
return token, sig, err
|
||||
}
|
||||
@ -66,25 +82,30 @@ func (s *dynamicOauth2HMACStrategy) ValidateAccessToken(
|
||||
ctx context.Context,
|
||||
requester fosite.Requester,
|
||||
token string,
|
||||
) (err error) {
|
||||
if !strings.HasPrefix(token, accessTokenPrefix) {
|
||||
) error {
|
||||
if !strings.HasPrefix(token, pinAccessTokenPrefix) {
|
||||
return errorsx.WithStack(fosite.ErrInvalidTokenFormat.
|
||||
WithDebugf("Access token did not have prefix %q", accessTokenPrefix))
|
||||
WithDebugf("Access token did not have prefix %q", pinAccessTokenPrefix))
|
||||
}
|
||||
return s.delegate().ValidateAccessToken(ctx, requester, token[len(accessTokenPrefix):])
|
||||
return s.delegate().ValidateAccessToken(ctx, requester, replacePrefix(token, pinAccessTokenPrefix, oryAccessTokenPrefix))
|
||||
}
|
||||
|
||||
func (s *dynamicOauth2HMACStrategy) RefreshTokenSignature(token string) string {
|
||||
return s.delegate().RefreshTokenSignature(token)
|
||||
func (s *dynamicOauth2HMACStrategy) RefreshTokenSignature(ctx context.Context, token string) string {
|
||||
return s.delegate().RefreshTokenSignature(ctx, token)
|
||||
}
|
||||
|
||||
func (s *dynamicOauth2HMACStrategy) GenerateRefreshToken(
|
||||
ctx context.Context,
|
||||
requester fosite.Requester,
|
||||
) (token string, signature string, err error) {
|
||||
) (string, string, error) {
|
||||
token, sig, err := s.delegate().GenerateRefreshToken(ctx, requester)
|
||||
if err == nil {
|
||||
token = refreshTokenPrefix + token
|
||||
if !strings.HasPrefix(token, oryRefreshTokenPrefix) {
|
||||
// This would only happen if fosite changed how it generates tokens. Defensive programming here.
|
||||
return "", "", errorsx.WithStack(fosite.ErrInvalidTokenFormat.
|
||||
WithDebugf("Generated token does not have expected prefix"))
|
||||
}
|
||||
token = replacePrefix(token, oryRefreshTokenPrefix, pinRefreshTokenPrefix)
|
||||
}
|
||||
return token, sig, err
|
||||
}
|
||||
@ -93,25 +114,30 @@ func (s *dynamicOauth2HMACStrategy) ValidateRefreshToken(
|
||||
ctx context.Context,
|
||||
requester fosite.Requester,
|
||||
token string,
|
||||
) (err error) {
|
||||
if !strings.HasPrefix(token, refreshTokenPrefix) {
|
||||
) error {
|
||||
if !strings.HasPrefix(token, pinRefreshTokenPrefix) {
|
||||
return errorsx.WithStack(fosite.ErrInvalidTokenFormat.
|
||||
WithDebugf("Refresh token did not have prefix %q", refreshTokenPrefix))
|
||||
WithDebugf("Refresh token did not have prefix %q", pinRefreshTokenPrefix))
|
||||
}
|
||||
return s.delegate().ValidateRefreshToken(ctx, requester, token[len(refreshTokenPrefix):])
|
||||
return s.delegate().ValidateRefreshToken(ctx, requester, replacePrefix(token, pinRefreshTokenPrefix, oryRefreshTokenPrefix))
|
||||
}
|
||||
|
||||
func (s *dynamicOauth2HMACStrategy) AuthorizeCodeSignature(token string) string {
|
||||
return s.delegate().AuthorizeCodeSignature(token)
|
||||
func (s *dynamicOauth2HMACStrategy) AuthorizeCodeSignature(ctx context.Context, token string) string {
|
||||
return s.delegate().AuthorizeCodeSignature(ctx, token)
|
||||
}
|
||||
|
||||
func (s *dynamicOauth2HMACStrategy) GenerateAuthorizeCode(
|
||||
ctx context.Context,
|
||||
requester fosite.Requester,
|
||||
) (token string, signature string, err error) {
|
||||
) (string, string, error) {
|
||||
authcode, sig, err := s.delegate().GenerateAuthorizeCode(ctx, requester)
|
||||
if err == nil {
|
||||
authcode = authcodePrefix + authcode
|
||||
if !strings.HasPrefix(authcode, oryAuthcodePrefix) {
|
||||
// This would only happen if fosite changed how it generates tokens. Defensive programming here.
|
||||
return "", "", errorsx.WithStack(fosite.ErrInvalidTokenFormat.
|
||||
WithDebugf("Generated token does not have expected prefix"))
|
||||
}
|
||||
authcode = replacePrefix(authcode, oryAuthcodePrefix, pinAuthcodePrefix)
|
||||
}
|
||||
return authcode, sig, err
|
||||
}
|
||||
@ -120,14 +146,14 @@ func (s *dynamicOauth2HMACStrategy) ValidateAuthorizeCode(
|
||||
ctx context.Context,
|
||||
requester fosite.Requester,
|
||||
token string,
|
||||
) (err error) {
|
||||
if !strings.HasPrefix(token, authcodePrefix) {
|
||||
) error {
|
||||
if !strings.HasPrefix(token, pinAuthcodePrefix) {
|
||||
return errorsx.WithStack(fosite.ErrInvalidTokenFormat.
|
||||
WithDebugf("Authorization code did not have prefix %q", authcodePrefix))
|
||||
WithDebugf("Authorization code did not have prefix %q", pinAuthcodePrefix))
|
||||
}
|
||||
return s.delegate().ValidateAuthorizeCode(ctx, requester, token[len(authcodePrefix):])
|
||||
return s.delegate().ValidateAuthorizeCode(ctx, requester, replacePrefix(token, pinAuthcodePrefix, oryAuthcodePrefix))
|
||||
}
|
||||
|
||||
func (s *dynamicOauth2HMACStrategy) delegate() *oauth2.HMACSHAStrategy {
|
||||
return compose.NewOAuth2HMACStrategy(s.fositeConfig, s.keyFunc(), nil)
|
||||
return compose.NewOAuth2HMACStrategy(NewDynamicGlobalSecretConfig(s.fositeConfig, s.keyFunc))
|
||||
}
|
||||
|
@ -11,20 +11,19 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/fosite/compose"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDynamicOauth2HMACStrategy_Signatures(t *testing.T) {
|
||||
s := &dynamicOauth2HMACStrategy{
|
||||
fositeConfig: &compose.Config{}, // defaults are good enough for this unit test
|
||||
keyFunc: func() []byte { return []byte("12345678901234567890123456789012") },
|
||||
}
|
||||
s := newDynamicOauth2HMACStrategy(
|
||||
&fosite.Config{}, // defaults are good enough for this unit test
|
||||
func() []byte { return []byte("12345678901234567890123456789012") }, // 32 character secret key
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
token string
|
||||
signatureFunc func(token string) (signature string)
|
||||
signatureFunc func(ctx context.Context, token string) (signature string)
|
||||
wantSignature string
|
||||
}{
|
||||
{
|
||||
@ -52,21 +51,21 @@ func TestDynamicOauth2HMACStrategy_Signatures(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.Equal(t, tt.wantSignature, tt.signatureFunc(tt.token))
|
||||
require.Equal(t, tt.wantSignature, tt.signatureFunc(context.Background(), tt.token))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDynamicOauth2HMACStrategy_Generate(t *testing.T) {
|
||||
s := &dynamicOauth2HMACStrategy{
|
||||
fositeConfig: &compose.Config{}, // defaults are good enough for this unit test
|
||||
keyFunc: func() []byte { return []byte("12345678901234567890123456789012") }, // 32 character secret key
|
||||
}
|
||||
s := newDynamicOauth2HMACStrategy(
|
||||
&fosite.Config{}, // defaults are good enough for this unit test
|
||||
func() []byte { return []byte("12345678901234567890123456789012") }, // 32 character secret key
|
||||
)
|
||||
|
||||
generateTokenErrorCausingStrategy := &dynamicOauth2HMACStrategy{
|
||||
fositeConfig: &compose.Config{},
|
||||
keyFunc: func() []byte { return []byte("too_short_causes_error") }, // secret key is below required 32 characters
|
||||
}
|
||||
generateTokenErrorCausingStrategy := newDynamicOauth2HMACStrategy(
|
||||
&fosite.Config{},
|
||||
func() []byte { return []byte("too_short_causes_error") }, // secret key is below required 32 characters
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -135,10 +134,10 @@ func TestDynamicOauth2HMACStrategy_Generate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDynamicOauth2HMACStrategy_Validate(t *testing.T) {
|
||||
s := &dynamicOauth2HMACStrategy{
|
||||
fositeConfig: &compose.Config{}, // defaults are good enough for this unit test
|
||||
keyFunc: func() []byte { return []byte("12345678901234567890123456789012") },
|
||||
}
|
||||
s := newDynamicOauth2HMACStrategy(
|
||||
&fosite.Config{}, // defaults are good enough for this unit test
|
||||
func() []byte { return []byte("12345678901234567890123456789012") }, // 32 character secret key
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package oidc
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/fosite/compose"
|
||||
@ -26,14 +27,14 @@ import (
|
||||
// could have an invariant that routes to an FederationDomain's endpoints are only wired up if an
|
||||
// FederationDomain has a valid signing key.
|
||||
type dynamicOpenIDConnectECDSAStrategy struct {
|
||||
fositeConfig *compose.Config
|
||||
fositeConfig *fosite.Config
|
||||
jwksProvider jwks.DynamicJWKSProvider
|
||||
}
|
||||
|
||||
var _ openid.OpenIDConnectTokenStrategy = &dynamicOpenIDConnectECDSAStrategy{}
|
||||
|
||||
func newDynamicOpenIDConnectECDSAStrategy(
|
||||
fositeConfig *compose.Config,
|
||||
fositeConfig *fosite.Config,
|
||||
jwksProvider jwks.DynamicJWKSProvider,
|
||||
) *dynamicOpenIDConnectECDSAStrategy {
|
||||
return &dynamicOpenIDConnectECDSAStrategy{
|
||||
@ -44,6 +45,7 @@ func newDynamicOpenIDConnectECDSAStrategy(
|
||||
|
||||
func (s *dynamicOpenIDConnectECDSAStrategy) GenerateIDToken(
|
||||
ctx context.Context,
|
||||
lifespan time.Duration,
|
||||
requester fosite.Requester,
|
||||
) (string, error) {
|
||||
_, activeJwk := s.jwksProvider.GetJWKS(s.fositeConfig.IDTokenIssuer)
|
||||
@ -67,5 +69,10 @@ func (s *dynamicOpenIDConnectECDSAStrategy) GenerateIDToken(
|
||||
return "", fosite.ErrServerError.WithWrap(constable.Error("JWK must be of type ecdsa"))
|
||||
}
|
||||
|
||||
return compose.NewOpenIDConnectECDSAStrategy(s.fositeConfig, key).GenerateIDToken(ctx, requester)
|
||||
keyGetter := func(context.Context) (interface{}, error) {
|
||||
return key, nil
|
||||
}
|
||||
strategy := compose.NewOpenIDConnectStrategy(keyGetter, s.fositeConfig)
|
||||
|
||||
return strategy.GenerateIDToken(ctx, lifespan, requester)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package oidc
|
||||
@ -12,9 +12,9 @@ import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/fosite/compose"
|
||||
"github.com/ory/fosite/handler/openid"
|
||||
"github.com/ory/fosite/token/jwt"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -95,7 +95,7 @@ func TestDynamicOpenIDConnectECDSAStrategy(t *testing.T) {
|
||||
test.jwksProvider(jwksProvider)
|
||||
}
|
||||
s := newDynamicOpenIDConnectECDSAStrategy(
|
||||
&compose.Config{IDTokenIssuer: test.issuer},
|
||||
&fosite.Config{IDTokenIssuer: test.issuer},
|
||||
jwksProvider,
|
||||
)
|
||||
|
||||
@ -114,7 +114,7 @@ func TestDynamicOpenIDConnectECDSAStrategy(t *testing.T) {
|
||||
"nonce": {goodNonce},
|
||||
},
|
||||
}
|
||||
idToken, err := s.GenerateIDToken(context.Background(), requester)
|
||||
idToken, err := s.GenerateIDToken(context.Background(), 2*time.Hour, requester)
|
||||
if test.wantErrorType != nil {
|
||||
require.True(t, errors.Is(err, test.wantErrorType))
|
||||
require.EqualError(t, err.(*fosite.RFC6749Error).Cause(), test.wantErrorCause)
|
||||
|
@ -5,10 +5,12 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/felixge/httpsnoop"
|
||||
@ -195,7 +197,11 @@ func FositeOauth2Helper(
|
||||
jwksProvider jwks.DynamicJWKSProvider,
|
||||
timeoutsConfiguration TimeoutsConfiguration,
|
||||
) fosite.OAuth2Provider {
|
||||
oauthConfig := &compose.Config{
|
||||
isRedirectURISecureStrict := func(_ context.Context, uri *url.URL) bool {
|
||||
return fosite.IsRedirectURISecureStrict(uri)
|
||||
}
|
||||
|
||||
oauthConfig := &fosite.Config{
|
||||
IDTokenIssuer: issuer,
|
||||
|
||||
AuthorizeCodeLifespan: timeoutsConfiguration.AuthorizeCodeLifespan,
|
||||
@ -217,10 +223,16 @@ func FositeOauth2Helper(
|
||||
MinParameterEntropy: fosite.MinParameterEntropy,
|
||||
|
||||
// do not allow custom scheme redirects, only https and http (on loopback)
|
||||
RedirectSecureChecker: fosite.IsRedirectURISecureStrict,
|
||||
RedirectSecureChecker: isRedirectURISecureStrict,
|
||||
|
||||
// html template for rendering the authorization response when the request has response_mode=form_post
|
||||
FormPostHTMLTemplate: formposthtml.Template(),
|
||||
|
||||
// defaults to using BCrypt when nil
|
||||
ClientSecretsHasher: nil,
|
||||
}
|
||||
|
||||
provider := compose.Compose(
|
||||
oAuth2Provider := compose.Compose(
|
||||
oauthConfig,
|
||||
oauthStore,
|
||||
&compose.CommonStrategy{
|
||||
@ -228,7 +240,6 @@ func FositeOauth2Helper(
|
||||
CoreStrategy: newDynamicOauth2HMACStrategy(oauthConfig, hmacSecretOfLengthAtLeast32Func),
|
||||
OpenIDConnectTokenStrategy: newDynamicOpenIDConnectECDSAStrategy(oauthConfig, jwksProvider),
|
||||
},
|
||||
nil, // hasher, defaults to using BCrypt when nil. Used for hashing client secrets.
|
||||
compose.OAuth2AuthorizeExplicitFactory,
|
||||
compose.OAuth2RefreshTokenGrantFactory,
|
||||
compose.OpenIDConnectExplicitFactory,
|
||||
@ -236,8 +247,8 @@ func FositeOauth2Helper(
|
||||
compose.OAuth2PKCEFactory,
|
||||
TokenExchangeFactory, // handle the "urn:ietf:params:oauth:grant-type:token-exchange" grant type
|
||||
)
|
||||
provider.(*fosite.Fosite).FormPostHTMLTemplate = formposthtml.Template()
|
||||
return provider
|
||||
|
||||
return oAuth2Provider
|
||||
}
|
||||
|
||||
// FositeErrorForLog generates a list of information about the provided Fosite error that can be
|
||||
@ -403,7 +414,7 @@ func FindUpstreamIDPByNameAndType(
|
||||
|
||||
// WriteAuthorizeError writes an authorization error as it should be returned by the authorization endpoint and other
|
||||
// similar endpoints that are the end of the downstream authcode flow. Errors responses are written in the usual fosite style.
|
||||
func WriteAuthorizeError(w http.ResponseWriter, oauthHelper fosite.OAuth2Provider, authorizeRequester fosite.AuthorizeRequester, err error, isBrowserless bool) {
|
||||
func WriteAuthorizeError(r *http.Request, w http.ResponseWriter, oauthHelper fosite.OAuth2Provider, authorizeRequester fosite.AuthorizeRequester, err error, isBrowserless bool) {
|
||||
if plog.Enabled(plog.LevelTrace) {
|
||||
// When trace level logging is enabled, include the stack trace in the log message.
|
||||
keysAndValues := FositeErrorForLog(err)
|
||||
@ -420,7 +431,7 @@ func WriteAuthorizeError(w http.ResponseWriter, oauthHelper fosite.OAuth2Provide
|
||||
w = rewriteStatusSeeOtherToStatusFoundForBrowserless(w)
|
||||
}
|
||||
// Return an error according to OIDC spec 3.1.2.6 (second paragraph).
|
||||
oauthHelper.WriteAuthorizeError(w, authorizeRequester, err)
|
||||
oauthHelper.WriteAuthorizeError(r.Context(), w, authorizeRequester, err)
|
||||
}
|
||||
|
||||
// PerformAuthcodeRedirect successfully completes a downstream login by creating a session and
|
||||
@ -437,13 +448,13 @@ func PerformAuthcodeRedirect(
|
||||
authorizeResponder, err := oauthHelper.NewAuthorizeResponse(r.Context(), authorizeRequester, openIDSession)
|
||||
if err != nil {
|
||||
plog.WarningErr("error while generating and saving authcode", err, "fositeErr", FositeErrorForLog(err))
|
||||
WriteAuthorizeError(w, oauthHelper, authorizeRequester, err, isBrowserless)
|
||||
WriteAuthorizeError(r, w, oauthHelper, authorizeRequester, err, isBrowserless)
|
||||
return
|
||||
}
|
||||
if isBrowserless {
|
||||
w = rewriteStatusSeeOtherToStatusFoundForBrowserless(w)
|
||||
}
|
||||
oauthHelper.WriteAuthorizeResponse(w, authorizeRequester, authorizeResponder)
|
||||
oauthHelper.WriteAuthorizeResponse(r.Context(), w, authorizeRequester, authorizeResponder)
|
||||
}
|
||||
|
||||
func rewriteStatusSeeOtherToStatusFoundForBrowserless(w http.ResponseWriter) http.ResponseWriter {
|
||||
|
@ -35,7 +35,7 @@ func NewHandler(
|
||||
accessRequest, err := oauthHelper.NewAccessRequest(r.Context(), r, session)
|
||||
if err != nil {
|
||||
plog.Info("token request error", oidc.FositeErrorForLog(err)...)
|
||||
oauthHelper.WriteAccessError(w, accessRequest, err)
|
||||
oauthHelper.WriteAccessError(r.Context(), w, accessRequest, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@ func NewHandler(
|
||||
err = upstreamRefresh(r.Context(), accessRequest, idpLister)
|
||||
if err != nil {
|
||||
plog.Info("upstream refresh error", oidc.FositeErrorForLog(err)...)
|
||||
oauthHelper.WriteAccessError(w, accessRequest, err)
|
||||
oauthHelper.WriteAccessError(r.Context(), w, accessRequest, err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -68,11 +68,11 @@ func NewHandler(
|
||||
accessResponse, err := oauthHelper.NewAccessResponse(r.Context(), accessRequest)
|
||||
if err != nil {
|
||||
plog.Info("token response error", oidc.FositeErrorForLog(err)...)
|
||||
oauthHelper.WriteAccessError(w, accessRequest, err)
|
||||
oauthHelper.WriteAccessError(r.Context(), w, accessRequest, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
oauthHelper.WriteAccessResponse(w, accessRequest, accessResponse)
|
||||
oauthHelper.WriteAccessResponse(r.Context(), w, accessRequest, accessResponse)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
@ -3735,8 +3735,6 @@ func TestRefreshGrant(t *testing.T) {
|
||||
require.Len(t, reqContextWarningRecorder.Warnings, 0, "wanted no warnings on the request context, but found some")
|
||||
}
|
||||
|
||||
// The bug in fosite that prevents at_hash from appearing in the initial ID token does not impact the refreshed ID token
|
||||
wantAtHashClaimInIDToken := true
|
||||
// Refreshed ID tokens do not include the nonce from the original auth request
|
||||
wantNonceValueInIDToken := false
|
||||
|
||||
@ -3745,7 +3743,6 @@ func TestRefreshGrant(t *testing.T) {
|
||||
test.authcodeExchange.want.wantUsername, // the old username from the initial login
|
||||
test.authcodeExchange.want.wantGroups, // the old groups from the initial login
|
||||
test.authcodeExchange.customSessionData, // the old custom session data from the initial login
|
||||
wantAtHashClaimInIDToken,
|
||||
wantNonceValueInIDToken,
|
||||
refreshResponse,
|
||||
authCode,
|
||||
@ -3784,8 +3781,9 @@ func TestRefreshGrant(t *testing.T) {
|
||||
err = secondIDTokenDecoded.UnsafeClaimsWithoutVerification(&claimsOfSecondIDToken)
|
||||
require.NoError(t, err)
|
||||
|
||||
requireClaimsAreNotEqual(t, "jti", claimsOfFirstIDToken, claimsOfSecondIDToken) // JWT ID
|
||||
requireClaimsAreNotEqual(t, "exp", claimsOfFirstIDToken, claimsOfSecondIDToken) // expires at
|
||||
requireClaimsAreNotEqual(t, "jti", claimsOfFirstIDToken, claimsOfSecondIDToken) // JWT ID
|
||||
requireClaimsAreNotEqual(t, "at_hash", claimsOfFirstIDToken, claimsOfSecondIDToken) // access token hash
|
||||
requireClaimsAreNotEqual(t, "exp", claimsOfFirstIDToken, claimsOfSecondIDToken) // expires at
|
||||
require.Greater(t, claimsOfSecondIDToken["exp"], claimsOfFirstIDToken["exp"])
|
||||
requireClaimsAreNotEqual(t, "iat", claimsOfFirstIDToken, claimsOfSecondIDToken) // issued at
|
||||
require.Greater(t, claimsOfSecondIDToken["iat"], claimsOfFirstIDToken["iat"])
|
||||
@ -3880,15 +3878,13 @@ func exchangeAuthcodeForTokens(
|
||||
t.Logf("response: %#v", rsp)
|
||||
t.Logf("response body: %q", rsp.Body.String())
|
||||
|
||||
wantAtHashClaimInIDToken := false // due to a bug in fosite, the at_hash claim is not filled in during authcode exchange
|
||||
wantNonceValueInIDToken := true // ID tokens returned by the authcode exchange must include the nonce from the auth request (unlike refreshed ID tokens)
|
||||
wantNonceValueInIDToken := true // ID tokens returned by the authcode exchange must include the nonce from the auth request (unlike refreshed ID tokens)
|
||||
|
||||
requireTokenEndpointBehavior(t,
|
||||
test.want,
|
||||
test.want.wantUsername, // the old username from the initial login
|
||||
test.want.wantGroups, // the old groups from the initial login
|
||||
test.customSessionData, // the old custom session data from the initial login
|
||||
wantAtHashClaimInIDToken,
|
||||
wantNonceValueInIDToken,
|
||||
rsp,
|
||||
authCode,
|
||||
@ -3907,7 +3903,6 @@ func requireTokenEndpointBehavior(
|
||||
oldUsername string,
|
||||
oldGroups []string,
|
||||
oldCustomSessionData *psession.CustomSessionData,
|
||||
wantAtHashClaimInIDToken bool,
|
||||
wantNonceValueInIDToken bool,
|
||||
tokenEndpointResponse *httptest.ResponseRecorder,
|
||||
authCode string,
|
||||
@ -3942,7 +3937,7 @@ func requireTokenEndpointBehavior(
|
||||
expectedNumberOfIDSessionsStored := 0
|
||||
if wantIDToken {
|
||||
expectedNumberOfIDSessionsStored = 1
|
||||
requireValidIDToken(t, parsedResponseBody, jwtSigningKey, test.wantClientID, wantAtHashClaimInIDToken, wantNonceValueInIDToken, test.wantUsername, test.wantGroups, parsedResponseBody["access_token"].(string), requestTime)
|
||||
requireValidIDToken(t, parsedResponseBody, jwtSigningKey, test.wantClientID, wantNonceValueInIDToken, test.wantUsername, test.wantGroups, parsedResponseBody["access_token"].(string), requestTime)
|
||||
}
|
||||
if wantRefreshToken {
|
||||
requireValidRefreshTokenStorage(t, parsedResponseBody, oauthStore, test.wantClientID, test.wantRequestedScopes, test.wantGrantedScopes, test.wantUsername, test.wantGroups, test.wantCustomSessionDataStored, secrets, requestTime)
|
||||
@ -4509,7 +4504,6 @@ func requireValidIDToken(
|
||||
body map[string]interface{},
|
||||
jwtSigningKey *ecdsa.PrivateKey,
|
||||
wantClientID string,
|
||||
wantAtHashClaimInIDToken bool,
|
||||
wantNonceValueInIDToken bool,
|
||||
wantUsernameInIDToken string,
|
||||
wantGroupsInIDToken []string,
|
||||
@ -4541,13 +4535,7 @@ func requireValidIDToken(
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
// Note that there is a bug in fosite which prevents the `at_hash` claim from appearing in this ID token
|
||||
// during the initial authcode exchange, but does not prevent `at_hash` from appearing in the refreshed ID token.
|
||||
// We can add a workaround for this later.
|
||||
idTokenFields := []string{"sub", "aud", "iss", "jti", "auth_time", "exp", "iat", "rat", "azp"}
|
||||
if wantAtHashClaimInIDToken {
|
||||
idTokenFields = append(idTokenFields, "at_hash")
|
||||
}
|
||||
idTokenFields := []string{"sub", "aud", "iss", "jti", "auth_time", "exp", "iat", "rat", "azp", "at_hash"}
|
||||
if wantNonceValueInIDToken {
|
||||
idTokenFields = append(idTokenFields, "nonce")
|
||||
}
|
||||
@ -4590,12 +4578,8 @@ func requireValidIDToken(
|
||||
testutil.RequireTimeInDelta(t, goodRequestedAtTime, requestedAt, timeComparisonFudge)
|
||||
testutil.RequireTimeInDelta(t, goodAuthTime, authTime, timeComparisonFudge)
|
||||
|
||||
if wantAtHashClaimInIDToken {
|
||||
require.NotEmpty(t, actualAccessToken)
|
||||
require.Equal(t, hashAccessToken(actualAccessToken), claims.AccessTokenHash)
|
||||
} else {
|
||||
require.Empty(t, claims.AccessTokenHash)
|
||||
}
|
||||
require.NotEmpty(t, actualAccessToken)
|
||||
require.Equal(t, hashAccessToken(actualAccessToken), claims.AccessTokenHash)
|
||||
}
|
||||
|
||||
func deepCopyRequestForm(r *http.Request) *http.Request {
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/fosite/compose"
|
||||
"github.com/ory/fosite/handler/oauth2"
|
||||
"github.com/ory/fosite/handler/openid"
|
||||
"github.com/pkg/errors"
|
||||
@ -28,11 +27,12 @@ type stsParams struct {
|
||||
requestedAudience string
|
||||
}
|
||||
|
||||
func TokenExchangeFactory(config *compose.Config, storage interface{}, strategy interface{}) interface{} {
|
||||
func TokenExchangeFactory(config fosite.Configurator, storage interface{}, strategy interface{}) interface{} {
|
||||
return &TokenExchangeHandler{
|
||||
idTokenStrategy: strategy.(openid.OpenIDConnectTokenStrategy),
|
||||
accessTokenStrategy: strategy.(oauth2.AccessTokenStrategy),
|
||||
accessTokenStorage: storage.(oauth2.AccessTokenStorage),
|
||||
fositeConfig: config,
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,12 +40,13 @@ type TokenExchangeHandler struct {
|
||||
idTokenStrategy openid.OpenIDConnectTokenStrategy
|
||||
accessTokenStrategy oauth2.AccessTokenStrategy
|
||||
accessTokenStorage oauth2.AccessTokenStorage
|
||||
fositeConfig fosite.Configurator
|
||||
}
|
||||
|
||||
var _ fosite.TokenEndpointHandler = (*TokenExchangeHandler)(nil)
|
||||
|
||||
func (t *TokenExchangeHandler) HandleTokenEndpointRequest(ctx context.Context, requester fosite.AccessRequester) error {
|
||||
if !t.CanHandleTokenEndpointRequest(requester) {
|
||||
if !t.CanHandleTokenEndpointRequest(ctx, requester) {
|
||||
return errors.WithStack(fosite.ErrUnknownRequest)
|
||||
}
|
||||
return nil
|
||||
@ -110,7 +111,12 @@ func (t *TokenExchangeHandler) PopulateTokenEndpointResponse(ctx context.Context
|
||||
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
|
||||
return t.idTokenStrategy.GenerateIDToken(ctx, downscoped)
|
||||
|
||||
// Note: if we wanted to support clients with custom token lifespans, then we would need to call
|
||||
// fosite.GetEffectiveLifespan() to determine the lifespan here.
|
||||
idTokenLifespan := t.fositeConfig.GetIDTokenLifespan(ctx)
|
||||
|
||||
return t.idTokenStrategy.GenerateIDToken(ctx, idTokenLifespan, downscoped)
|
||||
}
|
||||
|
||||
func (t *TokenExchangeHandler) validateSession(requester fosite.Requester) error {
|
||||
@ -185,7 +191,7 @@ func (t *TokenExchangeHandler) validateParams(params url.Values) (*stsParams, er
|
||||
|
||||
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(accessToken)
|
||||
signature := t.accessTokenStrategy.AccessTokenSignature(ctx, accessToken)
|
||||
originalRequester, err := t.accessTokenStorage.GetAccessTokenSession(ctx, signature, requester.GetSession())
|
||||
if err != nil {
|
||||
// The access token was not found, or there was some other error while reading it.
|
||||
@ -198,10 +204,10 @@ func (t *TokenExchangeHandler) validateAccessToken(ctx context.Context, requeste
|
||||
return originalRequester, nil
|
||||
}
|
||||
|
||||
func (t *TokenExchangeHandler) CanSkipClientAuth(_ fosite.AccessRequester) bool {
|
||||
func (t *TokenExchangeHandler) CanSkipClientAuth(_ context.Context, _ fosite.AccessRequester) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *TokenExchangeHandler) CanHandleTokenEndpointRequest(requester fosite.AccessRequester) bool {
|
||||
func (t *TokenExchangeHandler) CanHandleTokenEndpointRequest(_ context.Context, requester fosite.AccessRequester) bool {
|
||||
return requester.GetGrantTypes().ExactOne(oidcapi.GrantTypeTokenExchange)
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ const (
|
||||
|
||||
ErrOIDCClientSecretStorageVersion = constable.Error("OIDC client secret storage data has wrong version")
|
||||
|
||||
// Version 1 was the initial release of the OIDCClientSecretRequest API, which uses OIDCClientSecretStorage for storage.
|
||||
oidcClientSecretStorageVersion = "1"
|
||||
)
|
||||
|
||||
|
@ -4,6 +4,8 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hash"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
@ -185,11 +187,39 @@ func formpostTemplateServer(t *testing.T, redirectURI string, responseParams url
|
||||
return server.URL
|
||||
}
|
||||
|
||||
type testHMACStrategyConfigurator struct {
|
||||
secret []byte
|
||||
entropy int
|
||||
}
|
||||
|
||||
func newTestHMACStrategyConfigurator(secret []byte, entropy int) hmac.HMACStrategyConfigurator {
|
||||
return &testHMACStrategyConfigurator{
|
||||
secret: secret,
|
||||
entropy: entropy,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *testHMACStrategyConfigurator) GetTokenEntropy(_ context.Context) int {
|
||||
return t.entropy
|
||||
}
|
||||
|
||||
func (t *testHMACStrategyConfigurator) GetGlobalSecret(_ context.Context) ([]byte, error) {
|
||||
return t.secret, nil
|
||||
}
|
||||
|
||||
func (t *testHMACStrategyConfigurator) GetRotatedGlobalSecrets(_ context.Context) ([][]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *testHMACStrategyConfigurator) GetHMACHasher(_ context.Context) func() hash.Hash {
|
||||
return nil // nil will cause fosite to use a default hasher
|
||||
}
|
||||
|
||||
// formpostRandomParams is a helper to generate random OAuth2 response parameters for testing.
|
||||
func formpostRandomParams(t *testing.T) url.Values {
|
||||
t.Helper()
|
||||
generator := &hmac.HMACStrategy{GlobalSecret: testlib.RandBytes(t, 32), TokenEntropy: 32}
|
||||
authCode, _, err := generator.Generate()
|
||||
generator := &hmac.HMACStrategy{Config: newTestHMACStrategyConfigurator(testlib.RandBytes(t, 32), 32)}
|
||||
authCode, _, err := generator.Generate(context.Background())
|
||||
require.NoError(t, err)
|
||||
return url.Values{
|
||||
"code": []string{authCode},
|
||||
|
@ -5,6 +5,7 @@ package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
@ -2005,7 +2006,7 @@ func testSupervisorLogin(
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedIDTokenClaims := []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "nonce", "rat", "azp"}
|
||||
expectedIDTokenClaims := []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "nonce", "rat", "azp", "at_hash"}
|
||||
if slices.Contains(wantDownstreamScopes, "username") {
|
||||
// If the test wants the username scope to have been granted, then also expect the claim in the ID token.
|
||||
expectedIDTokenClaims = append(expectedIDTokenClaims, "username")
|
||||
@ -2052,7 +2053,7 @@ func testSupervisorLogin(
|
||||
refreshedTokenResponse, err := refreshSource.Token()
|
||||
require.NoError(t, err)
|
||||
|
||||
// When refreshing, expect to get an "at_hash" claim, but no "nonce" claim.
|
||||
// When refreshing, do not expect a "nonce" claim.
|
||||
expectRefreshedIDTokenClaims := []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "rat", "azp", "at_hash"}
|
||||
if slices.Contains(wantDownstreamScopes, "username") {
|
||||
// If the test wants the username scope to have been granted, then also expect the claim in the refreshed ID token.
|
||||
@ -2182,6 +2183,21 @@ func verifyTokenResponse(
|
||||
require.NotEmpty(t, tokenResponse.RefreshToken)
|
||||
// Refresh tokens should start with the custom prefix "pin_rt_" to make them identifiable as refresh tokens when seen by a user out of context.
|
||||
require.True(t, strings.HasPrefix(tokenResponse.RefreshToken, "pin_rt_"), "token %q did not have expected prefix 'pin_rt_'", tokenResponse.RefreshToken)
|
||||
|
||||
// The at_hash claim should be present and should be equal to the hash of the access token.
|
||||
actualAccessTokenHashClaimValue := idTokenClaims["at_hash"]
|
||||
require.NotEmpty(t, actualAccessTokenHashClaimValue)
|
||||
require.Equal(t, hashAccessToken(tokenResponse.AccessToken), actualAccessTokenHashClaimValue)
|
||||
}
|
||||
|
||||
func hashAccessToken(accessToken string) string {
|
||||
// See https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken.
|
||||
// "Access Token hash value. Its value is the base64url encoding of the left-most half of
|
||||
// the hash of the octets of the ASCII representation of the access_token value, where the
|
||||
// hash algorithm used is the hash algorithm used in the alg Header Parameter of the ID
|
||||
// Token's JOSE Header."
|
||||
b := sha256.Sum256([]byte(accessToken))
|
||||
return base64.RawURLEncoding.EncodeToString(b[:len(b)/2])
|
||||
}
|
||||
|
||||
func requestAuthorizationAndExpectImmediateRedirectToCallback(t *testing.T, _, downstreamAuthorizeURL, downstreamCallbackURL, _, _ string, _ *http.Client) {
|
||||
|
@ -34,10 +34,10 @@ func TestAuthorizeCodeStorage(t *testing.T) {
|
||||
name = "pinniped-storage-authcode-jssfhaibxdkiaugxufbsso3bixmfo7fzjvuevxbr35c4xdxolqga"
|
||||
)
|
||||
|
||||
hmac := compose.NewOAuth2HMACStrategy(&compose.Config{}, []byte("super-secret-32-byte-for-testing"), nil)
|
||||
hmac := compose.NewOAuth2HMACStrategy(&fosite.Config{GlobalSecret: []byte("super-secret-32-byte-for-testing")})
|
||||
// test data generation via:
|
||||
// code, signature, err := hmac.GenerateAuthorizeCode(ctx, nil)
|
||||
signature := hmac.AuthorizeCodeSignature(code)
|
||||
signature := hmac.AuthorizeCodeSignature(context.Background(), code)
|
||||
|
||||
secrets := client.CoreV1().Secrets(env.SupervisorNamespace)
|
||||
|
||||
@ -91,7 +91,7 @@ func TestAuthorizeCodeStorage(t *testing.T) {
|
||||
// Note that CreateAuthorizeCodeSession() sets Active to true and also sets the Version before storing the session,
|
||||
// so expect those here.
|
||||
session.Active = true
|
||||
session.Version = "3" // this is the value of the authorizationcode.authorizeCodeStorageVersion constant
|
||||
session.Version = "4" // this is the value of the authorizationcode.authorizeCodeStorageVersion constant
|
||||
expectedSessionStorageJSON, err := json.Marshal(session)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, string(expectedSessionStorageJSON), string(initialSecret.Data["pinniped-storage-data"]))
|
||||
|
Loading…
Reference in New Issue
Block a user