44package integration_test
55
66import (
7+ "context"
78 "encoding/json"
89 "net/http"
910 "net/http/httptest"
@@ -12,15 +13,14 @@ import (
1213 "testing"
1314 "time"
1415
15- "github.com/ory/fosite/internal/gen"
16-
1716 "github.com/stretchr/testify/assert"
1817 "github.com/stretchr/testify/require"
1918 "golang.org/x/oauth2"
2019
2120 "github.com/ory/fosite"
2221 "github.com/ory/fosite/compose"
2322 "github.com/ory/fosite/handler/openid"
23+ "github.com/ory/fosite/internal/gen"
2424 "github.com/ory/fosite/token/jwt"
2525)
2626
@@ -266,7 +266,9 @@ func TestRefreshTokenFlow(t *testing.T) {
266266 }
267267}
268268
269- func TestRefreshTokenFlowScopeNarrowing (t * testing.T ) {
269+ func TestRefreshTokenFlowScopeParameter (t * testing.T ) {
270+ ctx := context .Background ()
271+
270272 session := & defaultSession {
271273 DefaultSession : & openid.DefaultSession {
272274 Claims : & jwt.IDTokenClaims {
@@ -278,17 +280,16 @@ func TestRefreshTokenFlowScopeNarrowing(t *testing.T) {
278280 },
279281 }
280282 fc := new (fosite.Config )
281- fc .RefreshTokenLifespan = - 1
282283 fc .GlobalSecret = []byte ("some-secret-thats-random-some-secret-thats-random-" )
283284 f := compose .ComposeAllEnabled (fc , fositeStore , gen .MustRSAKey ())
284285 ts := mockServer (t , f , session )
285286 defer ts .Close ()
286287
287288 fc .ScopeStrategy = fosite .ExactScopeStrategy
288289
289- oauthClient := newOAuth2Client (ts )
290- oauthClient .Scopes = []string {"openid" , "offline" , "offline_access" , "foo" , "bar" }
291- oauthClient .ClientID = "grant-all-requested-scopes-client"
290+ client := newOAuth2Client (ts )
291+ client .Scopes = []string {"openid" , "offline" , "offline_access" , "foo" , "bar" }
292+ client .ClientID = "grant-all-requested-scopes-client"
292293
293294 state := "1234567890"
294295
@@ -304,53 +305,146 @@ func TestRefreshTokenFlowScopeNarrowing(t *testing.T) {
304305
305306 fositeStore .Clients ["grant-all-requested-scopes-client" ] = testRefreshingClient
306307
307- resp , err := http .Get (oauthClient .AuthCodeURL (state ))
308+ s := compose .NewOAuth2HMACStrategy (fc )
309+
310+ originalScopes := fosite.Arguments {"openid" , "offline" , "offline_access" , "foo" , "bar" }
311+
312+ testCases := []struct {
313+ name string
314+ scopes fosite.Arguments
315+ expected fosite.Arguments
316+ err string
317+ }{
318+ {
319+ "ShouldGrantOriginalScopesWhenOmitted" ,
320+ nil ,
321+ originalScopes ,
322+ "" ,
323+ },
324+ {
325+ "ShouldNarrowScopesWhenIncluded" ,
326+ fosite.Arguments {"openid" , "offline_access" , "foo" },
327+ fosite.Arguments {"openid" , "offline_access" , "foo" },
328+ "" ,
329+ },
330+ {
331+ "ShouldGrantOriginalScopesWhenOmittedAfterNarrowing" ,
332+ nil ,
333+ originalScopes ,
334+ "" ,
335+ },
336+ {
337+ "ShouldGrantOriginalScopesExplicitlyRequested" ,
338+ originalScopes ,
339+ originalScopes ,
340+ "" ,
341+ },
342+ {
343+ "ShouldErrorWhenBroadeningScopesAllowedByClientButNotOriginallyGranted" ,
344+ fosite.Arguments {"openid" , "offline" , "offline_access" , "foo" , "bar" , "baz" },
345+ nil ,
346+ "The requested scope is invalid, unknown, or malformed. The requested scope 'baz' was not originally granted by the resource owner." ,
347+ },
348+ }
349+
350+ type step struct {
351+ OAuth2 * oauth2.Token
352+ SessionAT , SessionRT fosite.Requester
353+ }
354+
355+ entries := make ([]step , len (testCases )+ 1 )
356+
357+ resp , err := http .Get (client .AuthCodeURL (state ))
308358 require .NoError (t , err )
309359 require .Equal (t , http .StatusOK , resp .StatusCode )
310360
311- token , err := oauthClient .Exchange (oauth2 .NoContext , resp .Request .URL .Query ().Get ("code" ), oauth2 .SetAuthURLParam ("client_id" , oauthClient .ClientID ))
361+ entries [0 ].OAuth2 , err = client .Exchange (ctx , resp .Request .URL .Query ().Get ("code" ), oauth2 .SetAuthURLParam ("client_id" , client .ClientID ))
362+
312363 require .NoError (t , err )
313- require .NotEmpty (t , token .AccessToken )
314- require .NotEmpty (t , token .RefreshToken )
364+ require .NotEmpty (t , entries [ 0 ]. OAuth2 .AccessToken )
365+ require .NotEmpty (t , entries [ 0 ]. OAuth2 .RefreshToken )
315366
316- assert .Equal (t , "openid offline offline_access foo bar" , token .Extra ("scope" ))
367+ assert .Equal (t , strings . Join ( originalScopes , " " ), entries [ 0 ]. OAuth2 .Extra ("scope" ))
317368
318- token1Refresh , err := doRefresh ( oauthClient , token , nil )
369+ entries [ 0 ]. SessionAT , err = fositeStore . GetAccessTokenSession ( ctx , s . AccessTokenSignature ( ctx , entries [ 0 ]. OAuth2 . AccessToken ) , nil )
319370 require .NoError (t , err )
320- require .NotEmpty (t , token1Refresh .AccessToken )
321- require .NotEmpty (t , token1Refresh .RefreshToken )
322-
323- assert .Equal (t , "openid offline offline_access foo bar" , token1Refresh .Extra ("scope" ))
324371
325- token2Refresh , err := doRefresh ( oauthClient , token1Refresh , [] string { "openid" , "offline_access" , "foo" } )
372+ entries [ 0 ]. SessionRT , err = fositeStore . GetRefreshTokenSession ( ctx , s . RefreshTokenSignature ( ctx , entries [ 0 ]. OAuth2 . RefreshToken ), nil )
326373 require .NoError (t , err )
327- require .NotEmpty (t , token2Refresh .AccessToken )
328- require .NotEmpty (t , token2Refresh .RefreshToken )
329374
330- assert .Equal (t , "openid offline_access foo" , token2Refresh .Extra ("scope" ))
375+ assert .ElementsMatch (t , entries [0 ].SessionAT .GetRequestedScopes (), originalScopes )
376+ assert .ElementsMatch (t , entries [0 ].SessionRT .GetRequestedScopes (), originalScopes )
377+ assert .ElementsMatch (t , entries [0 ].SessionAT .GetGrantedScopes (), originalScopes )
378+ assert .ElementsMatch (t , entries [0 ].SessionRT .GetGrantedScopes (), originalScopes )
379+ assert .Equal (t , strings .Join (originalScopes , " " ), entries [0 ].OAuth2 .Extra ("scope" ))
331380
332- token3Refresh , err := doRefresh (oauthClient , token2Refresh , []string {"openid" , "offline" , "offline_access" , "foo" , "bar" })
333- require .NoError (t , err )
334- require .NotEmpty (t , token3Refresh .AccessToken )
335- require .NotEmpty (t , token3Refresh .RefreshToken )
381+ for i , tc := range testCases {
382+ t .Run (tc .name , func (t * testing.T ) {
383+ time .Sleep (time .Second )
336384
337- assert . Equal ( t , "openid offline offline_access foo bar" , token3Refresh . Extra ( "scope" ))
385+ idx := i + 1
338386
339- token4Refresh , err := doRefresh (oauthClient , token3Refresh , []string {"openid" , "offline" , "offline_access" , "foo" , "bar" , "baz" })
340- require .Error (t , err )
341- require .Nil (t , token4Refresh )
342- require .Contains (t , err .Error (), "The requested scope is invalid, unknown, or malformed. The requested scope 'baz' was not originally granted by the resource owner." )
343- }
387+ opts := []oauth2.AuthCodeOption {
388+ oauth2 .SetAuthURLParam ("refresh_token" , entries [i ].OAuth2 .RefreshToken ),
389+ oauth2 .SetAuthURLParam ("grant_type" , "refresh_token" ),
390+ }
344391
345- func doRefresh (client * oauth2.Config , t * oauth2.Token , scopes []string ) (token * oauth2.Token , err error ) {
346- opts := []oauth2.AuthCodeOption {
347- oauth2 .SetAuthURLParam ("refresh_token" , t .RefreshToken ),
348- oauth2 .SetAuthURLParam ("grant_type" , "refresh_token" ),
349- }
392+ if len (tc .scopes ) != 0 {
393+ opts = append (opts , oauth2 .SetAuthURLParam ("scope" , strings .Join (tc .scopes , " " )), oauth2 .SetAuthURLParam ("client_id" , client .ClientID ))
394+ }
350395
351- if len (scopes ) != 0 {
352- opts = append (opts , oauth2 .SetAuthURLParam ("scope" , strings .Join (scopes , " " )), oauth2 .SetAuthURLParam ("client_id" , client .ClientID ))
353- }
396+ entries [idx ].OAuth2 , err = client .Exchange (ctx , "" , opts ... )
397+ if len (tc .err ) != 0 {
398+ require .Error (t , err )
399+ require .Nil (t , entries [idx ].OAuth2 )
400+ require .Contains (t , err .Error (), tc .err )
401+
402+ return
403+ }
354404
355- return client .Exchange (oauth2 .NoContext , "" , opts ... )
405+ require .NoError (t , err )
406+ require .NotEmpty (t , entries [idx ].OAuth2 .AccessToken )
407+ require .NotEmpty (t , entries [idx ].OAuth2 .RefreshToken )
408+
409+ entries [idx ].SessionAT , err = fositeStore .GetAccessTokenSession (ctx , s .AccessTokenSignature (ctx , entries [idx ].OAuth2 .AccessToken ), nil )
410+ require .NoError (t , err )
411+
412+ entries [idx ].SessionRT , err = fositeStore .GetRefreshTokenSession (ctx , s .RefreshTokenSignature (ctx , entries [idx ].OAuth2 .RefreshToken ), nil )
413+ require .NoError (t , err )
414+
415+ if len (tc .scopes ) != 0 {
416+ assert .ElementsMatch (t , entries [idx ].SessionAT .GetRequestedScopes (), tc .scopes )
417+ assert .Equal (t , strings .Join (tc .expected , " " ), entries [idx ].OAuth2 .Extra ("scope" ))
418+ } else {
419+ assert .ElementsMatch (t , entries [idx ].SessionAT .GetRequestedScopes (), originalScopes )
420+ assert .Equal (t , strings .Join (originalScopes , " " ), entries [idx ].OAuth2 .Extra ("scope" ))
421+ }
422+ assert .ElementsMatch (t , entries [idx ].SessionAT .GetGrantedScopes (), tc .expected )
423+ assert .ElementsMatch (t , entries [idx ].SessionRT .GetRequestedScopes (), originalScopes )
424+ assert .ElementsMatch (t , entries [idx ].SessionRT .GetGrantedScopes (), originalScopes )
425+
426+ var (
427+ j int
428+ entry step
429+ )
430+
431+ assert .Equal (t , entries [idx ].SessionAT .GetID (), entries [idx ].SessionRT .GetID ())
432+
433+ for j , entry = range entries {
434+ if j == idx {
435+ break
436+ }
437+
438+ assert .Equal (t , entries [idx ].SessionAT .GetID (), entry .SessionAT .GetID ())
439+ assert .Equal (t , entries [idx ].SessionAT .GetID (), entry .SessionRT .GetID ())
440+ assert .Equal (t , entries [idx ].SessionRT .GetID (), entry .SessionAT .GetID ())
441+ assert .Equal (t , entries [idx ].SessionRT .GetID (), entry .SessionRT .GetID ())
442+
443+ assert .Greater (t , entries [idx ].SessionAT .GetSession ().GetExpiresAt (fosite .AccessToken ).Unix (), entry .SessionAT .GetSession ().GetExpiresAt (fosite .AccessToken ).Unix ())
444+ assert .Greater (t , entries [idx ].SessionRT .GetSession ().GetExpiresAt (fosite .RefreshToken ).Unix (), entry .SessionRT .GetSession ().GetExpiresAt (fosite .RefreshToken ).Unix ())
445+ assert .Greater (t , entries [idx ].SessionAT .GetRequestedAt ().Unix (), entry .SessionAT .GetRequestedAt ().Unix ())
446+ assert .Greater (t , entries [idx ].SessionRT .GetRequestedAt ().Unix (), entry .SessionRT .GetRequestedAt ().Unix ())
447+ }
448+ })
449+ }
356450}
0 commit comments