Supervisor should emit a warning when access token lifetime is too short
This commit is contained in:
parent
284ce00aef
commit
b0ea7063c7
@ -53,7 +53,7 @@ func TestAccessTokenStorage(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`),
|
"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","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`),
|
||||||
"pinniped-storage-version": []byte("1"),
|
"pinniped-storage-version": []byte("1"),
|
||||||
},
|
},
|
||||||
Type: "storage.pinniped.dev/access-token",
|
Type: "storage.pinniped.dev/access-token",
|
||||||
@ -122,7 +122,7 @@ func TestAccessTokenStorageRevocation(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`),
|
"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","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`),
|
||||||
"pinniped-storage-version": []byte("1"),
|
"pinniped-storage-version": []byte("1"),
|
||||||
},
|
},
|
||||||
Type: "storage.pinniped.dev/access-token",
|
Type: "storage.pinniped.dev/access-token",
|
||||||
|
@ -369,36 +369,41 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
|
|||||||
"providerUID": "Ĝ眧Ĭ",
|
"providerUID": "Ĝ眧Ĭ",
|
||||||
"providerName": "ʼn2ƋŢ觛ǂ焺nŐǛ",
|
"providerName": "ʼn2ƋŢ觛ǂ焺nŐǛ",
|
||||||
"providerType": "ɥ闣ʬ橳(ý綃ʃʚƟ覣k眐4",
|
"providerType": "ɥ闣ʬ橳(ý綃ʃʚƟ覣k眐4",
|
||||||
|
"warnings": [
|
||||||
|
"掘ʃƸ澺淗a紽ǒ|鰽ŋ猊",
|
||||||
|
"毇妬\u003e6鉢緋uƴŤȱʀļÂ?"
|
||||||
|
],
|
||||||
"oidc": {
|
"oidc": {
|
||||||
"upstreamRefreshToken": "tC嵽痊w",
|
"upstreamRefreshToken": "\u003cƬb",
|
||||||
"upstreamAccessToken": "a紽ǒ|鰽ŋ猊I",
|
"upstreamAccessToken": "犘c钡ɏȫ",
|
||||||
"upstreamSubject": "妬\u003e6鉢緋uƴŤȱʀ",
|
"upstreamSubject": "鬌",
|
||||||
"upstreamIssuer": ":設虝27就伒犘c"
|
"upstreamIssuer": "%OpKȱ藚ɏ¬Ê蒭堜"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
"userDN": "ɏȫ齁š%Op",
|
"userDN": "ȗ韚ʫ繕ȫ碰+",
|
||||||
"extraRefreshAttributes": {
|
"extraRefreshAttributes": {
|
||||||
"T妼É4İ\u003e×1": "ʥ笿0D",
|
"+î艔垎0": "ĝ",
|
||||||
"÷驣7Ʀ澉1æɽ誮": "ʫ繕ȫ",
|
"4İ": "墀jMʥ",
|
||||||
"ŚB碠k9": "i磊ůď逳鞪?3)藵睋邔\u0026Ű"
|
"k9帴": "磊ůď逳鞪?3)藵睋邔\u0026Ű惫蜀Ģ"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"activedirectory": {
|
"activedirectory": {
|
||||||
"userDN": "s",
|
"userDN": "%Ä摱ìÓȐĨf跞@)¿,ɭS隑i",
|
||||||
"extraRefreshAttributes": {
|
"extraRefreshAttributes": {
|
||||||
"ƉǢIȽ齤士bEǎ儯惝IozŁ5rƖ螼": "偶宾儮猷V麹Œ颛Ė應,Ɣ鬅X¤"
|
" 皦pSǬŝ社Vƅȭǝ*擦28Dž": "vư",
|
||||||
|
"艱iYn面@yȝƋ鬯犦獢9c5¤.岵": "浛a齙\\蹼偦歛"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"requestedAudience": [
|
"requestedAudience": [
|
||||||
"tO灞浛a齙\\蹼偦歛ơ",
|
"置b",
|
||||||
"皦pSǬŝ社Vƅȭǝ*"
|
"筫MN\u0026錝D肁Ŷɽ蔒PR}Ųʓl{"
|
||||||
],
|
],
|
||||||
"grantedAudience": [
|
"grantedAudience": [
|
||||||
"ĝ\"zvưã置bņ抰蛖a³2ʫ",
|
"jÃ轘屔挝",
|
||||||
"Ŷɽ蔒PR}Ųʓl{鼐jÃ轘屔挝",
|
"Œų崓ļ憽-蹐È_¸]fś",
|
||||||
"Œų崓ļ憽-蹐È_¸]fś"
|
"ɵʮGɃɫ囤1+,Ȳ"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"version": "2"
|
"version": "2"
|
||||||
|
@ -65,7 +65,7 @@ func TestAuthorizationCodeStorage(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"pinniped-storage-data": []byte(`{"active":true,"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`),
|
"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","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`),
|
||||||
"pinniped-storage-version": []byte("1"),
|
"pinniped-storage-version": []byte("1"),
|
||||||
},
|
},
|
||||||
Type: "storage.pinniped.dev/authcode",
|
Type: "storage.pinniped.dev/authcode",
|
||||||
@ -84,7 +84,7 @@ func TestAuthorizationCodeStorage(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"pinniped-storage-data": []byte(`{"active":false,"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`),
|
"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","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`),
|
||||||
"pinniped-storage-version": []byte("1"),
|
"pinniped-storage-version": []byte("1"),
|
||||||
},
|
},
|
||||||
Type: "storage.pinniped.dev/authcode",
|
Type: "storage.pinniped.dev/authcode",
|
||||||
|
@ -52,7 +52,7 @@ func TestOpenIdConnectStorage(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`),
|
"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","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`),
|
||||||
"pinniped-storage-version": []byte("1"),
|
"pinniped-storage-version": []byte("1"),
|
||||||
},
|
},
|
||||||
Type: "storage.pinniped.dev/oidc",
|
Type: "storage.pinniped.dev/oidc",
|
||||||
|
@ -52,7 +52,7 @@ func TestPKCEStorage(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`),
|
"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","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`),
|
||||||
"pinniped-storage-version": []byte("1"),
|
"pinniped-storage-version": []byte("1"),
|
||||||
},
|
},
|
||||||
Type: "storage.pinniped.dev/pkce",
|
Type: "storage.pinniped.dev/pkce",
|
||||||
|
@ -52,7 +52,7 @@ func TestRefreshTokenStorage(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`),
|
"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","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`),
|
||||||
"pinniped-storage-version": []byte("1"),
|
"pinniped-storage-version": []byte("1"),
|
||||||
},
|
},
|
||||||
Type: "storage.pinniped.dev/refresh-token",
|
Type: "storage.pinniped.dev/refresh-token",
|
||||||
@ -122,7 +122,7 @@ func TestRefreshTokenStorageRevocation(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"custom":{"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`),
|
"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","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"2"}`),
|
||||||
"pinniped-storage-version": []byte("1"),
|
"pinniped-storage-version": []byte("1"),
|
||||||
},
|
},
|
||||||
Type: "storage.pinniped.dev/refresh-token",
|
Type: "storage.pinniped.dev/refresh-token",
|
||||||
|
@ -14,11 +14,13 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/securecookie"
|
"github.com/gorilla/securecookie"
|
||||||
"github.com/ory/fosite"
|
"github.com/ory/fosite"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
@ -893,7 +895,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "OIDC password grant happy path when upstream IDP returned empty refresh token but it did return an access token and has a userinfo endpoint",
|
name: "OIDC password grant happy path when upstream IDP returned empty refresh token but it did return an access token and has a userinfo endpoint",
|
||||||
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(passwordGrantUpstreamOIDCIdentityProviderBuilder().WithEmptyRefreshToken().WithAccessToken(oidcUpstreamAccessToken).WithUserInfoURL().Build()),
|
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(passwordGrantUpstreamOIDCIdentityProviderBuilder().WithEmptyRefreshToken().WithAccessToken(oidcUpstreamAccessToken, metav1.NewTime(time.Now().Add(9*time.Hour))).WithUserInfoURL().Build()),
|
||||||
method: http.MethodGet,
|
method: http.MethodGet,
|
||||||
path: happyGetRequestPath,
|
path: happyGetRequestPath,
|
||||||
customUsernameHeader: pointer.StringPtr(oidcUpstreamUsername),
|
customUsernameHeader: pointer.StringPtr(oidcUpstreamUsername),
|
||||||
@ -913,9 +915,41 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
|
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
|
||||||
wantDownstreamCustomSessionData: expectedHappyOIDCPasswordGrantCustomSessionWithAccessToken,
|
wantDownstreamCustomSessionData: expectedHappyOIDCPasswordGrantCustomSessionWithAccessToken,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "OIDC password grant happy path when upstream IDP returned empty refresh token and an access token that has a short lifetime",
|
||||||
|
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(passwordGrantUpstreamOIDCIdentityProviderBuilder().WithEmptyRefreshToken().WithAccessToken(oidcUpstreamAccessToken, metav1.NewTime(time.Now().Add(1*time.Hour))).WithUserInfoURL().Build()),
|
||||||
|
method: http.MethodGet,
|
||||||
|
path: happyGetRequestPath,
|
||||||
|
customUsernameHeader: pointer.StringPtr(oidcUpstreamUsername),
|
||||||
|
customPasswordHeader: pointer.StringPtr(oidcUpstreamPassword),
|
||||||
|
wantPasswordGrantCall: happyUpstreamPasswordGrantMockExpectation,
|
||||||
|
wantStatus: http.StatusFound,
|
||||||
|
wantContentType: htmlContentType,
|
||||||
|
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||||
|
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||||
|
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||||
|
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||||
|
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||||
|
wantDownstreamRedirectURI: downstreamRedirectURI,
|
||||||
|
wantDownstreamGrantedScopes: happyDownstreamScopesGranted,
|
||||||
|
wantDownstreamNonce: downstreamNonce,
|
||||||
|
wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
|
||||||
|
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
|
||||||
|
wantDownstreamCustomSessionData: &psession.CustomSessionData{
|
||||||
|
ProviderUID: oidcPasswordGrantUpstreamResourceUID,
|
||||||
|
ProviderName: oidcPasswordGrantUpstreamName,
|
||||||
|
ProviderType: psession.ProviderTypeOIDC,
|
||||||
|
Warnings: []string{"Access token from identity provider has lifetime of less than 3 hours. Expect frequent prompts to log in."},
|
||||||
|
OIDC: &psession.OIDCSessionData{
|
||||||
|
UpstreamAccessToken: oidcUpstreamAccessToken,
|
||||||
|
UpstreamSubject: oidcUpstreamSubject,
|
||||||
|
UpstreamIssuer: oidcUpstreamIssuer,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "OIDC password grant happy path when upstream IDP did not return a refresh token but it did return an access token and has a userinfo endpoint",
|
name: "OIDC password grant happy path when upstream IDP did not return a refresh token but it did return an access token and has a userinfo endpoint",
|
||||||
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(passwordGrantUpstreamOIDCIdentityProviderBuilder().WithoutRefreshToken().WithAccessToken(oidcUpstreamAccessToken).WithUserInfoURL().Build()),
|
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(passwordGrantUpstreamOIDCIdentityProviderBuilder().WithoutRefreshToken().WithAccessToken(oidcUpstreamAccessToken, metav1.NewTime(time.Now().Add(9*time.Hour))).WithUserInfoURL().Build()),
|
||||||
method: http.MethodGet,
|
method: http.MethodGet,
|
||||||
path: happyGetRequestPath,
|
path: happyGetRequestPath,
|
||||||
customUsernameHeader: pointer.StringPtr(oidcUpstreamUsername),
|
customUsernameHeader: pointer.StringPtr(oidcUpstreamUsername),
|
||||||
@ -1078,7 +1112,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "password grant returns an error when upstream IDP returns no refresh token with an access token but has no userinfo endpoint",
|
name: "password grant returns an error when upstream IDP returns no refresh token with an access token but has no userinfo endpoint",
|
||||||
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(passwordGrantUpstreamOIDCIdentityProviderBuilder().WithoutRefreshToken().WithAccessToken(oidcUpstreamAccessToken).WithoutUserInfoURL().Build()),
|
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(passwordGrantUpstreamOIDCIdentityProviderBuilder().WithoutRefreshToken().WithAccessToken(oidcUpstreamAccessToken, metav1.NewTime(time.Now().Add(9*time.Hour))).WithoutUserInfoURL().Build()),
|
||||||
method: http.MethodGet,
|
method: http.MethodGet,
|
||||||
path: happyGetRequestPath,
|
path: happyGetRequestPath,
|
||||||
customUsernameHeader: pointer.StringPtr(oidcUpstreamUsername),
|
customUsernameHeader: pointer.StringPtr(oidcUpstreamUsername),
|
||||||
@ -1091,7 +1125,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "password grant returns an error when upstream IDP returns empty refresh token with an access token but has no userinfo endpoint",
|
name: "password grant returns an error when upstream IDP returns empty refresh token with an access token but has no userinfo endpoint",
|
||||||
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(passwordGrantUpstreamOIDCIdentityProviderBuilder().WithEmptyRefreshToken().WithAccessToken(oidcUpstreamAccessToken).WithoutUserInfoURL().Build()),
|
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(passwordGrantUpstreamOIDCIdentityProviderBuilder().WithEmptyRefreshToken().WithAccessToken(oidcUpstreamAccessToken, metav1.NewTime(time.Now().Add(9*time.Hour))).WithoutUserInfoURL().Build()),
|
||||||
method: http.MethodGet,
|
method: http.MethodGet,
|
||||||
path: happyGetRequestPath,
|
path: happyGetRequestPath,
|
||||||
customUsernameHeader: pointer.StringPtr(oidcUpstreamUsername),
|
customUsernameHeader: pointer.StringPtr(oidcUpstreamUsername),
|
||||||
|
@ -11,9 +11,11 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/securecookie"
|
"github.com/gorilla/securecookie"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
|
|
||||||
"go.pinniped.dev/internal/oidc"
|
"go.pinniped.dev/internal/oidc"
|
||||||
@ -213,7 +215,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "GET with authcode exchange that returns an access token but no refresh token when there is a userinfo endpoint returns 303 to downstream client callback with its state and code",
|
name: "GET with authcode exchange that returns an access token but no refresh token when there is a userinfo endpoint returns 303 to downstream client callback with its state and code",
|
||||||
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().WithEmptyRefreshToken().WithAccessToken(oidcUpstreamAccessToken).WithUserInfoURL().Build()),
|
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().WithEmptyRefreshToken().WithAccessToken(oidcUpstreamAccessToken, metav1.NewTime(time.Now().Add(9*time.Hour))).WithUserInfoURL().Build()),
|
||||||
method: http.MethodGet,
|
method: http.MethodGet,
|
||||||
path: newRequestPath().WithState(happyState).String(),
|
path: newRequestPath().WithState(happyState).String(),
|
||||||
csrfCookie: happyCSRFCookie,
|
csrfCookie: happyCSRFCookie,
|
||||||
@ -234,6 +236,39 @@ func TestCallbackEndpoint(t *testing.T) {
|
|||||||
args: happyExchangeAndValidateTokensArgs,
|
args: happyExchangeAndValidateTokensArgs,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "GET with authcode exchange that returns an access token but no refresh token but has a short token lifetime which is stored as a warning in the session",
|
||||||
|
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().WithEmptyRefreshToken().WithAccessToken(oidcUpstreamAccessToken, metav1.NewTime(time.Now().Add(1*time.Hour))).WithUserInfoURL().Build()),
|
||||||
|
method: http.MethodGet,
|
||||||
|
path: newRequestPath().WithState(happyState).String(),
|
||||||
|
csrfCookie: happyCSRFCookie,
|
||||||
|
wantStatus: http.StatusSeeOther,
|
||||||
|
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||||
|
wantBody: "",
|
||||||
|
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||||
|
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||||
|
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||||
|
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||||
|
wantDownstreamGrantedScopes: happyDownstreamScopesGranted,
|
||||||
|
wantDownstreamNonce: downstreamNonce,
|
||||||
|
wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
|
||||||
|
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
|
||||||
|
wantDownstreamCustomSessionData: &psession.CustomSessionData{
|
||||||
|
ProviderUID: happyUpstreamIDPResourceUID,
|
||||||
|
ProviderName: happyUpstreamIDPName,
|
||||||
|
ProviderType: psession.ProviderTypeOIDC,
|
||||||
|
Warnings: []string{"Access token from identity provider has lifetime of less than 3 hours. Expect frequent prompts to log in."},
|
||||||
|
OIDC: &psession.OIDCSessionData{
|
||||||
|
UpstreamAccessToken: oidcUpstreamAccessToken,
|
||||||
|
UpstreamIssuer: oidcUpstreamIssuer,
|
||||||
|
UpstreamSubject: oidcUpstreamSubject,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantAuthcodeExchangeCall: &expectedAuthcodeExchange{
|
||||||
|
performedByUpstreamName: happyUpstreamIDPName,
|
||||||
|
args: happyExchangeAndValidateTokensArgs,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "upstream IDP provides no username or group claim configuration, so we use default username claim and skip groups",
|
name: "upstream IDP provides no username or group claim configuration, so we use default username claim and skip groups",
|
||||||
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(
|
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(
|
||||||
@ -358,7 +393,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "return an error when upstream IDP returned no refresh token with an access token when there is no userinfo endpoint",
|
name: "return an error when upstream IDP returned no refresh token with an access token when there is no userinfo endpoint",
|
||||||
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().WithoutRefreshToken().WithAccessToken(oidcUpstreamAccessToken).WithoutUserInfoURL().Build()),
|
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(happyUpstream().WithoutRefreshToken().WithAccessToken(oidcUpstreamAccessToken, metav1.NewTime(time.Now().Add(9*time.Hour))).WithoutUserInfoURL().Build()),
|
||||||
method: http.MethodGet,
|
method: http.MethodGet,
|
||||||
path: newRequestPath().WithState(happyState).String(),
|
path: newRequestPath().WithState(happyState).String(),
|
||||||
csrfCookie: happyCSRFCookie,
|
csrfCookie: happyCSRFCookie,
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/ory/fosite"
|
"github.com/ory/fosite"
|
||||||
"github.com/ory/fosite/handler/openid"
|
"github.com/ory/fosite/handler/openid"
|
||||||
"github.com/ory/fosite/token/jwt"
|
"github.com/ory/fosite/token/jwt"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"go.pinniped.dev/internal/constable"
|
"go.pinniped.dev/internal/constable"
|
||||||
"go.pinniped.dev/internal/oidc"
|
"go.pinniped.dev/internal/oidc"
|
||||||
@ -101,6 +102,13 @@ func MakeDownstreamOIDCCustomSessionData(oidcUpstream provider.UpstreamOIDCIdent
|
|||||||
}
|
}
|
||||||
plog.Info("refresh token not returned by upstream provider during login, using access token instead. "+pleaseCheck, logKV...)
|
plog.Info("refresh token not returned by upstream provider during login, using access token instead. "+pleaseCheck, logKV...)
|
||||||
customSessionData.OIDC.UpstreamAccessToken = token.AccessToken.Token
|
customSessionData.OIDC.UpstreamAccessToken = token.AccessToken.Token
|
||||||
|
// When we are in a flow where we will be performing access token based refresh, issue a warning to the client if the access
|
||||||
|
// token lifetime is very short, since that would mean that the user's session is very short.
|
||||||
|
// The warnings are stored here and will be processed by the token handler.
|
||||||
|
threeHoursFromNow := metav1.NewTime(time.Now().Add(3 * time.Hour))
|
||||||
|
if !token.AccessToken.Expiry.IsZero() && token.AccessToken.Expiry.Before(&threeHoursFromNow) {
|
||||||
|
customSessionData.Warnings = append(customSessionData.Warnings, "Access token from identity provider has lifetime of less than 3 hours. Expect frequent prompts to log in.")
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
plog.Warning("refresh token and access token not returned by upstream provider during login. "+pleaseCheck, logKV...)
|
plog.Warning("refresh token and access token not returned by upstream provider during login. "+pleaseCheck, logKV...)
|
||||||
return nil, errors.New("neither access token nor refresh token returned by upstream provider")
|
return nil, errors.New("neither access token nor refresh token returned by upstream provider")
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/ory/fosite"
|
"github.com/ory/fosite"
|
||||||
"github.com/ory/x/errorsx"
|
"github.com/ory/x/errorsx"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
"k8s.io/apiserver/pkg/warning"
|
||||||
|
|
||||||
"go.pinniped.dev/internal/httputil/httperr"
|
"go.pinniped.dev/internal/httputil/httperr"
|
||||||
"go.pinniped.dev/internal/oidc"
|
"go.pinniped.dev/internal/oidc"
|
||||||
@ -63,6 +64,14 @@ func NewHandler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When we are in the authorization code flow, check if we have any warnings that previous handlers want us
|
||||||
|
// to send to the client to be printed on the CLI.
|
||||||
|
if accessRequest.GetGrantTypes().ExactOne("authorization_code") {
|
||||||
|
for _, warningText := range session.Custom.Warnings {
|
||||||
|
warning.AddWarning(r.Context(), "", warningText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
accessResponse, err := oauthHelper.NewAccessResponse(r.Context(), accessRequest)
|
accessResponse, err := oauthHelper.NewAccessResponse(r.Context(), accessRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
plog.Info("token response error", oidc.FositeErrorForLog(err)...)
|
plog.Info("token response error", oidc.FositeErrorForLog(err)...)
|
||||||
|
@ -43,6 +43,10 @@ type CustomSessionData struct {
|
|||||||
// Used during a downstream refresh to decide which upstream to refresh.
|
// Used during a downstream refresh to decide which upstream to refresh.
|
||||||
ProviderType ProviderType `json:"providerType"`
|
ProviderType ProviderType `json:"providerType"`
|
||||||
|
|
||||||
|
// Warnings that were encountered at some point during login that should be emitted to the client.
|
||||||
|
// These will be RFC 2616-formatted errors with error code 299.
|
||||||
|
Warnings []string `json:"warnings"`
|
||||||
|
|
||||||
// Only used when ProviderType == "oidc".
|
// Only used when ProviderType == "oidc".
|
||||||
OIDC *OIDCSessionData `json:"oidc,omitempty"`
|
OIDC *OIDCSessionData `json:"oidc,omitempty"`
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"gopkg.in/square/go-jose.v2"
|
"gopkg.in/square/go-jose.v2"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
@ -726,8 +727,8 @@ func (u *TestUpstreamOIDCIdentityProviderBuilder) WithoutRefreshToken() *TestUps
|
|||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *TestUpstreamOIDCIdentityProviderBuilder) WithAccessToken(token string) *TestUpstreamOIDCIdentityProviderBuilder {
|
func (u *TestUpstreamOIDCIdentityProviderBuilder) WithAccessToken(token string, expiry metav1.Time) *TestUpstreamOIDCIdentityProviderBuilder {
|
||||||
u.accessToken = &oidctypes.AccessToken{Token: token}
|
u.accessToken = &oidctypes.AccessToken{Token: token, Expiry: expiry}
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
#! Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||||
#! SPDX-License-Identifier: Apache-2.0
|
#! SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
#@ load("@ytt:data", "data")
|
#@ load("@ytt:data", "data")
|
||||||
@ -15,6 +15,8 @@ web:
|
|||||||
https: 0.0.0.0:8443
|
https: 0.0.0.0:8443
|
||||||
tlsCert: /var/certs/dex.pem
|
tlsCert: /var/certs/dex.pem
|
||||||
tlsKey: /var/certs/dex-key.pem
|
tlsKey: /var/certs/dex-key.pem
|
||||||
|
expiry:
|
||||||
|
idTokens: 20m #! this is the lifetime for the id token as well as the access token.
|
||||||
oauth2:
|
oauth2:
|
||||||
skipApprovalScreen: true
|
skipApprovalScreen: true
|
||||||
#! Allow the resource owner password grant, which Dex implements to also return ID tokens.
|
#! Allow the resource owner password grant, which Dex implements to also return ID tokens.
|
||||||
|
Loading…
Reference in New Issue
Block a user