Skip to content

Commit

Permalink
fix: retain refresh_token original scopes
Browse files Browse the repository at this point in the history
  • Loading branch information
james-d-elliott committed Nov 15, 2022
1 parent cda9c24 commit 008a527
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 9 deletions.
31 changes: 31 additions & 0 deletions access_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ type AccessRequest struct {
GrantTypes Arguments `json:"grantTypes" gorethink:"grantTypes"`
HandledGrantType Arguments `json:"handledGrantType" gorethink:"handledGrantType"`

RefreshTokenRequestedScope Arguments
RefreshTokenGrantedScope Arguments

Request
}

Expand All @@ -41,3 +44,31 @@ func NewAccessRequest(session Session) *AccessRequest {
func (a *AccessRequest) GetGrantTypes() Arguments {
return a.GrantTypes
}

func (a *AccessRequest) GetRefreshTokenRequestedScopes() (scopes Arguments) {
if a.RefreshTokenRequestedScope == nil {
return a.RequestedScope
}

return a.RefreshTokenRequestedScope
}

func (a *AccessRequest) SetRefreshTokenRequestedScopes(scopes Arguments) {
a.RefreshTokenRequestedScope = scopes
}

func (a *AccessRequest) GetRefreshTokenGrantedScopes() (scopes Arguments) {
if a.RefreshTokenGrantedScope == nil {
return a.GrantedScope
}

return a.RefreshTokenGrantedScope
}

func (a *AccessRequest) SetRefreshTokenGrantedScopes(scopes Arguments) {
a.RefreshTokenGrantedScope = scopes
}

func (a *AccessRequest) SetGrantedScopes(scopes Arguments) {
a.GrantedScope = scopes
}
35 changes: 30 additions & 5 deletions handler/oauth2/flow_refresh.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ func (c *RefreshTokenGrantHandler) HandleTokenEndpointRequest(ctx context.Contex
scopeNames := strings.Join(c.Config.GetRefreshTokenScopes(ctx), " or ")
hint := fmt.Sprintf("The OAuth 2.0 Client was not granted scope %s and may thus not perform the 'refresh_token' authorization grant.", scopeNames)
return errorsx.WithStack(fosite.ErrScopeNotGranted.WithHint(hint))

}

// The authorization server MUST ... and ensure that the refresh token was issued to the authenticated client
Expand Down Expand Up @@ -116,15 +115,25 @@ func (c *RefreshTokenGrantHandler) HandleTokenEndpointRequest(ctx context.Contex
request.SetRequestedScopes(fosite.RemoveEmpty(strings.Split(scope, " ")))
}

// If a new refresh token is issued, the refresh token scope MUST be identical to that of the refresh token included
// by the client in the request.
if rtRequest, ok := request.(fosite.RefreshTokenAccessRequester); ok {
rtRequest.SetRefreshTokenRequestedScopes(originalRequest.GetRequestedScopes())
rtRequest.SetRefreshTokenGrantedScopes(originalRequest.GetGrantedScopes())
}

request.SetRequestedAudience(originalRequest.GetRequestedAudience())

strategy := c.Config.GetScopeStrategy(ctx)
originalScopes := originalRequest.GetGrantedScopes()

for _, scope := range request.GetRequestedScopes() {
// Addresses point 2 of the text in RFC6749 Section 6.
if !originalRequest.GetGrantedScopes().Has(scope) {
if !strategy(originalScopes, scope) {
return errorsx.WithStack(fosite.ErrInvalidScope.WithHintf("The requested scope '%s' was not originally granted by the resource owner.", scope))
}

if !c.Config.GetScopeStrategy(ctx)(request.GetClient().GetScopes(), scope) {
if !strategy(request.GetClient().GetScopes(), scope) {
return errorsx.WithStack(fosite.ErrInvalidScope.WithHintf("The OAuth 2.0 Client is not allowed to request scope '%s'.", scope))
}

Expand Down Expand Up @@ -194,8 +203,24 @@ func (c *RefreshTokenGrantHandler) PopulateTokenEndpointResponse(ctx context.Con
return err
}

if err = c.TokenRevocationStorage.CreateRefreshTokenSession(ctx, refreshSignature, storeReq); err != nil {
return err
if rtRequest, ok := requester.(fosite.RefreshTokenAccessRequester); ok {
r := requester.Sanitize([]string{}).(*fosite.Request)
r.SetID(ts.GetID())

rtStoreReq := &fosite.AccessRequest{
Request: *r,
}

rtStoreReq.SetRequestedScopes(rtRequest.GetRefreshTokenRequestedScopes())
rtStoreReq.SetGrantedScopes(rtRequest.GetRefreshTokenGrantedScopes())

if err = c.TokenRevocationStorage.CreateRefreshTokenSession(ctx, refreshSignature, rtStoreReq); err != nil {
return err
}
} else {
if err = c.TokenRevocationStorage.CreateRefreshTokenSession(ctx, refreshSignature, storeReq); err != nil {
return err
}
}

responder.SetAccessToken(accessToken)
Expand Down
30 changes: 26 additions & 4 deletions oauth2.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ type IntrospectionResponder interface {
// IsActive returns true if the introspected token is active and false otherwise.
IsActive() bool

// AccessRequester returns nil when IsActive() is false and the original access request object otherwise.
// GetAccessRequester returns nil when IsActive() is false and the original access request object otherwise.
GetAccessRequester() AccessRequester

// GetTokenUse optionally returns the type of the token that was introspected. This could be "access_token", "refresh_token",
Expand Down Expand Up @@ -235,7 +235,7 @@ type Requester interface {
// AppendRequestedScope appends a scope to the request.
AppendRequestedScope(scope string)

// GetGrantScopes returns all granted scopes.
// GetGrantedScopes returns all granted scopes.
GetGrantedScopes() (grantedScopes Arguments)

// GetGrantedAudience returns all granted audiences.
Expand Down Expand Up @@ -263,9 +263,31 @@ type Requester interface {
Sanitize(allowedParameters []string) Requester
}

// RefreshTokenAccessRequester is an extended AccessRequester implementation that allows preserving
// the original Requester.
type RefreshTokenAccessRequester interface {
// GetRefreshTokenRequestedScopes returns the request's scopes specifically for the refresh token.
GetRefreshTokenRequestedScopes() (scopes Arguments)

// SetRefreshTokenRequestedScopes sets the request's scopes specifically for the refresh token.
SetRefreshTokenRequestedScopes(scopes Arguments)

// GetRefreshTokenGrantedScopes returns all granted scopes specifically for the refresh token.
GetRefreshTokenGrantedScopes() (scopes Arguments)

// SetRefreshTokenGrantedScopes sets all granted scopes specifically for the refresh token.
SetRefreshTokenGrantedScopes(scopes Arguments)

// SetGrantedScopes sets all granted scopes. This is specifically used in the refresh flow to restore the originally
// granted scopes to the session.
SetGrantedScopes(scopes Arguments)

AccessRequester
}

// AccessRequester is a token endpoint's request context.
type AccessRequester interface {
// GetGrantType returns the requests grant type.
// GetGrantTypes returns the requests grant type.
GetGrantTypes() (grantTypes Arguments)

Requester
Expand Down Expand Up @@ -323,7 +345,7 @@ type AccessResponder interface {
// SetTokenType set's the responses mandatory token type
SetTokenType(tokenType string)

// SetAccessToken returns the responses access token.
// GetAccessToken returns the responses access token.
GetAccessToken() (token string)

// GetTokenType returns the responses token type.
Expand Down

0 comments on commit 008a527

Please sign in to comment.