Merge pull request #262 from vmware-tanzu/token-refresh
Support for the refresh grant in the supervisor's token endpoint
This commit is contained in:
commit
4d82ec1283
@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
coreosoidc "github.com/coreos/go-oidc"
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/fosite/handler/openid"
|
||||
"github.com/ory/fosite/token/jwt"
|
||||
@ -57,7 +58,10 @@ func NewHandler(
|
||||
}
|
||||
|
||||
// Grant the openid scope (for now) if they asked for it so that `NewAuthorizeResponse` will perform its OIDC validations.
|
||||
grantOpenIDScopeIfRequested(authorizeRequester)
|
||||
oidc.GrantScopeIfRequested(authorizeRequester, coreosoidc.ScopeOpenID)
|
||||
// There don't seem to be any validations inside `NewAuthorizeResponse` related to the offline_access scope
|
||||
// at this time, however we will temporarily grant the scope just in case that changes in a future release of fosite.
|
||||
oidc.GrantScopeIfRequested(authorizeRequester, coreosoidc.ScopeOfflineAccess)
|
||||
|
||||
now := time.Now()
|
||||
_, err = oauthHelper.NewAuthorizeResponse(r.Context(), authorizeRequester, &openid.DefaultSession{
|
||||
@ -148,14 +152,6 @@ func readCSRFCookie(r *http.Request, codec oidc.Codec) csrftoken.CSRFToken {
|
||||
return csrfFromCookie
|
||||
}
|
||||
|
||||
func grantOpenIDScopeIfRequested(authorizeRequester fosite.AuthorizeRequester) {
|
||||
for _, scope := range authorizeRequester.GetRequestedScopes() {
|
||||
if scope == "openid" {
|
||||
authorizeRequester.GrantScope(scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func chooseUpstreamIDP(idpListGetter oidc.IDPListGetter) (provider.UpstreamOIDCIdentityProviderI, error) {
|
||||
allUpstreamIDPs := idpListGetter.GetIDPList()
|
||||
if len(allUpstreamIDPs) == 0 {
|
||||
|
@ -119,7 +119,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
||||
Name: "some-idp",
|
||||
ClientID: "some-client-id",
|
||||
AuthorizationURL: *upstreamAuthURL,
|
||||
Scopes: []string{"scope1", "scope2"},
|
||||
Scopes: []string{"scope1", "scope2"}, // the scopes to request when starting the upstream authorization flow
|
||||
}
|
||||
|
||||
// Configure fosite the same way that the production code would, using NullStorage to turn off storage.
|
||||
@ -372,6 +372,26 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
||||
wantUpstreamStateParamInLocationHeader: true,
|
||||
wantBodyStringWithLocationInHref: true,
|
||||
},
|
||||
{
|
||||
name: "happy path when downstream requested scopes include offline_access",
|
||||
issuer: downstreamIssuer,
|
||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
||||
generateCSRF: happyCSRFGenerator,
|
||||
generatePKCE: happyPKCEGenerator,
|
||||
generateNonce: happyNonceGenerator,
|
||||
stateEncoder: happyStateEncoder,
|
||||
cookieEncoder: happyCookieEncoder,
|
||||
method: http.MethodGet,
|
||||
path: modifiedHappyGetRequestPath(map[string]string{"scope": "openid offline_access"}),
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: "text/html; charset=utf-8",
|
||||
wantCSRFValueInCookieHeader: happyCSRF,
|
||||
wantLocationHeader: expectedRedirectLocation(expectedUpstreamStateParam(map[string]string{
|
||||
"scope": "openid offline_access",
|
||||
}, "", "")),
|
||||
wantUpstreamStateParamInLocationHeader: true,
|
||||
wantBodyStringWithLocationInHref: true,
|
||||
},
|
||||
{
|
||||
name: "downstream redirect uri does not match what is configured for client",
|
||||
issuer: downstreamIssuer,
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
coreosoidc "github.com/coreos/go-oidc"
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/fosite/handler/openid"
|
||||
"github.com/ory/fosite/token/jwt"
|
||||
@ -70,8 +71,9 @@ func NewHandler(
|
||||
return httperr.New(http.StatusBadRequest, "error using state downstream auth params")
|
||||
}
|
||||
|
||||
// Grant the openid scope only if it was requested.
|
||||
grantOpenIDScopeIfRequested(authorizeRequester)
|
||||
// Automatically grant the openid and offline_access scopes, but only if they were requested.
|
||||
oidc.GrantScopeIfRequested(authorizeRequester, coreosoidc.ScopeOpenID)
|
||||
oidc.GrantScopeIfRequested(authorizeRequester, coreosoidc.ScopeOfflineAccess)
|
||||
|
||||
token, err := upstreamIDPConfig.ExchangeAuthcodeAndValidateTokens(
|
||||
r.Context(),
|
||||
@ -189,14 +191,6 @@ func readState(r *http.Request, stateDecoder oidc.Decoder) (*oidc.UpstreamStateP
|
||||
return &state, nil
|
||||
}
|
||||
|
||||
func grantOpenIDScopeIfRequested(authorizeRequester fosite.AuthorizeRequester) {
|
||||
for _, scope := range authorizeRequester.GetRequestedScopes() {
|
||||
if scope == "openid" {
|
||||
authorizeRequester.GrantScope(scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getUsernameFromUpstreamIDToken(
|
||||
upstreamIDPConfig provider.UpstreamOIDCIdentityProviderI,
|
||||
idTokenClaims map[string]interface{},
|
||||
|
@ -66,7 +66,8 @@ const (
|
||||
|
||||
var (
|
||||
upstreamGroupMembership = []string{"test-pinniped-group-0", "test-pinniped-group-1"}
|
||||
happyDownstreamScopesRequested = []string{"openid", "profile", "email"}
|
||||
happyDownstreamScopesRequested = []string{"openid"}
|
||||
happyDownstreamScopesGranted = []string{"openid"}
|
||||
|
||||
happyDownstreamRequestParamsQuery = url.Values{
|
||||
"response_type": []string{"code"},
|
||||
@ -127,7 +128,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus int
|
||||
wantBody string
|
||||
wantRedirectLocationRegexp string
|
||||
wantGrantedOpenidScope bool
|
||||
wantDownstreamGrantedScopes []string
|
||||
wantDownstreamIDTokenSubject string
|
||||
wantDownstreamIDTokenGroups []string
|
||||
wantDownstreamRequestedScopes []string
|
||||
@ -145,11 +146,11 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
csrfCookie: happyCSRFCookie,
|
||||
wantStatus: http.StatusFound,
|
||||
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||
wantGrantedOpenidScope: true,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: upstreamUsername,
|
||||
wantDownstreamIDTokenGroups: upstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
wantDownstreamGrantedScopes: happyDownstreamScopesGranted,
|
||||
wantDownstreamNonce: downstreamNonce,
|
||||
wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
|
||||
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
|
||||
@ -163,11 +164,11 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
csrfCookie: happyCSRFCookie,
|
||||
wantStatus: http.StatusFound,
|
||||
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||
wantGrantedOpenidScope: true,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: upstreamIssuer + "?sub=" + upstreamSubject,
|
||||
wantDownstreamIDTokenGroups: nil,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
wantDownstreamGrantedScopes: happyDownstreamScopesGranted,
|
||||
wantDownstreamNonce: downstreamNonce,
|
||||
wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
|
||||
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
|
||||
@ -181,11 +182,11 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
csrfCookie: happyCSRFCookie,
|
||||
wantStatus: http.StatusFound,
|
||||
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||
wantGrantedOpenidScope: true,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: upstreamSubject,
|
||||
wantDownstreamIDTokenGroups: upstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
wantDownstreamGrantedScopes: happyDownstreamScopesGranted,
|
||||
wantDownstreamNonce: downstreamNonce,
|
||||
wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
|
||||
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
|
||||
@ -316,6 +317,28 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
|
||||
wantExchangeAndValidateTokensCall: happyExchangeAndValidateTokensArgs,
|
||||
},
|
||||
{
|
||||
name: "state's downstream auth params also included offline_access scope",
|
||||
idp: happyUpstream().Build(),
|
||||
method: http.MethodGet,
|
||||
path: newRequestPath().
|
||||
WithState(
|
||||
happyUpstreamStateParam().
|
||||
WithAuthorizeRequestParams(shallowCopyAndModifyQuery(happyDownstreamRequestParamsQuery, map[string]string{"scope": "openid offline_access"}).Encode()).
|
||||
Build(t, happyStateCodec),
|
||||
).String(),
|
||||
csrfCookie: happyCSRFCookie,
|
||||
wantStatus: http.StatusFound,
|
||||
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid%20offline_access&state=` + happyDownstreamState,
|
||||
wantDownstreamIDTokenSubject: upstreamUsername,
|
||||
wantDownstreamRequestedScopes: []string{"openid", "offline_access"},
|
||||
wantDownstreamGrantedScopes: []string{"openid", "offline_access"},
|
||||
wantDownstreamIDTokenGroups: upstreamGroupMembership,
|
||||
wantDownstreamNonce: downstreamNonce,
|
||||
wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
|
||||
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
|
||||
wantExchangeAndValidateTokensCall: happyExchangeAndValidateTokensArgs,
|
||||
},
|
||||
{
|
||||
name: "the UpstreamOIDCProvider CRD has been deleted",
|
||||
idp: otherUpstreamOIDCIdentityProvider,
|
||||
@ -481,7 +504,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
|
||||
// Several Secrets should have been created
|
||||
expectedNumberOfCreatedSecrets := 2
|
||||
if test.wantGrantedOpenidScope {
|
||||
if includesOpenIDScope(test.wantDownstreamGrantedScopes) {
|
||||
expectedNumberOfCreatedSecrets++
|
||||
}
|
||||
require.Len(t, client.Actions(), expectedNumberOfCreatedSecrets)
|
||||
@ -493,7 +516,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
t,
|
||||
oauthStore,
|
||||
authcodeDataAndSignature[1], // Authcode store key is authcode signature
|
||||
test.wantGrantedOpenidScope,
|
||||
test.wantDownstreamGrantedScopes,
|
||||
test.wantDownstreamIDTokenSubject,
|
||||
test.wantDownstreamIDTokenGroups,
|
||||
test.wantDownstreamRequestedScopes,
|
||||
@ -513,7 +536,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
)
|
||||
|
||||
// One IDSession should have been stored, if the downstream actually requested the "openid" scope
|
||||
if test.wantGrantedOpenidScope {
|
||||
if includesOpenIDScope(test.wantDownstreamGrantedScopes) {
|
||||
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: openidconnect.TypeLabelValue}, 1)
|
||||
|
||||
validateIDSessionStorage(
|
||||
@ -530,6 +553,15 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func includesOpenIDScope(scopes []string) bool {
|
||||
for _, scope := range scopes {
|
||||
if scope == "openid" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type requestPath struct {
|
||||
code, state *string
|
||||
}
|
||||
@ -704,7 +736,7 @@ func validateAuthcodeStorage(
|
||||
t *testing.T,
|
||||
oauthStore *oidc.KubeStorage,
|
||||
storeKey string,
|
||||
wantGrantedOpenidScope bool,
|
||||
wantDownstreamGrantedScopes []string,
|
||||
wantDownstreamIDTokenSubject string,
|
||||
wantDownstreamIDTokenGroups []string,
|
||||
wantDownstreamRequestedScopes []string,
|
||||
@ -719,11 +751,7 @@ func validateAuthcodeStorage(
|
||||
storedRequestFromAuthcode, storedSessionFromAuthcode := castStoredAuthorizeRequest(t, storedAuthorizeRequestFromAuthcode)
|
||||
|
||||
// Check which scopes were granted.
|
||||
if wantGrantedOpenidScope {
|
||||
require.Contains(t, storedRequestFromAuthcode.GetGrantedScopes(), "openid")
|
||||
} else {
|
||||
require.NotContains(t, storedRequestFromAuthcode.GetGrantedScopes(), "openid")
|
||||
}
|
||||
require.ElementsMatch(t, wantDownstreamGrantedScopes, storedRequestFromAuthcode.GetGrantedScopes())
|
||||
|
||||
// Check all the other fields of the stored request.
|
||||
require.NotEmpty(t, storedRequestFromAuthcode.ID)
|
||||
|
@ -41,73 +41,147 @@ func NewKubeStorage(secrets corev1client.SecretInterface) *KubeStorage {
|
||||
}
|
||||
}
|
||||
|
||||
func (k KubeStorage) RevokeRefreshToken(ctx context.Context, requestID string) error {
|
||||
return k.refreshTokenStorage.RevokeRefreshToken(ctx, requestID)
|
||||
//
|
||||
// Authorization Code sessions:
|
||||
//
|
||||
// These are keyed by the signature of the authcode.
|
||||
//
|
||||
// Fosite will create these in the authorize endpoint.
|
||||
//
|
||||
// Fosite will never delete them. Instead, it wants to mark them as invalidated once the authcode is used to redeem tokens.
|
||||
// That way, it can later detect the case where an authcode that was already redeemed gets used again.
|
||||
//
|
||||
|
||||
func (k KubeStorage) CreateAuthorizeCodeSession(ctx context.Context, signatureOfAuthcode string, r fosite.Requester) (err error) {
|
||||
return k.authorizationCodeStorage.CreateAuthorizeCodeSession(ctx, signatureOfAuthcode, r)
|
||||
}
|
||||
|
||||
func (k KubeStorage) GetAuthorizeCodeSession(ctx context.Context, signatureOfAuthcode string, s fosite.Session) (request fosite.Requester, err error) {
|
||||
return k.authorizationCodeStorage.GetAuthorizeCodeSession(ctx, signatureOfAuthcode, s)
|
||||
}
|
||||
|
||||
func (k KubeStorage) InvalidateAuthorizeCodeSession(ctx context.Context, signatureOfAuthcode string) (err error) {
|
||||
return k.authorizationCodeStorage.InvalidateAuthorizeCodeSession(ctx, signatureOfAuthcode)
|
||||
}
|
||||
|
||||
//
|
||||
// PKCE sessions:
|
||||
//
|
||||
// These are keyed by the signature of the authcode.
|
||||
//
|
||||
// Fosite will create these in the authorize endpoint at the same time that it is creating an authcode.
|
||||
//
|
||||
// Fosite will delete these in the token endpoint during authcode redemption since they are no longer needed after that.
|
||||
// If the user chooses to never redeem their authcode, then fosite will never delete these.
|
||||
//
|
||||
|
||||
func (k KubeStorage) CreatePKCERequestSession(ctx context.Context, signatureOfAuthcode string, requester fosite.Requester) error {
|
||||
return k.pkceStorage.CreatePKCERequestSession(ctx, signatureOfAuthcode, requester)
|
||||
}
|
||||
|
||||
func (k KubeStorage) GetPKCERequestSession(ctx context.Context, signatureOfAuthcode string, session fosite.Session) (fosite.Requester, error) {
|
||||
return k.pkceStorage.GetPKCERequestSession(ctx, signatureOfAuthcode, session)
|
||||
}
|
||||
|
||||
func (k KubeStorage) DeletePKCERequestSession(ctx context.Context, signatureOfAuthcode string) error {
|
||||
return k.pkceStorage.DeletePKCERequestSession(ctx, signatureOfAuthcode)
|
||||
}
|
||||
|
||||
//
|
||||
// OpenID Connect sessions:
|
||||
//
|
||||
// These are keyed by the full value of the authcode (not just the signature).
|
||||
//
|
||||
// Fosite will create these in the authorize endpoint when it creates an authcode, but only if the user
|
||||
// requested the openid scope.
|
||||
//
|
||||
// Fosite will never delete these, which is likely a bug in fosite. Although there is a delete method below, fosite
|
||||
// never calls it. Used during authcode redemption, they will never be accessed again after a successful authcode
|
||||
// redemption. Although that implies that they should probably follow a lifecycle similar the the PKCE storage, they
|
||||
// are, in fact, not deleted.
|
||||
//
|
||||
|
||||
func (k KubeStorage) CreateOpenIDConnectSession(ctx context.Context, fullAuthcode string, requester fosite.Requester) error {
|
||||
return k.oidcStorage.CreateOpenIDConnectSession(ctx, fullAuthcode, requester)
|
||||
}
|
||||
|
||||
func (k KubeStorage) GetOpenIDConnectSession(ctx context.Context, fullAuthcode string, requester fosite.Requester) (fosite.Requester, error) {
|
||||
return k.oidcStorage.GetOpenIDConnectSession(ctx, fullAuthcode, requester)
|
||||
}
|
||||
|
||||
func (k KubeStorage) DeleteOpenIDConnectSession(ctx context.Context, fullAuthcode string) error {
|
||||
return k.oidcStorage.DeleteOpenIDConnectSession(ctx, fullAuthcode)
|
||||
}
|
||||
|
||||
//
|
||||
// Access token sessions:
|
||||
//
|
||||
// These are keyed by the signature of the access token.
|
||||
//
|
||||
// Fosite will create these in the token endpoint whenever it wants to hand out an access token, including the original
|
||||
// authcode redemption and also during refresh.
|
||||
//
|
||||
// Fosite will not use the delete method. Instead, it will use the revoke method to delete them.
|
||||
// During a refresh in the token endpoint, the old access token is revoked just before the new access token is created.
|
||||
// Also, if the token endpoint receives an authcode that was already used successfully, then it revokes the access token
|
||||
// that was previously handed out for that authcode. If a user stops coming back to refresh their tokens, then that
|
||||
// access token will never be deleted.
|
||||
//
|
||||
|
||||
func (k KubeStorage) CreateAccessTokenSession(ctx context.Context, signatureOfAccessToken string, requester fosite.Requester) (err error) {
|
||||
return k.accessTokenStorage.CreateAccessTokenSession(ctx, signatureOfAccessToken, requester)
|
||||
}
|
||||
|
||||
func (k KubeStorage) GetAccessTokenSession(ctx context.Context, signatureOfAccessToken string, session fosite.Session) (request fosite.Requester, err error) {
|
||||
return k.accessTokenStorage.GetAccessTokenSession(ctx, signatureOfAccessToken, session)
|
||||
}
|
||||
|
||||
func (k KubeStorage) DeleteAccessTokenSession(ctx context.Context, signatureOfAccessToken string) (err error) {
|
||||
return k.accessTokenStorage.DeleteAccessTokenSession(ctx, signatureOfAccessToken)
|
||||
}
|
||||
|
||||
func (k KubeStorage) RevokeAccessToken(ctx context.Context, requestID string) error {
|
||||
return k.accessTokenStorage.RevokeAccessToken(ctx, requestID)
|
||||
}
|
||||
|
||||
func (k KubeStorage) CreateRefreshTokenSession(ctx context.Context, signature string, request fosite.Requester) (err error) {
|
||||
return k.refreshTokenStorage.CreateRefreshTokenSession(ctx, signature, request)
|
||||
//
|
||||
// Refresh token sessions:
|
||||
//
|
||||
// These are keyed by the signature of the refresh token.
|
||||
//
|
||||
// Fosite will create these in the token endpoint whenever it wants to hand out an refresh token, including the original
|
||||
// authcode redemption and also during refresh. Refresh tokens are only handed out when the user requested the
|
||||
// offline_access scope on the original authorization request.
|
||||
//
|
||||
// Fosite will not use the delete method. Instead, it will use the revoke method to delete them.
|
||||
// During a refresh in the token endpoint, the old refresh token is revoked just before the new refresh token is created.
|
||||
// Also, if the token endpoint receives an authcode that was already used successfully, then it revokes the refresh token
|
||||
// that was previously handed out for that authcode. If a user stops coming back to refresh their tokens, then that
|
||||
// refresh token will never be deleted.
|
||||
//
|
||||
|
||||
func (k KubeStorage) CreateRefreshTokenSession(ctx context.Context, signatureOfRefreshToken string, request fosite.Requester) (err error) {
|
||||
return k.refreshTokenStorage.CreateRefreshTokenSession(ctx, signatureOfRefreshToken, request)
|
||||
}
|
||||
|
||||
func (k KubeStorage) GetRefreshTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error) {
|
||||
return k.refreshTokenStorage.GetRefreshTokenSession(ctx, signature, session)
|
||||
func (k KubeStorage) GetRefreshTokenSession(ctx context.Context, signatureOfRefreshToken string, session fosite.Session) (request fosite.Requester, err error) {
|
||||
return k.refreshTokenStorage.GetRefreshTokenSession(ctx, signatureOfRefreshToken, session)
|
||||
}
|
||||
|
||||
func (k KubeStorage) DeleteRefreshTokenSession(ctx context.Context, signature string) (err error) {
|
||||
return k.refreshTokenStorage.DeleteRefreshTokenSession(ctx, signature)
|
||||
func (k KubeStorage) DeleteRefreshTokenSession(ctx context.Context, signatureOfRefreshToken string) (err error) {
|
||||
return k.refreshTokenStorage.DeleteRefreshTokenSession(ctx, signatureOfRefreshToken)
|
||||
}
|
||||
|
||||
func (k KubeStorage) CreateAccessTokenSession(ctx context.Context, signature string, requester fosite.Requester) (err error) {
|
||||
return k.accessTokenStorage.CreateAccessTokenSession(ctx, signature, requester)
|
||||
func (k KubeStorage) RevokeRefreshToken(ctx context.Context, requestID string) error {
|
||||
return k.refreshTokenStorage.RevokeRefreshToken(ctx, requestID)
|
||||
}
|
||||
|
||||
func (k KubeStorage) GetAccessTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error) {
|
||||
return k.accessTokenStorage.GetAccessTokenSession(ctx, signature, session)
|
||||
}
|
||||
|
||||
func (k KubeStorage) DeleteAccessTokenSession(ctx context.Context, signature string) (err error) {
|
||||
return k.accessTokenStorage.DeleteAccessTokenSession(ctx, signature)
|
||||
}
|
||||
|
||||
func (k KubeStorage) CreateOpenIDConnectSession(ctx context.Context, authcode string, requester fosite.Requester) error {
|
||||
return k.oidcStorage.CreateOpenIDConnectSession(ctx, authcode, requester)
|
||||
}
|
||||
|
||||
func (k KubeStorage) GetOpenIDConnectSession(ctx context.Context, authcode string, requester fosite.Requester) (fosite.Requester, error) {
|
||||
return k.oidcStorage.GetOpenIDConnectSession(ctx, authcode, requester)
|
||||
}
|
||||
|
||||
func (k KubeStorage) DeleteOpenIDConnectSession(ctx context.Context, authcode string) error {
|
||||
return k.oidcStorage.DeleteOpenIDConnectSession(ctx, authcode)
|
||||
}
|
||||
|
||||
func (k KubeStorage) GetPKCERequestSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error) {
|
||||
return k.pkceStorage.GetPKCERequestSession(ctx, signature, session)
|
||||
}
|
||||
|
||||
func (k KubeStorage) CreatePKCERequestSession(ctx context.Context, signature string, requester fosite.Requester) error {
|
||||
return k.pkceStorage.CreatePKCERequestSession(ctx, signature, requester)
|
||||
}
|
||||
|
||||
func (k KubeStorage) DeletePKCERequestSession(ctx context.Context, signature string) error {
|
||||
return k.pkceStorage.DeletePKCERequestSession(ctx, signature)
|
||||
}
|
||||
|
||||
func (k KubeStorage) CreateAuthorizeCodeSession(ctx context.Context, signature string, r fosite.Requester) (err error) {
|
||||
return k.authorizationCodeStorage.CreateAuthorizeCodeSession(ctx, signature, r)
|
||||
}
|
||||
|
||||
func (k KubeStorage) GetAuthorizeCodeSession(ctx context.Context, signature string, s fosite.Session) (request fosite.Requester, err error) {
|
||||
return k.authorizationCodeStorage.GetAuthorizeCodeSession(ctx, signature, s)
|
||||
}
|
||||
|
||||
func (k KubeStorage) InvalidateAuthorizeCodeSession(ctx context.Context, signature string) (err error) {
|
||||
return k.authorizationCodeStorage.InvalidateAuthorizeCodeSession(ctx, signature)
|
||||
}
|
||||
//
|
||||
// OAuth client definitions:
|
||||
//
|
||||
// For the time being, we only allow a single pre-defined client, so we do not need to interact with any underlying
|
||||
// storage mechanism to fetch them.
|
||||
//
|
||||
|
||||
func (KubeStorage) GetClient(_ context.Context, id string) (fosite.Client, error) {
|
||||
client := PinnipedCLIOIDCClient()
|
||||
@ -117,6 +191,10 @@ func (KubeStorage) GetClient(_ context.Context, id string) (fosite.Client, error
|
||||
return nil, fosite.ErrNotFound
|
||||
}
|
||||
|
||||
//
|
||||
// Unused interface methods.
|
||||
//
|
||||
|
||||
func (KubeStorage) ClientAssertionJWTValid(_ context.Context, _ string) error {
|
||||
return errKubeStorageNotImplemented
|
||||
}
|
||||
|
@ -27,8 +27,8 @@ func TestNullStorage_GetClient(t *testing.T) {
|
||||
Public: true,
|
||||
RedirectURIs: []string{"http://127.0.0.1/callback"},
|
||||
ResponseTypes: []string{"code"},
|
||||
GrantTypes: []string{"authorization_code"},
|
||||
Scopes: []string{"openid", "profile", "email"},
|
||||
GrantTypes: []string{"authorization_code", "refresh_token"},
|
||||
Scopes: []string{"openid", "offline_access", "profile", "email"},
|
||||
},
|
||||
TokenEndpointAuthMethod: "none",
|
||||
},
|
||||
|
@ -7,6 +7,7 @@ package oidc
|
||||
import (
|
||||
"time"
|
||||
|
||||
coreosoidc "github.com/coreos/go-oidc"
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/fosite/compose"
|
||||
|
||||
@ -83,8 +84,8 @@ func PinnipedCLIOIDCClient() *fosite.DefaultOpenIDConnectClient {
|
||||
Public: true,
|
||||
RedirectURIs: []string{"http://127.0.0.1/callback"},
|
||||
ResponseTypes: []string{"code"},
|
||||
GrantTypes: []string{"authorization_code"},
|
||||
Scopes: []string{"openid", "profile", "email"},
|
||||
GrantTypes: []string{"authorization_code", "refresh_token"},
|
||||
Scopes: []string{coreosoidc.ScopeOpenID, coreosoidc.ScopeOfflineAccess, "profile", "email"},
|
||||
},
|
||||
TokenEndpointAuthMethod: "none",
|
||||
}
|
||||
@ -111,8 +112,9 @@ func FositeOauth2Helper(
|
||||
EnforcePKCE: true, // follow current set of best practices and always require PKCE
|
||||
AllowedPromptValues: []string{"none"}, // TODO unclear what we should set here
|
||||
|
||||
RefreshTokenScopes: nil, // TODO decide what makes sense when we add refresh token support
|
||||
MinParameterEntropy: 32, // 256 bits seems about right
|
||||
RefreshTokenScopes: []string{coreosoidc.ScopeOfflineAccess}, // as per https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
|
||||
|
||||
MinParameterEntropy: 32, // 256 bits seems about right
|
||||
}
|
||||
|
||||
return compose.Compose(
|
||||
@ -125,9 +127,9 @@ func FositeOauth2Helper(
|
||||
},
|
||||
nil, // hasher, defaults to using BCrypt when nil. Used for hashing client secrets.
|
||||
compose.OAuth2AuthorizeExplicitFactory,
|
||||
// compose.OAuth2RefreshTokenGrantFactory,
|
||||
compose.OAuth2RefreshTokenGrantFactory,
|
||||
compose.OpenIDConnectExplicitFactory,
|
||||
// compose.OpenIDConnectRefreshFactory,
|
||||
compose.OpenIDConnectRefreshFactory,
|
||||
compose.OAuth2PKCEFactory,
|
||||
)
|
||||
}
|
||||
@ -156,3 +158,11 @@ func FositeErrorForLog(err error) []interface{} {
|
||||
type IDPListGetter interface {
|
||||
GetIDPList() []provider.UpstreamOIDCIdentityProviderI
|
||||
}
|
||||
|
||||
func GrantScopeIfRequested(authorizeRequester fosite.AuthorizeRequester, scopeName string) {
|
||||
for _, scope := range authorizeRequester.GetRequestedScopes() {
|
||||
if scope == scopeName {
|
||||
authorizeRequester.GrantScope(scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user