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 <margaretc@vmware.com>
This commit is contained in:
Ryan Richard 2021-10-06 15:28:13 -07:00 committed by Margo Crawford
parent 6e41c10584
commit c6f1d29538
18 changed files with 462 additions and 315 deletions

View File

@ -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{},
},
}
}

View File

@ -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",
@ -87,13 +88,7 @@ func TestAccessTokenStorage(t *testing.T) {
RequestedScope: nil,
GrantedScope: nil,
Form: url.Values{"key": []string{"val"}},
Session: &openid.DefaultSession{
Claims: nil,
Headers: nil,
ExpiresAt: nil,
Username: "snorlax",
Subject: "panda",
},
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",
@ -152,10 +148,7 @@ func TestAccessTokenStorageRevocation(t *testing.T) {
},
},
Form: url.Values{"key": []string{"val"}},
Session: &openid.DefaultSession{
Username: "snorlax",
Subject: "panda",
},
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)

View File

@ -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{},
},
}
}
@ -179,7 +179,7 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
"redirect_uris": [
"ǖ枭kʍ切厦ȳ箦;¥ʊXĝ奨誷傥祩d",
"zŇZ",
"優蒼ĊɌț訫DŽǽeʀO2ƚ&N"
"優蒼ĊɌț訫DŽǽeʀO2ƚ\u0026N"
],
"grant_types": [
"唐W6ɻ橩斚薛ɑƐ"
@ -189,7 +189,7 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
"ǔŭe[u@阽羂ŷ-Ĵ½輢OÅ濲喾H"
],
"scopes": [
"G螩歐湡ƙı唡ɸğƎ&胢輢Ƈĵƚ"
"G螩歐湡ƙı唡ɸğƎ\u0026胢輢Ƈĵƚ"
],
"audience": [
"ě"
@ -234,7 +234,7 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
}
]
},
"token_endpoint_auth_method": "ƿʥǟȒ伉<x¹T鼓c吏",
"token_endpoint_auth_method": "ƿʥǟȒ伉\u003cx¹T鼓c吏",
"request_uris": [
"Ć捘j]=谅ʑɑɮ$Ól4Ȟ",
",Q7钎漡臧n"
@ -252,78 +252,89 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
"form": {
"褾攚ŝlĆ厦駳骪l拁乖¡J¿Ƈ妔": [
"懧¥ɂĵ~Čyʊ恀c\"NJřðȿ/",
"裢?霃谥vƘ:ƿ/濔Aʉ<",
"裢?霃谥vƘ:ƿ/濔Aʉ\u003c",
"ȭ$奍囀Dž悷鵱民撲ʓeŘ嬀j¤"
],
"诞": [
"狲N<Cq罉ZPſĝEK郊©l",
"狲N\u003cCq罉ZPſĝEK郊©l",
"餚LJ/ɷȑ潠[ĝU噤'pX ",
"Y妶ǵ!ȁu狍ɶȳsčɦƦ诱"
]
},
"session": {
"fosite": {
"Claims": {
"JTI": "攬林Ñz焁糳¿o>Q鱙翑ȲŻ",
"Issuer": "锰劝旣樎Ȱ鍌#ȳńƩŴȭ",
"Subject": "绝TFNJĆw宵ɚeY48珎²",
"JTI": "u妔隤ʑƍš駎竪0ɔ闏À1",
"Issuer": "麤ã桒嘞\\摗Ǘū稖咾鎅ǸÖ绝TF",
"Subject": "巽ēđų蓼tùZ蛆鬣a\"ÙǞ0觢Û±",
"Audience": [
"éã越|j¦鲶H股ƲLŋZ-{5£踉4"
"H股ƲL",
"肟v\u0026đehpƧ",
"5^驜Ŗ~ů崧軒q腟u尿"
],
"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",
"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": {
"PƢ曰": {
"ĸŴB岺Ð嫹Sx镯荫ő": [
843216989
],
"疂ư墫ɓ": {
"\\BRë_g\"ʎ啴SƇMǃļ": {
"ʦ4": false
},
"鶡萷ɵ啜s攦": null
"#\u0026PƢ曰l騌蘙螤\\阏Đ镴Ƥm蔻ǭ\\鿞": 1677215584,
"Y\u0026鶡萷ɵ啜s攦Ɩïdnǔ": {
",t猟i\u0026\u0026Q@ǤǟǗǪ飘ȱF?Ƈ": {
"~劰û橸ɽ銐ƭ?}H": null,
"癑勦e骲v0H晦XŘO溪V蔓": {
"碼Ǫ": false
}
},
"曓蓳n匟鯘磹*金爃鶴滱ůĮǐ_c3#": 2520197933
"钻煐ɨəÅDČ{Ȩʦ4撎": [
3684968178
]
}
}
},
"Headers": {
"Extra": {
"寱ĊƑ÷Ƒ螞费Ďğ~劰û橸ɽ銐ƭ?}": {
"ȜʁɁ;Bd謺錳4帳ŅǃĊd": {
"翢砜Fȏl鐉诳DT=3骜": {
"ų厷ɁOƪ穋嶿鳈恱va|载ǰɱ汶C": false
"ĊdŘ鸨EJ毕懴řĬń戹": {
"诳DT=3骜Ǹ,": {
"\u003e": {
"ǰ": false
},
"鸨EJ毕懴řĬń戹%c": null
"ɁOƪ穋嶿鳈恱va": null
},
"室癑勦e骲v0H晦XŘO溪V蔓Ȍ+~ē": [
954647573
"豑觳翢砜Fȏl": [
927958776
]
},
"麈ƵDǀ\\郂üţ垂": 1572524915
"埅ȜʁɁ;Bd謺錳4帳Ņ": 388005986
}
},
"ExpiresAt": {
"'=ĸ闒NȢȰ.醋fʜ": "2031-10-18T22:07:34.950803105Z",
"ɦüHêQ仏1őƖ2Ė暮唍ǞʜƢú4": "2049-05-13T15:27:20.968432454Z"
"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丬鎒ơ娻}ɼƟȥE",
"Subject": "龳ǽÙ龦O亾EW莛8嘶×姮c恭企"
"Username": "韁臯氃妪婝rȤ\"h丬鎒ơ娻}ɼƟ",
"Subject": "闺髉龳ǽÙ龦O亾EW莛8嘶×"
},
"custom": {
"providerUID": "鵮碡ʯiŬŽ非Ĝ眧Ĭ葜SŦ餧Ĭ倏4",
"providerName": "nŐǛ3",
"providerType": "闣ʬ橳(ý綃ʃʚƟ覣k眐4Ĉt",
"oidc": {
"upstreamRefreshToken": "嵽痊w©Ź榨Q|ôɵt毇妬"
}
}
},
"requestedAudience": [
"邖ɐ5檄¬",
"Ĭ葜SŦ餧Ĭ倏4ĵ嶼仒篻ɥ闣ʬ橳(ý綃"
"6鉢緋uƴŤȱʀļÂ?墖\u003cƬb獭潜Ʃ饾"
],
"grantedAudience": [
"ʚƟ覣k眐4ĈtC嵽痊w©Ź榨Q|ô",
"猊Ia瓕巈環_ɑƍ蛊ʚ£:設虝2"
"|鬌R蜚蠣麹概÷驣7Ʀ澉1æɽ誮rʨ鷞"
]
},
"version": "1"
}`
"version": "2"
}`

View File

@ -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",
@ -116,13 +117,7 @@ func TestAuthorizationCodeStorage(t *testing.T) {
RequestedScope: nil,
GrantedScope: nil,
Form: url.Values{"key": []string{"val"}},
Session: &openid.DefaultSession{
Claims: nil,
Headers: nil,
ExpiresAt: nil,
Username: "snorlax",
Subject: "panda",
},
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)

View File

@ -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
}

View File

@ -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{},
},
}
}

View File

@ -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",
@ -87,13 +89,7 @@ func TestOpenIdConnectStorage(t *testing.T) {
RequestedScope: nil,
GrantedScope: nil,
Form: url.Values{"key": []string{"val"}},
Session: &openid.DefaultSession{
Claims: nil,
Headers: nil,
ExpiresAt: nil,
Username: "snorlax",
Subject: "panda",
},
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)

View File

@ -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{},
},
}
}

View File

@ -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",
@ -88,13 +89,7 @@ func TestPKCEStorage(t *testing.T) {
RequestedScope: nil,
GrantedScope: nil,
Form: url.Values{"key": []string{"val"}},
Session: &openid.DefaultSession{
Claims: nil,
Headers: nil,
ExpiresAt: nil,
Username: "snorlax",
Subject: "panda",
},
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)

View File

@ -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{},
},
}
}

View File

@ -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",
@ -87,13 +88,7 @@ func TestRefreshTokenStorage(t *testing.T) {
RequestedScope: nil,
GrantedScope: nil,
Form: url.Values{"key": []string{"val"}},
Session: &openid.DefaultSession{
Claims: nil,
Headers: nil,
ExpiresAt: nil,
Username: "snorlax",
Subject: "panda",
},
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",
@ -152,10 +148,7 @@ func TestRefreshTokenStorageRevocation(t *testing.T) {
},
},
Form: url.Values{"key": []string{"val"}},
Session: &openid.DefaultSession{
Username: "snorlax",
Subject: "panda",
},
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)

View File

@ -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,13 +174,15 @@ func handleAuthRequestForOIDCUpstreamAuthcodeGrant(
}
now := time.Now()
_, err := oauthHelper.NewAuthorizeResponse(r.Context(), authorizeRequester, &openid.DefaultSession{
_, 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 {
return writeAuthorizeError(w, oauthHelper, authorizeRequester, err)

View File

@ -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{
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,
}

View File

@ -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)

View File

@ -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,7 +1360,8 @@ 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{
session := &psession.PinnipedSession{
Fosite: &openid.DefaultSession{
Claims: &jwt.IDTokenClaims{
Subject: goodSubject,
RequestedAt: goodRequestedAtTime,
@ -1370,6 +1373,12 @@ func simulateAuthEndpointHavingAlreadyRun(t *testing.T, authRequest *http.Reques
},
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",
},
},
}
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"`
}

View File

@ -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()
}

View File

@ -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
}

View File

@ -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"]))
}