From c6f1d2953870940f9c0dda83899c31a1ca10bcca Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Wed, 6 Oct 2021 15:28:13 -0700 Subject: [PATCH] Use PinnipedSession type instead of fosite's DefaultSesssion type This will allow us to store custom data inside the fosite session storage for all downstream OIDC sessions. Signed-off-by: Margo Crawford --- .../fositestorage/accesstoken/accesstoken.go | 6 +- .../accesstoken/accesstoken_test.go | 42 ++- .../authorizationcode/authorizationcode.go | 297 +++++++++--------- .../authorizationcode_test.go | 45 ++- internal/fositestorage/fositestorage.go | 6 +- .../openidconnect/openidconnect.go | 5 +- .../openidconnect/openidconnect_test.go | 29 +- internal/fositestorage/pkce/pkce.go | 6 +- internal/fositestorage/pkce/pkce_test.go | 30 +- .../refreshtoken/refreshtoken.go | 6 +- .../refreshtoken/refreshtoken_test.go | 42 ++- internal/oidc/auth/auth_handler.go | 15 +- .../downstreamsession/downstream_session.go | 17 +- internal/oidc/token/token_handler.go | 8 +- internal/oidc/token/token_handler_test.go | 55 ++-- internal/psession/pinniped_session.go | 99 ++++++ .../testutil/oidctestutil/oidctestutil.go | 26 +- internal/testutil/psession.go | 43 +++ 18 files changed, 462 insertions(+), 315 deletions(-) create mode 100644 internal/psession/pinniped_session.go create mode 100644 internal/testutil/psession.go diff --git a/internal/fositestorage/accesstoken/accesstoken.go b/internal/fositestorage/accesstoken/accesstoken.go index b4427543..fc2427ad 100644 --- a/internal/fositestorage/accesstoken/accesstoken.go +++ b/internal/fositestorage/accesstoken/accesstoken.go @@ -10,7 +10,6 @@ import ( "github.com/ory/fosite" "github.com/ory/fosite/handler/oauth2" - "github.com/ory/fosite/handler/openid" "k8s.io/apimachinery/pkg/api/errors" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" @@ -18,6 +17,7 @@ import ( "go.pinniped.dev/internal/crud" "go.pinniped.dev/internal/fositestorage" "go.pinniped.dev/internal/oidc/clientregistry" + "go.pinniped.dev/internal/psession" ) const ( @@ -26,7 +26,7 @@ const ( ErrInvalidAccessTokenRequestVersion = constable.Error("access token request data has wrong version") ErrInvalidAccessTokenRequestData = constable.Error("access token request data must be present") - accessTokenStorageVersion = "1" + accessTokenStorageVersion = "2" ) type RevocationStorage interface { @@ -110,7 +110,7 @@ func newValidEmptyAccessTokenSession() *session { return &session{ Request: &fosite.Request{ Client: &clientregistry.Client{}, - Session: &openid.DefaultSession{}, + Session: &psession.PinnipedSession{}, }, } } diff --git a/internal/fositestorage/accesstoken/accesstoken_test.go b/internal/fositestorage/accesstoken/accesstoken_test.go index 3c5b22df..dc468742 100644 --- a/internal/fositestorage/accesstoken/accesstoken_test.go +++ b/internal/fositestorage/accesstoken/accesstoken_test.go @@ -10,7 +10,6 @@ import ( "time" "github.com/ory/fosite" - "github.com/ory/fosite/handler/openid" "github.com/pkg/errors" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" @@ -22,6 +21,8 @@ import ( coretesting "k8s.io/client-go/testing" "go.pinniped.dev/internal/oidc/clientregistry" + "go.pinniped.dev/internal/psession" + "go.pinniped.dev/internal/testutil" ) const namespace = "test-ns" @@ -51,7 +52,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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"1"}`), + "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":{"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/access-token", @@ -84,16 +85,10 @@ func TestAccessTokenStorage(t *testing.T) { RequestObjectSigningAlgorithm: "", TokenEndpointAuthSigningAlgorithm: "", }}, - RequestedScope: nil, - GrantedScope: nil, - Form: url.Values{"key": []string{"val"}}, - Session: &openid.DefaultSession{ - Claims: nil, - Headers: nil, - ExpiresAt: nil, - Username: "snorlax", - Subject: "panda", - }, + RequestedScope: nil, + GrantedScope: nil, + Form: url.Values{"key": []string{"val"}}, + Session: testutil.NewFakePinnipedSession(), RequestedAudience: nil, GrantedAudience: nil, } @@ -107,6 +102,7 @@ func TestAccessTokenStorage(t *testing.T) { err = storage.DeleteAccessTokenSession(ctx, "fancy-signature") require.NoError(t, err) + testutil.LogActualJSONFromCreateAction(t, client, 0) // makes it easier to update expected values when needed require.Equal(t, wantActions, client.Actions()) } @@ -125,7 +121,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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"1"}`), + "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":{"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/access-token", @@ -151,11 +147,8 @@ func TestAccessTokenStorageRevocation(t *testing.T) { TokenEndpointAuthMethod: "something", }, }, - Form: url.Values{"key": []string{"val"}}, - Session: &openid.DefaultSession{ - Username: "snorlax", - Subject: "panda", - }, + Form: url.Values{"key": []string{"val"}}, + Session: testutil.NewFakePinnipedSession(), } err := storage.CreateAccessTokenSession(ctx, "fancy-signature", request) require.NoError(t, err) @@ -164,6 +157,7 @@ func TestAccessTokenStorageRevocation(t *testing.T) { err = storage.RevokeAccessToken(ctx, "abcd-1") require.NoError(t, err) + testutil.LogActualJSONFromCreateAction(t, client, 0) // makes it easier to update expected values when needed require.Equal(t, wantActions, client.Actions()) } @@ -190,7 +184,7 @@ func TestWrongVersion(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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"not-the-right-version"}`), + "pinniped-storage-data": []byte(`{"request":{"id":"abcd-1"},"version":"not-the-right-version"}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/access-token", @@ -200,7 +194,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 1") + 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 2") } func TestNilSessionRequest(t *testing.T) { @@ -218,7 +212,7 @@ func TestNilSessionRequest(t *testing.T) { }, }, Data: map[string][]byte{ - "pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value","version":"1"}`), + "pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value","version":"2"}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/access-token", @@ -246,10 +240,10 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) { Client: &clientregistry.Client{}, } err := storage.CreateAccessTokenSession(ctx, "signature-doesnt-matter", request) - require.EqualError(t, err, "requester's session must be of type openid.DefaultSession") + require.EqualError(t, err, "requester's session must be of type PinnipedSession") request = &fosite.Request{ - Session: &openid.DefaultSession{}, + Session: &psession.PinnipedSession{}, Client: nil, } err = storage.CreateAccessTokenSession(ctx, "signature-doesnt-matter", request) @@ -261,7 +255,7 @@ func TestCreateWithoutRequesterID(t *testing.T) { request := &fosite.Request{ ID: "", // empty ID - Session: &openid.DefaultSession{}, + Session: &psession.PinnipedSession{}, Client: &clientregistry.Client{}, } err := storage.CreateAccessTokenSession(ctx, "signature-doesnt-matter", request) diff --git a/internal/fositestorage/authorizationcode/authorizationcode.go b/internal/fositestorage/authorizationcode/authorizationcode.go index f66c193c..a76f684f 100644 --- a/internal/fositestorage/authorizationcode/authorizationcode.go +++ b/internal/fositestorage/authorizationcode/authorizationcode.go @@ -11,7 +11,6 @@ import ( "github.com/ory/fosite" "github.com/ory/fosite/handler/oauth2" - "github.com/ory/fosite/handler/openid" "k8s.io/apimachinery/pkg/api/errors" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" @@ -19,6 +18,7 @@ import ( "go.pinniped.dev/internal/crud" "go.pinniped.dev/internal/fositestorage" "go.pinniped.dev/internal/oidc/clientregistry" + "go.pinniped.dev/internal/psession" ) const ( @@ -27,7 +27,7 @@ const ( ErrInvalidAuthorizeRequestData = constable.Error("authorization request data must be present") ErrInvalidAuthorizeRequestVersion = constable.Error("authorization request data has wrong version") - authorizeCodeStorageVersion = "1" + authorizeCodeStorageVersion = "2" ) var _ oauth2.AuthorizeCodeStorage = &authorizeCodeStorage{} @@ -139,7 +139,7 @@ func NewValidEmptyAuthorizeCodeSession() *AuthorizeCodeSession { return &AuthorizeCodeSession{ Request: &fosite.Request{ Client: &clientregistry.Client{}, - Session: &openid.DefaultSession{}, + Session: &psession.PinnipedSession{}, }, } } @@ -169,161 +169,172 @@ func (e *errSerializationFailureWithCause) Error() string { // ExpectedAuthorizeCodeSessionJSONFromFuzzing is used for round tripping tests. // It is exported to allow integration tests to use it. const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{ - "active": true, - "request": { + "active": true, + "request": { "id": "曑x螠Gæ鄋楨", "requestedAt": "2082-11-10T18:36:11.627253638Z", "client": { - "id": ":NJ¸Ɣ8(黋馛ÄRɴJa¶z", - "client_secret": "UQ==", - "redirect_uris": [ - "ǖ枭kʍ切厦ȳ箦;¥ʊXĝ奨誷傥祩d", - "zŇZ", - "優蒼ĊɌț訫DŽǽeʀO2ƚ&N" - ], - "grant_types": [ - "唐W6ɻ橩斚薛ɑƐ" - ], - "response_types": [ - "w", - "ǔŭe[u@阽羂ŷ-Ĵ½輢OÅ濲喾H" - ], - "scopes": [ - "G螩歐湡ƙı唡ɸğƎ&胢輢Ƈĵƚ" - ], - "audience": [ - "ě" - ], - "public": false, - "jwks_uri": "o*泞羅ʘ Ⱦķ瀊垰7ã\")", - "jwks": { - "keys": [ - { - "kty": "OKP", - "crv": "Ed25519", - "x": "nK9xgX_iN7u3u_i8YOO7ZRT_WK028Vd_nhtsUu7Eo6E", - "x5u": { - "Scheme": "", - "Opaque": "", - "User": null, - "Host": "", - "Path": "", - "RawPath": "", - "ForceQuery": false, - "RawQuery": "", - "Fragment": "", - "RawFragment": "" - } - }, - { - "kty": "OKP", - "crv": "Ed25519", - "x": "UbbswQgzWhfGCRlwQmMp6fw_HoIoqkIaKT-2XN2fuYU", - "x5u": { - "Scheme": "", - "Opaque": "", - "User": null, - "Host": "", - "Path": "", - "RawPath": "", - "ForceQuery": false, - "RawQuery": "", - "Fragment": "", - "RawFragment": "" - } - } - ] - }, - "token_endpoint_auth_method": "ƿʥǟȒ伉Q鱙翑ȲŻ", - "Issuer": "锰劝旣樎Ȱ鍌#ȳńƩŴȭ", - "Subject": "绝TFNJĆw宵ɚeY48珎²", - "Audience": [ - "éã越|j¦鲶H股ƲLŋZ-{5£踉4" - ], - "Nonce": "5^驜Ŗ~ů崧軒q腟u尿", - "ExpiresAt": "2065-11-30T13:47:03.613000626Z", - "IssuedAt": "1976-02-22T09:57:20.479850437Z", - "RequestedAt": "2016-04-13T04:18:53.648949323Z", - "AuthTime": "2098-07-12T04:38:54.034043015Z", - "AccessTokenHash": "嫯R", - "AuthenticationContextClassReference": "¤'+ʣ", - "AuthenticationMethodsReference": "L&ɽ艄ʬʏ", - "CodeHash": "ğǫ\\aȊ4ț髄Al", - "Extra": { - "PƢ曰": { - "ĸŴB岺Ð嫹Sx镯荫ő": [ - 843216989 - ], - "疂ư墫ɓ": { - "\\BRë_g\"ʎ啴SƇMǃļ": { - "ʦ4": false - }, - "鶡萷ɵ啜s攦": null - } - }, - "曓蓳n匟鯘磹*金爃鶴滱ůĮǐ_c3#": 2520197933 - } - }, - "Headers": { - "Extra": { - "寱ĊƑ÷Ƒ螞费Ďğ~劰û橸ɽ銐ƭ?}": { - "ȜʁɁ;Bd謺錳4帳ŅǃĊd": { - "翢砜Fȏl鐉诳DT=3骜": { - "ų厷ɁOƪ穋嶿鳈恱va|载ǰɱ汶C": false - }, - "鸨EJ毕懴řĬń戹%c": null + "fosite": { + "Claims": { + "JTI": "u妔隤ʑƍš駎竪0ɔ闏À1", + "Issuer": "麤ã桒嘞\\摗Ǘū稖咾鎅ǸÖ绝TF", + "Subject": "巽ēđų蓼tùZ蛆鬣a\"ÙǞ0觢Û±", + "Audience": [ + "H股ƲL", + "肟v\u0026đehpƧ", + "5^驜Ŗ~ů崧軒q腟u尿" + ], + "Nonce": "ğ", + "ExpiresAt": "2016-11-22T21:33:58.460521133Z", + "IssuedAt": "1990-07-25T23:42:07.055978334Z", + "RequestedAt": "1971-01-30T00:23:36.377684025Z", + "AuthTime": "2088-11-09T12:09:14.051840239Z", + "AccessTokenHash": "蕖¤'+ʣȍ瓁U4鞀", + "AuthenticationContextClassReference": "ʏÑęN\u003c_z", + "AuthenticationMethodsReference": "ț髄A", + "CodeHash": "4磔_袻vÓG-壧丵礴鋈k蟵pAɂʅ", + "Extra": { + "#\u0026PƢ曰l騌蘙螤\\阏Đ镴Ƥm蔻ǭ\\鿞": 1677215584, + "Y\u0026鶡萷ɵ啜s攦Ɩïdnǔ": { + ",t猟i\u0026\u0026Q@ǤǟǗǪ飘ȱF?Ƈ": { + "~劰û橸ɽ銐ƭ?}H": null, + "癑勦e骲v0H晦XŘO溪V蔓": { + "碼Ǫ": false + } + }, + "钻煐ɨəÅDČ{Ȩʦ4撎": [ + 3684968178 + ] + } + } }, - "室癑勦e骲v0H晦XŘO溪V蔓Ȍ+~ē": [ - 954647573 - ] - }, - "麈ƵDǀ\\郂üţ垂": 1572524915 + "Headers": { + "Extra": { + "ĊdŘ鸨EJ毕懴řĬń戹": { + "诳DT=3骜Ǹ,": { + "\u003e": { + "ǰ": false + }, + "ɁOƪ穋嶿鳈恱va": null + }, + "豑觳翢砜Fȏl": [ + 927958776 + ] + }, + "埅ȜʁɁ;Bd謺錳4帳Ņ": 388005986 + } + }, + "ExpiresAt": { + "C]ɲ'=ĸ闒NȢȰ.醋": "1970-07-19T18:03:29.902062193Z", + "fɤȆʪ融ƆuŤn": "2064-01-24T20:34:16.593152073Z", + "爣縗ɦüHêQ仏1ő": "2102-03-17T06:24:40.256846902Z" + }, + "Username": "韁臯氃妪婝rȤ\"h丬鎒ơ娻}ɼƟ", + "Subject": "闺髉龳ǽÙ龦O亾EW莛8嘶×" + }, + "custom": { + "providerUID": "鵮碡ʯiŬŽ非Ĝ眧Ĭ葜SŦ餧Ĭ倏4", + "providerName": "nŐǛ3", + "providerType": "闣ʬ橳(ý綃ʃʚƟ覣k眐4Ĉt", + "oidc": { + "upstreamRefreshToken": "嵽痊w©Ź榨Q|ôɵt毇妬" + } } - }, - "ExpiresAt": { - "'=ĸ闒NȢȰ.醋fʜ": "2031-10-18T22:07:34.950803105Z", - "ɦüHêQ仏1őƖ2Ė暮唍ǞʜƢú4": "2049-05-13T15:27:20.968432454Z" - }, - "Username": "+韁臯氃妪婝rȤ\"h丬鎒ơ娻}ɼƟȥE", - "Subject": "龳ǽÙ龦O亾EW莛8嘶×姮c恭企" }, "requestedAudience": [ - "邖ɐ5檄¬", - "Ĭ葜SŦ餧Ĭ倏4ĵ嶼仒篻ɥ闣ʬ橳(ý綃" + "6鉢緋uƴŤȱʀļÂ?墖\u003cƬb獭潜Ʃ饾" ], "grantedAudience": [ - "ʚƟ覣k眐4ĈtC嵽痊w©Ź榨Q|ô", - "猊Ia瓕巈環_ɑ彨ƍ蛊ʚ£:設虝2" + "|鬌R蜚蠣麹概÷驣7Ʀ澉1æɽ誮rʨ鷞" ] - }, - "version": "1" - }` + }, + "version": "2" +}` diff --git a/internal/fositestorage/authorizationcode/authorizationcode_test.go b/internal/fositestorage/authorizationcode/authorizationcode_test.go index b93481c9..fdc98e96 100644 --- a/internal/fositestorage/authorizationcode/authorizationcode_test.go +++ b/internal/fositestorage/authorizationcode/authorizationcode_test.go @@ -18,7 +18,6 @@ import ( fuzz "github.com/google/gofuzz" "github.com/ory/fosite" "github.com/ory/fosite/handler/oauth2" - "github.com/ory/fosite/handler/openid" "github.com/pkg/errors" "github.com/stretchr/testify/require" "gopkg.in/square/go-jose.v2" @@ -34,6 +33,8 @@ import ( "go.pinniped.dev/internal/fositestorage" "go.pinniped.dev/internal/oidc/clientregistry" + "go.pinniped.dev/internal/psession" + "go.pinniped.dev/internal/testutil" ) const namespace = "test-ns" @@ -62,7 +63,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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"1"}`), + "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":{"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/authcode", @@ -81,7 +82,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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"1"}`), + "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":{"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/authcode", @@ -113,16 +114,10 @@ func TestAuthorizationCodeStorage(t *testing.T) { TokenEndpointAuthSigningAlgorithm: "", }, }, - RequestedScope: nil, - GrantedScope: nil, - Form: url.Values{"key": []string{"val"}}, - Session: &openid.DefaultSession{ - Claims: nil, - Headers: nil, - ExpiresAt: nil, - Username: "snorlax", - Subject: "panda", - }, + RequestedScope: nil, + GrantedScope: nil, + Form: url.Values{"key": []string{"val"}}, + Session: testutil.NewFakePinnipedSession(), RequestedAudience: nil, GrantedAudience: nil, } @@ -136,6 +131,8 @@ func TestAuthorizationCodeStorage(t *testing.T) { err = storage.InvalidateAuthorizeCodeSession(ctx, "fancy-signature") require.NoError(t, err) + testutil.LogActualJSONFromCreateAction(t, client, 0) // makes it easier to update expected values when needed + testutil.LogActualJSONFromUpdateAction(t, client, 3) // makes it easier to update expected values when needed require.Equal(t, wantActions, client.Actions()) // Doing a Get on an invalidated session should still return the session, but also return an error. @@ -173,7 +170,7 @@ func TestInvalidateWhenConflictOnUpdateHappens(t *testing.T) { request := &fosite.Request{ ID: "some-request-id", Client: &clientregistry.Client{}, - Session: &openid.DefaultSession{}, + Session: testutil.NewFakePinnipedSession(), } err := storage.CreateAuthorizeCodeSession(ctx, "fancy-signature", request) require.NoError(t, err) @@ -193,7 +190,7 @@ func TestWrongVersion(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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"not-the-right-version", "active": true}`), + "pinniped-storage-data": []byte(`{"request":{"id":"abcd-1"},"version":"not-the-right-version","active": true}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/authcode", @@ -203,7 +200,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 1") + require.EqualError(t, err, "authorization request data has wrong version: authorization code session for fancy-signature has version not-the-right-version instead of 2") } func TestNilSessionRequest(t *testing.T) { @@ -218,7 +215,7 @@ func TestNilSessionRequest(t *testing.T) { }, }, Data: map[string][]byte{ - "pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value", "version":"1", "active": true}`), + "pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value", "version":"2", "active": true}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/authcode", @@ -246,10 +243,10 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) { Client: &clientregistry.Client{}, } err := storage.CreateAuthorizeCodeSession(ctx, "signature-doesnt-matter", request) - require.EqualError(t, err, "requester's session must be of type openid.DefaultSession") + require.EqualError(t, err, "requester's session must be of type PinnipedSession") request = &fosite.Request{ - Session: &openid.DefaultSession{}, + Session: &psession.PinnipedSession{}, Client: nil, } err = storage.CreateAuthorizeCodeSession(ctx, "signature-doesnt-matter", request) @@ -274,7 +271,7 @@ func TestFuzzAndJSONNewValidEmptyAuthorizeCodeSession(t *testing.T) { // checked above defaultClient := validSession.Request.Client.(*clientregistry.Client) - defaultSession := validSession.Request.Session.(*openid.DefaultSession) + pinnipedSession := validSession.Request.Session.(*psession.PinnipedSession) // makes it easier to use a raw string replacer := strings.NewReplacer("`", "a") @@ -297,12 +294,12 @@ func TestFuzzAndJSONNewValidEmptyAuthorizeCodeSession(t *testing.T) { *fc = defaultClient }, func(fs *fosite.Session, c fuzz.Continue) { - c.Fuzz(defaultSession) - *fs = defaultSession + c.Fuzz(pinnipedSession) + *fs = pinnipedSession }, // these types contain an interface{} that we need to handle - // this is safe because we explicitly provide the openid.DefaultSession concrete type + // this is safe because we explicitly provide the PinnipedSession concrete type func(value *map[string]interface{}, c fuzz.Continue) { // cover all the JSON data types just in case *value = map[string]interface{}{ @@ -382,7 +379,7 @@ func TestFuzzAndJSONNewValidEmptyAuthorizeCodeSession(t *testing.T) { // set these to match CreateAuthorizeCodeSession so that .JSONEq works validSession.Active = true - validSession.Version = "1" + validSession.Version = "2" validSessionJSONBytes, err := json.MarshalIndent(validSession, "", "\t") require.NoError(t, err) diff --git a/internal/fositestorage/fositestorage.go b/internal/fositestorage/fositestorage.go index bf1c20e9..d3c8f476 100644 --- a/internal/fositestorage/fositestorage.go +++ b/internal/fositestorage/fositestorage.go @@ -5,16 +5,16 @@ package fositestorage import ( "github.com/ory/fosite" - "github.com/ory/fosite/handler/openid" "go.pinniped.dev/internal/constable" "go.pinniped.dev/internal/oidc/clientregistry" + "go.pinniped.dev/internal/psession" ) const ( ErrInvalidRequestType = constable.Error("requester must be of type fosite.Request") ErrInvalidClientType = constable.Error("requester's client must be of type clientregistry.Client") - ErrInvalidSessionType = constable.Error("requester's session must be of type openid.DefaultSession") + ErrInvalidSessionType = constable.Error("requester's session must be of type PinnipedSession") StorageRequestIDLabelName = "storage.pinniped.dev/request-id" //nolint:gosec // this is not a credential ) @@ -27,7 +27,7 @@ func ValidateAndExtractAuthorizeRequest(requester fosite.Requester) (*fosite.Req if !ok2 { return nil, ErrInvalidClientType } - _, ok3 := request.Session.(*openid.DefaultSession) + _, ok3 := request.Session.(*psession.PinnipedSession) if !ok3 { return nil, ErrInvalidSessionType } diff --git a/internal/fositestorage/openidconnect/openidconnect.go b/internal/fositestorage/openidconnect/openidconnect.go index f747a667..f1e2200f 100644 --- a/internal/fositestorage/openidconnect/openidconnect.go +++ b/internal/fositestorage/openidconnect/openidconnect.go @@ -18,6 +18,7 @@ import ( "go.pinniped.dev/internal/crud" "go.pinniped.dev/internal/fositestorage" "go.pinniped.dev/internal/oidc/clientregistry" + "go.pinniped.dev/internal/psession" ) const ( @@ -27,7 +28,7 @@ const ( ErrInvalidOIDCRequestData = constable.Error("oidc request data must be present") ErrMalformedAuthorizationCode = constable.Error("malformed authorization code") - oidcStorageVersion = "1" + oidcStorageVersion = "2" ) var _ openid.OpenIDConnectRequestStorage = &openIDConnectRequestStorage{} @@ -112,7 +113,7 @@ func newValidEmptyOIDCSession() *session { return &session{ Request: &fosite.Request{ Client: &clientregistry.Client{}, - Session: &openid.DefaultSession{}, + Session: &psession.PinnipedSession{}, }, } } diff --git a/internal/fositestorage/openidconnect/openidconnect_test.go b/internal/fositestorage/openidconnect/openidconnect_test.go index 6328ffa2..ba76fe8e 100644 --- a/internal/fositestorage/openidconnect/openidconnect_test.go +++ b/internal/fositestorage/openidconnect/openidconnect_test.go @@ -22,6 +22,8 @@ import ( coretesting "k8s.io/client-go/testing" "go.pinniped.dev/internal/oidc/clientregistry" + "go.pinniped.dev/internal/psession" + "go.pinniped.dev/internal/testutil" ) const namespace = "test-ns" @@ -50,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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"1"}`), + "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":{"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/oidc", @@ -84,16 +86,10 @@ func TestOpenIdConnectStorage(t *testing.T) { TokenEndpointAuthSigningAlgorithm: "", }, }, - RequestedScope: nil, - GrantedScope: nil, - Form: url.Values{"key": []string{"val"}}, - Session: &openid.DefaultSession{ - Claims: nil, - Headers: nil, - ExpiresAt: nil, - Username: "snorlax", - Subject: "panda", - }, + RequestedScope: nil, + GrantedScope: nil, + Form: url.Values{"key": []string{"val"}}, + Session: testutil.NewFakePinnipedSession(), RequestedAudience: nil, GrantedAudience: nil, } @@ -107,6 +103,7 @@ func TestOpenIdConnectStorage(t *testing.T) { err = storage.DeleteOpenIDConnectSession(ctx, "fancy-code.fancy-signature") require.NoError(t, err) + testutil.LogActualJSONFromCreateAction(t, client, 0) // makes it easier to update expected values when needed require.Equal(t, wantActions, client.Actions()) } @@ -130,7 +127,7 @@ func TestWrongVersion(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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"not-the-right-version"}`), + "pinniped-storage-data": []byte(`{"request":{"id":"abcd-1"},"version":"not-the-right-version"}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/oidc", @@ -140,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 1") + require.EqualError(t, err, "oidc request data has wrong version: oidc session for fancy-signature has version not-the-right-version instead of 2") } func TestNilSessionRequest(t *testing.T) { @@ -155,7 +152,7 @@ func TestNilSessionRequest(t *testing.T) { }, }, Data: map[string][]byte{ - "pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value","version":"1"}`), + "pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value","version":"2"}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/oidc", @@ -183,10 +180,10 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) { Client: &clientregistry.Client{}, } err := storage.CreateOpenIDConnectSession(ctx, "authcode.signature-doesnt-matter", request) - require.EqualError(t, err, "requester's session must be of type openid.DefaultSession") + require.EqualError(t, err, "requester's session must be of type PinnipedSession") request = &fosite.Request{ - Session: &openid.DefaultSession{}, + Session: &psession.PinnipedSession{}, Client: nil, } err = storage.CreateOpenIDConnectSession(ctx, "authcode.signature-doesnt-matter", request) diff --git a/internal/fositestorage/pkce/pkce.go b/internal/fositestorage/pkce/pkce.go index 424a7855..cf6cec69 100644 --- a/internal/fositestorage/pkce/pkce.go +++ b/internal/fositestorage/pkce/pkce.go @@ -9,7 +9,6 @@ import ( "time" "github.com/ory/fosite" - "github.com/ory/fosite/handler/openid" "github.com/ory/fosite/handler/pkce" "k8s.io/apimachinery/pkg/api/errors" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" @@ -18,6 +17,7 @@ import ( "go.pinniped.dev/internal/crud" "go.pinniped.dev/internal/fositestorage" "go.pinniped.dev/internal/oidc/clientregistry" + "go.pinniped.dev/internal/psession" ) const ( @@ -26,7 +26,7 @@ const ( ErrInvalidPKCERequestVersion = constable.Error("pkce request data has wrong version") ErrInvalidPKCERequestData = constable.Error("pkce request data must be present") - pkceStorageVersion = "1" + pkceStorageVersion = "2" ) var _ pkce.PKCERequestStorage = &pkceStorage{} @@ -96,7 +96,7 @@ func newValidEmptyPKCESession() *session { return &session{ Request: &fosite.Request{ Client: &clientregistry.Client{}, - Session: &openid.DefaultSession{}, + Session: &psession.PinnipedSession{}, }, } } diff --git a/internal/fositestorage/pkce/pkce_test.go b/internal/fositestorage/pkce/pkce_test.go index 671797eb..d57649be 100644 --- a/internal/fositestorage/pkce/pkce_test.go +++ b/internal/fositestorage/pkce/pkce_test.go @@ -10,7 +10,6 @@ import ( "time" "github.com/ory/fosite" - "github.com/ory/fosite/handler/openid" "github.com/ory/fosite/handler/pkce" "github.com/pkg/errors" "github.com/stretchr/testify/require" @@ -23,6 +22,8 @@ import ( coretesting "k8s.io/client-go/testing" "go.pinniped.dev/internal/oidc/clientregistry" + "go.pinniped.dev/internal/psession" + "go.pinniped.dev/internal/testutil" ) const namespace = "test-ns" @@ -51,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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"1"}`), + "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":{"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/pkce", @@ -85,16 +86,10 @@ func TestPKCEStorage(t *testing.T) { TokenEndpointAuthSigningAlgorithm: "", }, }, - RequestedScope: nil, - GrantedScope: nil, - Form: url.Values{"key": []string{"val"}}, - Session: &openid.DefaultSession{ - Claims: nil, - Headers: nil, - ExpiresAt: nil, - Username: "snorlax", - Subject: "panda", - }, + RequestedScope: nil, + GrantedScope: nil, + Form: url.Values{"key": []string{"val"}}, + Session: testutil.NewFakePinnipedSession(), RequestedAudience: nil, GrantedAudience: nil, } @@ -108,6 +103,7 @@ func TestPKCEStorage(t *testing.T) { err = storage.DeletePKCERequestSession(ctx, "fancy-signature") require.NoError(t, err) + testutil.LogActualJSONFromCreateAction(t, client, 0) // makes it easier to update expected values when needed require.Equal(t, wantActions, client.Actions()) } @@ -134,7 +130,7 @@ func TestWrongVersion(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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"not-the-right-version"}`), + "pinniped-storage-data": []byte(`{"request":{"id":"abcd-1"},"version":"not-the-right-version"}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/pkce", @@ -144,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 1") + require.EqualError(t, err, "pkce request data has wrong version: pkce session for fancy-signature has version not-the-right-version instead of 2") } func TestNilSessionRequest(t *testing.T) { @@ -162,7 +158,7 @@ func TestNilSessionRequest(t *testing.T) { }, }, Data: map[string][]byte{ - "pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value","version":"1"}`), + "pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value","version":"2"}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/pkce", @@ -190,10 +186,10 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) { Client: &clientregistry.Client{}, } err := storage.CreatePKCERequestSession(ctx, "signature-doesnt-matter", request) - require.EqualError(t, err, "requester's session must be of type openid.DefaultSession") + require.EqualError(t, err, "requester's session must be of type PinnipedSession") request = &fosite.Request{ - Session: &openid.DefaultSession{}, + Session: &psession.PinnipedSession{}, Client: nil, } err = storage.CreatePKCERequestSession(ctx, "signature-doesnt-matter", request) diff --git a/internal/fositestorage/refreshtoken/refreshtoken.go b/internal/fositestorage/refreshtoken/refreshtoken.go index f53d9bf7..a456ed75 100644 --- a/internal/fositestorage/refreshtoken/refreshtoken.go +++ b/internal/fositestorage/refreshtoken/refreshtoken.go @@ -10,7 +10,6 @@ import ( "github.com/ory/fosite" "github.com/ory/fosite/handler/oauth2" - "github.com/ory/fosite/handler/openid" "k8s.io/apimachinery/pkg/api/errors" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" @@ -18,6 +17,7 @@ import ( "go.pinniped.dev/internal/crud" "go.pinniped.dev/internal/fositestorage" "go.pinniped.dev/internal/oidc/clientregistry" + "go.pinniped.dev/internal/psession" ) const ( @@ -26,7 +26,7 @@ const ( ErrInvalidRefreshTokenRequestVersion = constable.Error("refresh token request data has wrong version") ErrInvalidRefreshTokenRequestData = constable.Error("refresh token request data must be present") - refreshTokenStorageVersion = "1" + refreshTokenStorageVersion = "2" ) type RevocationStorage interface { @@ -110,7 +110,7 @@ func newValidEmptyRefreshTokenSession() *session { return &session{ Request: &fosite.Request{ Client: &clientregistry.Client{}, - Session: &openid.DefaultSession{}, + Session: &psession.PinnipedSession{}, }, } } diff --git a/internal/fositestorage/refreshtoken/refreshtoken_test.go b/internal/fositestorage/refreshtoken/refreshtoken_test.go index 10ee75bf..e8a155c9 100644 --- a/internal/fositestorage/refreshtoken/refreshtoken_test.go +++ b/internal/fositestorage/refreshtoken/refreshtoken_test.go @@ -10,7 +10,6 @@ import ( "time" "github.com/ory/fosite" - "github.com/ory/fosite/handler/openid" "github.com/pkg/errors" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" @@ -22,6 +21,8 @@ import ( coretesting "k8s.io/client-go/testing" "go.pinniped.dev/internal/oidc/clientregistry" + "go.pinniped.dev/internal/psession" + "go.pinniped.dev/internal/testutil" ) const namespace = "test-ns" @@ -50,7 +51,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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"1"}`), + "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":{"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/refresh-token", @@ -84,16 +85,10 @@ func TestRefreshTokenStorage(t *testing.T) { TokenEndpointAuthSigningAlgorithm: "", }, }, - RequestedScope: nil, - GrantedScope: nil, - Form: url.Values{"key": []string{"val"}}, - Session: &openid.DefaultSession{ - Claims: nil, - Headers: nil, - ExpiresAt: nil, - Username: "snorlax", - Subject: "panda", - }, + RequestedScope: nil, + GrantedScope: nil, + Form: url.Values{"key": []string{"val"}}, + Session: testutil.NewFakePinnipedSession(), RequestedAudience: nil, GrantedAudience: nil, } @@ -107,6 +102,7 @@ func TestRefreshTokenStorage(t *testing.T) { err = storage.DeleteRefreshTokenSession(ctx, "fancy-signature") require.NoError(t, err) + testutil.LogActualJSONFromCreateAction(t, client, 0) // makes it easier to update expected values when needed require.Equal(t, wantActions, client.Actions()) } @@ -125,7 +121,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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"1"}`), + "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":{"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/refresh-token", @@ -151,11 +147,8 @@ func TestRefreshTokenStorageRevocation(t *testing.T) { TokenEndpointAuthMethod: "something", }, }, - Form: url.Values{"key": []string{"val"}}, - Session: &openid.DefaultSession{ - Username: "snorlax", - Subject: "panda", - }, + Form: url.Values{"key": []string{"val"}}, + Session: testutil.NewFakePinnipedSession(), } err := storage.CreateRefreshTokenSession(ctx, "fancy-signature", request) require.NoError(t, err) @@ -164,6 +157,7 @@ func TestRefreshTokenStorageRevocation(t *testing.T) { err = storage.RevokeRefreshToken(ctx, "abcd-1") require.NoError(t, err) + testutil.LogActualJSONFromCreateAction(t, client, 0) // makes it easier to update expected values when needed require.Equal(t, wantActions, client.Actions()) } @@ -190,7 +184,7 @@ func TestWrongVersion(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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"not-the-right-version"}`), + "pinniped-storage-data": []byte(`{"request":{"id":"abcd-1"},"version":"not-the-right-version"}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/refresh-token", @@ -200,7 +194,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 1") + 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 2") } func TestNilSessionRequest(t *testing.T) { @@ -218,7 +212,7 @@ func TestNilSessionRequest(t *testing.T) { }, }, Data: map[string][]byte{ - "pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value","version":"1"}`), + "pinniped-storage-data": []byte(`{"nonsense-key": "nonsense-value","version":"2"}`), "pinniped-storage-version": []byte("1"), }, Type: "storage.pinniped.dev/refresh-token", @@ -246,10 +240,10 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) { Client: &clientregistry.Client{}, } err := storage.CreateRefreshTokenSession(ctx, "signature-doesnt-matter", request) - require.EqualError(t, err, "requester's session must be of type openid.DefaultSession") + require.EqualError(t, err, "requester's session must be of type PinnipedSession") request = &fosite.Request{ - Session: &openid.DefaultSession{}, + Session: &psession.PinnipedSession{}, Client: nil, } err = storage.CreateRefreshTokenSession(ctx, "signature-doesnt-matter", request) @@ -261,7 +255,7 @@ func TestCreateWithoutRequesterID(t *testing.T) { request := &fosite.Request{ ID: "", // empty ID - Session: &openid.DefaultSession{}, + Session: &psession.PinnipedSession{}, Client: &clientregistry.Client{}, } err := storage.CreateRefreshTokenSession(ctx, "signature-doesnt-matter", request) diff --git a/internal/oidc/auth/auth_handler.go b/internal/oidc/auth/auth_handler.go index 5dd9e211..ed6e1137 100644 --- a/internal/oidc/auth/auth_handler.go +++ b/internal/oidc/auth/auth_handler.go @@ -25,6 +25,7 @@ import ( "go.pinniped.dev/internal/oidc/downstreamsession" "go.pinniped.dev/internal/oidc/provider" "go.pinniped.dev/internal/plog" + "go.pinniped.dev/internal/psession" "go.pinniped.dev/pkg/oidcclient/nonce" "go.pinniped.dev/pkg/oidcclient/pkce" ) @@ -173,12 +174,14 @@ func handleAuthRequestForOIDCUpstreamAuthcodeGrant( } now := time.Now() - _, err := oauthHelper.NewAuthorizeResponse(r.Context(), authorizeRequester, &openid.DefaultSession{ - Claims: &jwt.IDTokenClaims{ - // Temporary claim values to allow `NewAuthorizeResponse` to perform other OIDC validations. - Subject: "none", - AuthTime: now, - RequestedAt: now, + _, err := oauthHelper.NewAuthorizeResponse(r.Context(), authorizeRequester, &psession.PinnipedSession{ + Fosite: &openid.DefaultSession{ + Claims: &jwt.IDTokenClaims{ + // Temporary claim values to allow `NewAuthorizeResponse` to perform other OIDC validations. + Subject: "none", + AuthTime: now, + RequestedAt: now, + }, }, }) if err != nil { diff --git a/internal/oidc/downstreamsession/downstream_session.go b/internal/oidc/downstreamsession/downstream_session.go index 64838d5e..bd29646e 100644 --- a/internal/oidc/downstreamsession/downstream_session.go +++ b/internal/oidc/downstreamsession/downstream_session.go @@ -18,6 +18,7 @@ import ( "go.pinniped.dev/internal/oidc" "go.pinniped.dev/internal/oidc/provider" "go.pinniped.dev/internal/plog" + "go.pinniped.dev/internal/psession" ) const ( @@ -35,19 +36,21 @@ const ( ) // MakeDownstreamSession creates a downstream OIDC session. -func MakeDownstreamSession(subject string, username string, groups []string) *openid.DefaultSession { +func MakeDownstreamSession(subject string, username string, groups []string) *psession.PinnipedSession { now := time.Now().UTC() - openIDSession := &openid.DefaultSession{ - Claims: &jwt.IDTokenClaims{ - Subject: subject, - RequestedAt: now, - AuthTime: now, + openIDSession := &psession.PinnipedSession{ + Fosite: &openid.DefaultSession{ + Claims: &jwt.IDTokenClaims{ + Subject: subject, + RequestedAt: now, + AuthTime: now, + }, }, } if groups == nil { groups = []string{} } - openIDSession.Claims.Extra = map[string]interface{}{ + openIDSession.IDTokenClaims().Extra = map[string]interface{}{ oidc.DownstreamUsernameClaim: username, oidc.DownstreamGroupsClaim: groups, } diff --git a/internal/oidc/token/token_handler.go b/internal/oidc/token/token_handler.go index c481168b..72222367 100644 --- a/internal/oidc/token/token_handler.go +++ b/internal/oidc/token/token_handler.go @@ -1,4 +1,4 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // Package token provides a handler for the OIDC token endpoint. @@ -8,19 +8,19 @@ import ( "net/http" "github.com/ory/fosite" - "github.com/ory/fosite/handler/openid" "go.pinniped.dev/internal/httputil/httperr" "go.pinniped.dev/internal/oidc" "go.pinniped.dev/internal/plog" + "go.pinniped.dev/internal/psession" ) func NewHandler( oauthHelper fosite.OAuth2Provider, ) http.Handler { return httperr.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error { - var session openid.DefaultSession - accessRequest, err := oauthHelper.NewAccessRequest(r.Context(), r, &session) + session := psession.NewPinnipedSession() + accessRequest, err := oauthHelper.NewAccessRequest(r.Context(), r, session) if err != nil { plog.Info("token request error", oidc.FositeErrorForLog(err)...) oauthHelper.WriteAccessError(w, accessRequest, err) diff --git a/internal/oidc/token/token_handler_test.go b/internal/oidc/token/token_handler_test.go index 9032dd10..c8a36153 100644 --- a/internal/oidc/token/token_handler_test.go +++ b/internal/oidc/token/token_handler_test.go @@ -46,6 +46,7 @@ import ( "go.pinniped.dev/internal/here" "go.pinniped.dev/internal/oidc" "go.pinniped.dev/internal/oidc/jwks" + "go.pinniped.dev/internal/psession" "go.pinniped.dev/internal/testutil" "go.pinniped.dev/internal/testutil/oidctestutil" ) @@ -58,7 +59,6 @@ const ( goodNonce = "some-nonce-value-with-enough-bytes-to-exceed-min-allowed" goodSubject = "https://issuer?sub=some-subject" goodUsername = "some-username" - goodGroups = "group1,groups2" hmacSecret = "this needs to be at least 32 characters to meet entropy requirements" @@ -72,6 +72,8 @@ const ( var ( goodAuthTime = time.Date(1, 2, 3, 4, 5, 6, 7, time.UTC) goodRequestedAtTime = time.Date(7, 6, 5, 4, 3, 2, 1, time.UTC) + goodGroups = []string{"group1", "groups2"} + expectedGoodGroups = []interface{}{"group1", "groups2"} hmacSecretFunc = func() []byte { return []byte(hmacSecret) @@ -813,7 +815,7 @@ func TestTokenExchange(t *testing.T) { require.Equal(t, goodSubject, tokenClaims["sub"]) require.Equal(t, goodIssuer, tokenClaims["iss"]) require.Equal(t, goodUsername, tokenClaims["username"]) - require.Equal(t, goodGroups, tokenClaims["groups"]) + require.Equal(t, expectedGoodGroups, tokenClaims["groups"]) // Also assert that some are the same as the original downstream ID token. requireClaimsAreEqual(t, "iss", claimsOfFirstIDToken, tokenClaims) // issuer @@ -1358,18 +1360,25 @@ func makeOauthHelperWithNilPrivateJWTSigningKey( func simulateAuthEndpointHavingAlreadyRun(t *testing.T, authRequest *http.Request, oauthHelper fosite.OAuth2Provider) fosite.AuthorizeResponder { // We only set the fields in the session that Fosite wants us to set. ctx := context.Background() - session := &openid.DefaultSession{ - Claims: &jwt.IDTokenClaims{ - Subject: goodSubject, - RequestedAt: goodRequestedAtTime, - AuthTime: goodAuthTime, - Extra: map[string]interface{}{ - oidc.DownstreamUsernameClaim: goodUsername, - oidc.DownstreamGroupsClaim: goodGroups, + session := &psession.PinnipedSession{ + Fosite: &openid.DefaultSession{ + Claims: &jwt.IDTokenClaims{ + Subject: goodSubject, + RequestedAt: goodRequestedAtTime, + AuthTime: goodAuthTime, + Extra: map[string]interface{}{ + oidc.DownstreamUsernameClaim: goodUsername, + oidc.DownstreamGroupsClaim: goodGroups, + }, + }, + Subject: "", // not used, note that callback_handler.go does not set this + Username: "", // not used, note that callback_handler.go does not set this + }, + Custom: &psession.PinnipedSessionData{ + OIDC: &psession.OIDCSessionData{ + UpstreamRefreshToken: "starting-fake-refresh-token", }, }, - Subject: "", // not used, note that callback_handler.go does not set this - Username: "", // not used, note that callback_handler.go does not set this } authRequester, err := oauthHelper.NewAuthorizeRequest(ctx, authRequest) require.NoError(t, err) @@ -1588,19 +1597,19 @@ func requireValidStoredRequest( require.Equal(t, wantRequestForm, request.GetRequestForm()) // Fosite stores access token request without form // Cast session to the type we think it should be. - session, ok := request.GetSession().(*openid.DefaultSession) - require.Truef(t, ok, "could not cast %T to %T", request.GetSession(), &openid.DefaultSession{}) + session, ok := request.GetSession().(*psession.PinnipedSession) + require.Truef(t, ok, "could not cast %T to %T", request.GetSession(), &psession.PinnipedSession{}) // Assert that the session claims are what we think they should be, but only if we are doing OIDC. if contains(wantGrantedScopes, "openid") { - claims := session.Claims + claims := session.Fosite.Claims require.Empty(t, claims.JTI) // When claims.JTI is empty, Fosite will generate a UUID for this field. require.Equal(t, goodSubject, claims.Subject) // Our custom claims from the authorize endpoint should still be set. require.Equal(t, map[string]interface{}{ "username": goodUsername, - "groups": goodGroups, + "groups": expectedGoodGroups, }, claims.Extra) // We are in charge of setting these fields. For the purpose of testing, we ensure that the @@ -1610,7 +1619,7 @@ func requireValidStoredRequest( // These fields will all be given good defaults by fosite at runtime and we only need to use them // if we want to override the default behaviors. We currently don't need to override these defaults, - // so they do not end up being stored. Fosite sets its defaults at runtime in openid.DefaultSession's + // so they do not end up being stored. Fosite sets its defaults at runtime in openid.DefaultStrategy's // GenerateIDToken() method. require.Empty(t, claims.Issuer) require.Empty(t, claims.Audience) @@ -1630,11 +1639,11 @@ func requireValidStoredRequest( } // Assert that the session headers are what we think they should be. - headers := session.Headers + headers := session.Fosite.Headers require.Empty(t, headers) // Assert that the token expirations are what we think they should be. - authCodeExpiresAt, ok := session.ExpiresAt[fosite.AuthorizeCode] + authCodeExpiresAt, ok := session.Fosite.ExpiresAt[fosite.AuthorizeCode] require.True(t, ok, "expected session to hold expiration time for auth code") testutil.RequireTimeInDelta( t, @@ -1644,7 +1653,7 @@ func requireValidStoredRequest( ) // OpenID Connect sessions do not store access token expiration information. - accessTokenExpiresAt, ok := session.ExpiresAt[fosite.AccessToken] + accessTokenExpiresAt, ok := session.Fosite.ExpiresAt[fosite.AccessToken] if wantAccessTokenExpiresAt { require.True(t, ok, "expected session to hold expiration time for access token") testutil.RequireTimeInDelta( @@ -1658,8 +1667,8 @@ func requireValidStoredRequest( } // We don't use these, so they should be empty. - require.Empty(t, session.Username) - require.Empty(t, session.Subject) + require.Empty(t, session.Fosite.Username) + require.Empty(t, session.Fosite.Subject) } func requireGarbageCollectTimeInDelta(t *testing.T, tokenString string, typeLabel string, secrets v1.SecretInterface, wantExpirationTime time.Time, deltaTime time.Duration) { @@ -1709,7 +1718,7 @@ func requireValidIDToken( IssuedAt int64 `json:"iat"` RequestedAt int64 `json:"rat"` AuthTime int64 `json:"auth_time"` - Groups string `json:"groups"` + Groups []string `json:"groups"` Username string `json:"username"` } diff --git a/internal/psession/pinniped_session.go b/internal/psession/pinniped_session.go new file mode 100644 index 00000000..334dce06 --- /dev/null +++ b/internal/psession/pinniped_session.go @@ -0,0 +1,99 @@ +// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package psession + +import ( + "time" + + "github.com/mohae/deepcopy" + "github.com/ory/fosite" + "github.com/ory/fosite/handler/openid" + "github.com/ory/fosite/token/jwt" +) + +// PinnipedSession is a session container which includes the fosite standard stuff plus custom Pinniped stuff. +type PinnipedSession struct { + // Delegate most things to the standard fosite OpenID JWT session. + Fosite *openid.DefaultSession `json:"fosite,omitempty"` + + // Custom Pinniped extensions to the session data. + Custom *PinnipedSessionData `json:"custom,omitempty"` +} + +var _ openid.Session = &PinnipedSession{} + +// PinnipedSessionData is the custom session data needed by Pinniped. It should be treated as a union type, +// where the value of ProviderType decides which other fields to use. +type PinnipedSessionData struct { + // The Kubernetes resource UID of the identity provider CRD for the upstream IDP used to start this session. + // This should be validated again upon downstream refresh to make sure that we are not refreshing against + // a different identity provider CRD which just happens to have the same name. + // This implies that when a user deletes an identity provider CRD, then the sessions that were started + // using that identity provider will not be able to perform any more downstream refreshes. + ProviderUID string `json:"providerUID"` + + // The Kubernetes resource name of the identity provider CRD for the upstream IDP used to start this session. + // Used during a downstream refresh to decide which upstream to refresh. + // Also used to decide which of the pointer types below should be used. + ProviderName string `json:"providerName"` + + // The type of the identity provider for the upstream IDP used to start this session. + // Used during a downstream refresh to decide which upstream to refresh. + ProviderType string `json:"providerType"` + + // Only used when ProviderType == "oidc". + OIDC *OIDCSessionData `json:"oidc,omitempty"` +} + +// OIDCSessionData is the additional data needed by Pinniped when the upstream IDP is an OIDC provider. +type OIDCSessionData struct { + UpstreamRefreshToken string `json:"upstreamRefreshToken"` +} + +// NewPinnipedSession returns a new empty session. +func NewPinnipedSession() *PinnipedSession { + return &PinnipedSession{ + Fosite: &openid.DefaultSession{ + Claims: &jwt.IDTokenClaims{}, + Headers: &jwt.Headers{}, + }, + Custom: &PinnipedSessionData{}, + } +} + +func (s *PinnipedSession) Clone() fosite.Session { + // Implementation copied from openid.DefaultSession's clone method. + if s == nil { + return nil + } + return deepcopy.Copy(s).(fosite.Session) +} + +func (s *PinnipedSession) SetExpiresAt(key fosite.TokenType, exp time.Time) { + s.Fosite.SetExpiresAt(key, exp) +} + +func (s *PinnipedSession) GetExpiresAt(key fosite.TokenType) time.Time { + return s.Fosite.GetExpiresAt(key) +} + +func (s *PinnipedSession) GetUsername() string { + return s.Fosite.GetUsername() +} + +func (s *PinnipedSession) SetSubject(subject string) { + s.Fosite.SetSubject(subject) +} + +func (s *PinnipedSession) GetSubject() string { + return s.Fosite.GetSubject() +} + +func (s *PinnipedSession) IDTokenHeaders() *jwt.Headers { + return s.Fosite.IDTokenHeaders() +} + +func (s *PinnipedSession) IDTokenClaims() *jwt.IDTokenClaims { + return s.Fosite.IDTokenClaims() +} diff --git a/internal/testutil/oidctestutil/oidctestutil.go b/internal/testutil/oidctestutil/oidctestutil.go index 5666e610..5ba9512a 100644 --- a/internal/testutil/oidctestutil/oidctestutil.go +++ b/internal/testutil/oidctestutil/oidctestutil.go @@ -16,7 +16,6 @@ import ( coreosoidc "github.com/coreos/go-oidc/v3/oidc" "github.com/ory/fosite" - "github.com/ory/fosite/handler/openid" "github.com/stretchr/testify/require" "golang.org/x/oauth2" "gopkg.in/square/go-jose.v2" @@ -31,6 +30,7 @@ import ( pkce2 "go.pinniped.dev/internal/fositestorage/pkce" "go.pinniped.dev/internal/fositestoragei" "go.pinniped.dev/internal/oidc/provider" + "go.pinniped.dev/internal/psession" "go.pinniped.dev/internal/testutil" "go.pinniped.dev/pkg/oidcclient/nonce" "go.pinniped.dev/pkg/oidcclient/oidctypes" @@ -563,7 +563,7 @@ func validateAuthcodeStorage( wantDownstreamRequestedScopes []string, wantDownstreamClientID string, wantDownstreamRedirectURI string, -) (*fosite.Request, *openid.DefaultSession) { +) (*fosite.Request, *psession.PinnipedSession) { t.Helper() const ( @@ -591,16 +591,16 @@ func validateAuthcodeStorage( testutil.RequireTimeInDelta(t, time.Now(), storedRequestFromAuthcode.RequestedAt, timeComparisonFudgeFactor) // We're not using these fields yet, so confirm that we did not set them (for now). - require.Empty(t, storedSessionFromAuthcode.Subject) - require.Empty(t, storedSessionFromAuthcode.Username) - require.Empty(t, storedSessionFromAuthcode.Headers) + require.Empty(t, storedSessionFromAuthcode.Fosite.Subject) + require.Empty(t, storedSessionFromAuthcode.Fosite.Username) + require.Empty(t, storedSessionFromAuthcode.Fosite.Headers) // The authcode that we are issuing should be good for the length of time that we declare in the fosite config. - testutil.RequireTimeInDelta(t, time.Now().Add(authCodeExpirationSeconds*time.Second), storedSessionFromAuthcode.ExpiresAt[fosite.AuthorizeCode], timeComparisonFudgeFactor) - require.Len(t, storedSessionFromAuthcode.ExpiresAt, 1) + testutil.RequireTimeInDelta(t, time.Now().Add(authCodeExpirationSeconds*time.Second), storedSessionFromAuthcode.Fosite.ExpiresAt[fosite.AuthorizeCode], timeComparisonFudgeFactor) + require.Len(t, storedSessionFromAuthcode.Fosite.ExpiresAt, 1) // Now confirm the ID token claims. - actualClaims := storedSessionFromAuthcode.Claims + actualClaims := storedSessionFromAuthcode.Fosite.Claims // Check the user's identity, which are put into the downstream ID token's subject, username and groups claims. require.Equal(t, wantDownstreamIDTokenSubject, actualClaims.Subject) @@ -642,7 +642,7 @@ func validatePKCEStorage( oauthStore fositestoragei.AllFositeStorage, storeKey string, storedRequestFromAuthcode *fosite.Request, - storedSessionFromAuthcode *openid.DefaultSession, + storedSessionFromAuthcode *psession.PinnipedSession, wantDownstreamPKCEChallenge, wantDownstreamPKCEChallengeMethod string, ) { t.Helper() @@ -667,7 +667,7 @@ func validateIDSessionStorage( oauthStore fositestoragei.AllFositeStorage, storeKey string, storedRequestFromAuthcode *fosite.Request, - storedSessionFromAuthcode *openid.DefaultSession, + storedSessionFromAuthcode *psession.PinnipedSession, wantDownstreamNonce string, ) { t.Helper() @@ -686,13 +686,13 @@ func validateIDSessionStorage( require.Equal(t, wantDownstreamNonce, storedRequestFromIDSession.Form.Get("nonce")) } -func castStoredAuthorizeRequest(t *testing.T, storedAuthorizeRequest fosite.Requester) (*fosite.Request, *openid.DefaultSession) { +func castStoredAuthorizeRequest(t *testing.T, storedAuthorizeRequest fosite.Requester) (*fosite.Request, *psession.PinnipedSession) { t.Helper() storedRequest, ok := storedAuthorizeRequest.(*fosite.Request) require.Truef(t, ok, "could not cast %T to %T", storedAuthorizeRequest, &fosite.Request{}) - storedSession, ok := storedAuthorizeRequest.GetSession().(*openid.DefaultSession) - require.Truef(t, ok, "could not cast %T to %T", storedAuthorizeRequest.GetSession(), &openid.DefaultSession{}) + storedSession, ok := storedAuthorizeRequest.GetSession().(*psession.PinnipedSession) + require.Truef(t, ok, "could not cast %T to %T", storedAuthorizeRequest.GetSession(), &psession.PinnipedSession{}) return storedRequest, storedSession } diff --git a/internal/testutil/psession.go b/internal/testutil/psession.go new file mode 100644 index 00000000..d6f4bcc8 --- /dev/null +++ b/internal/testutil/psession.go @@ -0,0 +1,43 @@ +// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package testutil + +import ( + "testing" + + "github.com/ory/fosite/handler/openid" + v1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes/fake" + testing2 "k8s.io/client-go/testing" + + "go.pinniped.dev/internal/psession" +) + +func NewFakePinnipedSession() *psession.PinnipedSession { + return &psession.PinnipedSession{ + Fosite: &openid.DefaultSession{ + Claims: nil, + Headers: nil, + ExpiresAt: nil, + Username: "snorlax", + Subject: "panda", + }, + Custom: &psession.PinnipedSessionData{ + ProviderUID: "fake-provider-uid", + ProviderType: "fake-provider-type", + ProviderName: "fake-provider-name", + OIDC: &psession.OIDCSessionData{ + UpstreamRefreshToken: "fake-upstream-refresh-token", + }, + }, + } +} + +func LogActualJSONFromCreateAction(t *testing.T, client *fake.Clientset, actionIndex int) { + t.Log("actual value of CreateAction secret data", string(client.Actions()[actionIndex].(testing2.CreateActionImpl).Object.(*v1.Secret).Data["pinniped-storage-data"])) +} + +func LogActualJSONFromUpdateAction(t *testing.T, client *fake.Clientset, actionIndex int) { + t.Log("actual value of UpdateAction secret data", string(client.Actions()[actionIndex].(testing2.UpdateActionImpl).Object.(*v1.Secret).Data["pinniped-storage-data"])) +}