Skip to content

Commit 008a527

Browse files
fix: retain refresh_token original scopes
1 parent cda9c24 commit 008a527

File tree

3 files changed

+87
-9
lines changed

3 files changed

+87
-9
lines changed

access_request.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ type AccessRequest struct {
2525
GrantTypes Arguments `json:"grantTypes" gorethink:"grantTypes"`
2626
HandledGrantType Arguments `json:"handledGrantType" gorethink:"handledGrantType"`
2727

28+
RefreshTokenRequestedScope Arguments
29+
RefreshTokenGrantedScope Arguments
30+
2831
Request
2932
}
3033

@@ -41,3 +44,31 @@ func NewAccessRequest(session Session) *AccessRequest {
4144
func (a *AccessRequest) GetGrantTypes() Arguments {
4245
return a.GrantTypes
4346
}
47+
48+
func (a *AccessRequest) GetRefreshTokenRequestedScopes() (scopes Arguments) {
49+
if a.RefreshTokenRequestedScope == nil {
50+
return a.RequestedScope
51+
}
52+
53+
return a.RefreshTokenRequestedScope
54+
}
55+
56+
func (a *AccessRequest) SetRefreshTokenRequestedScopes(scopes Arguments) {
57+
a.RefreshTokenRequestedScope = scopes
58+
}
59+
60+
func (a *AccessRequest) GetRefreshTokenGrantedScopes() (scopes Arguments) {
61+
if a.RefreshTokenGrantedScope == nil {
62+
return a.GrantedScope
63+
}
64+
65+
return a.RefreshTokenGrantedScope
66+
}
67+
68+
func (a *AccessRequest) SetRefreshTokenGrantedScopes(scopes Arguments) {
69+
a.RefreshTokenGrantedScope = scopes
70+
}
71+
72+
func (a *AccessRequest) SetGrantedScopes(scopes Arguments) {
73+
a.GrantedScope = scopes
74+
}

handler/oauth2/flow_refresh.go

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ func (c *RefreshTokenGrantHandler) HandleTokenEndpointRequest(ctx context.Contex
8686
scopeNames := strings.Join(c.Config.GetRefreshTokenScopes(ctx), " or ")
8787
hint := fmt.Sprintf("The OAuth 2.0 Client was not granted scope %s and may thus not perform the 'refresh_token' authorization grant.", scopeNames)
8888
return errorsx.WithStack(fosite.ErrScopeNotGranted.WithHint(hint))
89-
9089
}
9190

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

118+
// If a new refresh token is issued, the refresh token scope MUST be identical to that of the refresh token included
119+
// by the client in the request.
120+
if rtRequest, ok := request.(fosite.RefreshTokenAccessRequester); ok {
121+
rtRequest.SetRefreshTokenRequestedScopes(originalRequest.GetRequestedScopes())
122+
rtRequest.SetRefreshTokenGrantedScopes(originalRequest.GetGrantedScopes())
123+
}
124+
119125
request.SetRequestedAudience(originalRequest.GetRequestedAudience())
120126

127+
strategy := c.Config.GetScopeStrategy(ctx)
128+
originalScopes := originalRequest.GetGrantedScopes()
129+
121130
for _, scope := range request.GetRequestedScopes() {
122131
// Addresses point 2 of the text in RFC6749 Section 6.
123-
if !originalRequest.GetGrantedScopes().Has(scope) {
132+
if !strategy(originalScopes, scope) {
124133
return errorsx.WithStack(fosite.ErrInvalidScope.WithHintf("The requested scope '%s' was not originally granted by the resource owner.", scope))
125134
}
126135

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

@@ -194,8 +203,24 @@ func (c *RefreshTokenGrantHandler) PopulateTokenEndpointResponse(ctx context.Con
194203
return err
195204
}
196205

197-
if err = c.TokenRevocationStorage.CreateRefreshTokenSession(ctx, refreshSignature, storeReq); err != nil {
198-
return err
206+
if rtRequest, ok := requester.(fosite.RefreshTokenAccessRequester); ok {
207+
r := requester.Sanitize([]string{}).(*fosite.Request)
208+
r.SetID(ts.GetID())
209+
210+
rtStoreReq := &fosite.AccessRequest{
211+
Request: *r,
212+
}
213+
214+
rtStoreReq.SetRequestedScopes(rtRequest.GetRefreshTokenRequestedScopes())
215+
rtStoreReq.SetGrantedScopes(rtRequest.GetRefreshTokenGrantedScopes())
216+
217+
if err = c.TokenRevocationStorage.CreateRefreshTokenSession(ctx, refreshSignature, rtStoreReq); err != nil {
218+
return err
219+
}
220+
} else {
221+
if err = c.TokenRevocationStorage.CreateRefreshTokenSession(ctx, refreshSignature, storeReq); err != nil {
222+
return err
223+
}
199224
}
200225

201226
responder.SetAccessToken(accessToken)

oauth2.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ type IntrospectionResponder interface {
194194
// IsActive returns true if the introspected token is active and false otherwise.
195195
IsActive() bool
196196

197-
// AccessRequester returns nil when IsActive() is false and the original access request object otherwise.
197+
// GetAccessRequester returns nil when IsActive() is false and the original access request object otherwise.
198198
GetAccessRequester() AccessRequester
199199

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

238-
// GetGrantScopes returns all granted scopes.
238+
// GetGrantedScopes returns all granted scopes.
239239
GetGrantedScopes() (grantedScopes Arguments)
240240

241241
// GetGrantedAudience returns all granted audiences.
@@ -263,9 +263,31 @@ type Requester interface {
263263
Sanitize(allowedParameters []string) Requester
264264
}
265265

266+
// RefreshTokenAccessRequester is an extended AccessRequester implementation that allows preserving
267+
// the original Requester.
268+
type RefreshTokenAccessRequester interface {
269+
// GetRefreshTokenRequestedScopes returns the request's scopes specifically for the refresh token.
270+
GetRefreshTokenRequestedScopes() (scopes Arguments)
271+
272+
// SetRefreshTokenRequestedScopes sets the request's scopes specifically for the refresh token.
273+
SetRefreshTokenRequestedScopes(scopes Arguments)
274+
275+
// GetRefreshTokenGrantedScopes returns all granted scopes specifically for the refresh token.
276+
GetRefreshTokenGrantedScopes() (scopes Arguments)
277+
278+
// SetRefreshTokenGrantedScopes sets all granted scopes specifically for the refresh token.
279+
SetRefreshTokenGrantedScopes(scopes Arguments)
280+
281+
// SetGrantedScopes sets all granted scopes. This is specifically used in the refresh flow to restore the originally
282+
// granted scopes to the session.
283+
SetGrantedScopes(scopes Arguments)
284+
285+
AccessRequester
286+
}
287+
266288
// AccessRequester is a token endpoint's request context.
267289
type AccessRequester interface {
268-
// GetGrantType returns the requests grant type.
290+
// GetGrantTypes returns the requests grant type.
269291
GetGrantTypes() (grantTypes Arguments)
270292

271293
Requester
@@ -323,7 +345,7 @@ type AccessResponder interface {
323345
// SetTokenType set's the responses mandatory token type
324346
SetTokenType(tokenType string)
325347

326-
// SetAccessToken returns the responses access token.
348+
// GetAccessToken returns the responses access token.
327349
GetAccessToken() (token string)
328350

329351
// GetTokenType returns the responses token type.

0 commit comments

Comments
 (0)