...

Source file src/github.com/ory/fosite/handler/oauth2/flow_refresh_test.go

Documentation: github.com/ory/fosite/handler/oauth2

     1  /*
     2   * Copyright © 2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   * @author		Aeneas Rekkas <aeneas+oss@aeneas.io>
    17   * @copyright 	2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
    18   * @license 	Apache-2.0
    19   *
    20   */
    21  
    22  package oauth2
    23  
    24  import (
    25  	"context"
    26  	"fmt"
    27  	"net/url"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/golang/mock/gomock"
    32  
    33  	"github.com/ory/fosite/internal"
    34  
    35  	"github.com/pkg/errors"
    36  	"github.com/stretchr/testify/assert"
    37  	"github.com/stretchr/testify/require"
    38  
    39  	"github.com/ory/fosite"
    40  	"github.com/ory/fosite/storage"
    41  )
    42  
    43  func TestRefreshFlow_HandleTokenEndpointRequest(t *testing.T) {
    44  	var areq *fosite.AccessRequest
    45  	sess := &fosite.DefaultSession{Subject: "othersub"}
    46  	expiredSess := &fosite.DefaultSession{
    47  		ExpiresAt: map[fosite.TokenType]time.Time{
    48  			fosite.RefreshToken: time.Now().UTC().Add(-time.Hour),
    49  		},
    50  	}
    51  
    52  	for k, strategy := range map[string]RefreshTokenStrategy{
    53  		"hmac": &hmacshaStrategy,
    54  	} {
    55  		t.Run("strategy="+k, func(t *testing.T) {
    56  
    57  			store := storage.NewMemoryStore()
    58  			var h RefreshTokenGrantHandler
    59  
    60  			for _, c := range []struct {
    61  				description string
    62  				setup       func()
    63  				expectErr   error
    64  				expect      func(t *testing.T)
    65  			}{
    66  				{
    67  					description: "should fail because not responsible",
    68  					expectErr:   fosite.ErrUnknownRequest,
    69  					setup: func() {
    70  						areq.GrantTypes = fosite.Arguments{"123"}
    71  					},
    72  				},
    73  				{
    74  					description: "should fail because token invalid",
    75  					setup: func() {
    76  						areq.GrantTypes = fosite.Arguments{"refresh_token"}
    77  						areq.Client = &fosite.DefaultClient{GrantTypes: fosite.Arguments{"refresh_token"}}
    78  
    79  						areq.Form.Add("refresh_token", "some.refreshtokensig")
    80  					},
    81  					expectErr: fosite.ErrInvalidGrant,
    82  				},
    83  				{
    84  					description: "should fail because token is valid but does not exist",
    85  					setup: func() {
    86  						areq.GrantTypes = fosite.Arguments{"refresh_token"}
    87  						areq.Client = &fosite.DefaultClient{GrantTypes: fosite.Arguments{"refresh_token"}}
    88  
    89  						token, _, err := strategy.GenerateRefreshToken(nil, nil)
    90  						require.NoError(t, err)
    91  						areq.Form.Add("refresh_token", token)
    92  					},
    93  					expectErr: fosite.ErrInvalidGrant,
    94  				},
    95  				{
    96  					description: "should fail because client mismatches",
    97  					setup: func() {
    98  						areq.GrantTypes = fosite.Arguments{"refresh_token"}
    99  						areq.Client = &fosite.DefaultClient{
   100  							ID:         "foo",
   101  							GrantTypes: fosite.Arguments{"refresh_token"},
   102  						}
   103  
   104  						token, sig, err := strategy.GenerateRefreshToken(nil, nil)
   105  						require.NoError(t, err)
   106  
   107  						areq.Form.Add("refresh_token", token)
   108  						err = store.CreateRefreshTokenSession(nil, sig, &fosite.Request{
   109  							Client:       &fosite.DefaultClient{ID: ""},
   110  							GrantedScope: []string{"offline"},
   111  							Session:      sess,
   112  						})
   113  						require.NoError(t, err)
   114  					},
   115  					expectErr: fosite.ErrInvalidGrant,
   116  				},
   117  				{
   118  					description: "should fail because token is expired",
   119  					setup: func() {
   120  						areq.GrantTypes = fosite.Arguments{"refresh_token"}
   121  						areq.Client = &fosite.DefaultClient{
   122  							ID:         "foo",
   123  							GrantTypes: fosite.Arguments{"refresh_token"},
   124  							Scopes:     []string{"foo", "bar", "offline"},
   125  						}
   126  
   127  						token, sig, err := strategy.GenerateRefreshToken(nil, nil)
   128  						require.NoError(t, err)
   129  
   130  						areq.Form.Add("refresh_token", token)
   131  						err = store.CreateRefreshTokenSession(nil, sig, &fosite.Request{
   132  							Client:         areq.Client,
   133  							GrantedScope:   fosite.Arguments{"foo", "offline"},
   134  							RequestedScope: fosite.Arguments{"foo", "bar", "offline"},
   135  							Session:        expiredSess,
   136  							Form:           url.Values{"foo": []string{"bar"}},
   137  							RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour),
   138  						})
   139  						require.NoError(t, err)
   140  					},
   141  					expectErr: fosite.ErrInvalidGrant,
   142  				},
   143  				{
   144  					description: "should fail because offline scope has been granted but client no longer allowed to request it",
   145  					setup: func() {
   146  						areq.GrantTypes = fosite.Arguments{"refresh_token"}
   147  						areq.Client = &fosite.DefaultClient{
   148  							ID:         "foo",
   149  							GrantTypes: fosite.Arguments{"refresh_token"},
   150  						}
   151  
   152  						token, sig, err := strategy.GenerateRefreshToken(nil, nil)
   153  						require.NoError(t, err)
   154  
   155  						areq.Form.Add("refresh_token", token)
   156  						err = store.CreateRefreshTokenSession(nil, sig, &fosite.Request{
   157  							Client:         areq.Client,
   158  							GrantedScope:   fosite.Arguments{"foo", "offline"},
   159  							RequestedScope: fosite.Arguments{"foo", "offline"},
   160  							Session:        sess,
   161  							Form:           url.Values{"foo": []string{"bar"}},
   162  							RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour),
   163  						})
   164  						require.NoError(t, err)
   165  					},
   166  					expectErr: fosite.ErrInvalidScope,
   167  				},
   168  				{
   169  					description: "should pass",
   170  					setup: func() {
   171  						areq.GrantTypes = fosite.Arguments{"refresh_token"}
   172  						areq.Client = &fosite.DefaultClient{
   173  							ID:         "foo",
   174  							GrantTypes: fosite.Arguments{"refresh_token"},
   175  							Scopes:     []string{"foo", "bar", "offline"},
   176  						}
   177  
   178  						token, sig, err := strategy.GenerateRefreshToken(nil, nil)
   179  						require.NoError(t, err)
   180  
   181  						areq.Form.Add("refresh_token", token)
   182  						err = store.CreateRefreshTokenSession(nil, sig, &fosite.Request{
   183  							Client:         areq.Client,
   184  							GrantedScope:   fosite.Arguments{"foo", "offline"},
   185  							RequestedScope: fosite.Arguments{"foo", "bar", "offline"},
   186  							Session:        sess,
   187  							Form:           url.Values{"foo": []string{"bar"}},
   188  							RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour),
   189  						})
   190  						require.NoError(t, err)
   191  					},
   192  					expect: func(t *testing.T) {
   193  						assert.NotEqual(t, sess, areq.Session)
   194  						assert.NotEqual(t, time.Now().UTC().Add(-time.Hour).Round(time.Hour), areq.RequestedAt)
   195  						assert.Equal(t, fosite.Arguments{"foo", "offline"}, areq.GrantedScope)
   196  						assert.Equal(t, fosite.Arguments{"foo", "bar", "offline"}, areq.RequestedScope)
   197  						assert.NotEqual(t, url.Values{"foo": []string{"bar"}}, areq.Form)
   198  						assert.Equal(t, time.Now().Add(time.Hour).UTC().Round(time.Second), areq.GetSession().GetExpiresAt(fosite.AccessToken))
   199  						assert.Equal(t, time.Now().Add(time.Hour).UTC().Round(time.Second), areq.GetSession().GetExpiresAt(fosite.RefreshToken))
   200  					},
   201  				},
   202  				{
   203  					description: "should fail without offline scope",
   204  					setup: func() {
   205  						areq.GrantTypes = fosite.Arguments{"refresh_token"}
   206  						areq.Client = &fosite.DefaultClient{
   207  							ID:         "foo",
   208  							GrantTypes: fosite.Arguments{"refresh_token"},
   209  							Scopes:     []string{"foo", "bar"},
   210  						}
   211  
   212  						token, sig, err := strategy.GenerateRefreshToken(nil, nil)
   213  						require.NoError(t, err)
   214  
   215  						areq.Form.Add("refresh_token", token)
   216  						err = store.CreateRefreshTokenSession(nil, sig, &fosite.Request{
   217  							Client:         areq.Client,
   218  							GrantedScope:   fosite.Arguments{"foo"},
   219  							RequestedScope: fosite.Arguments{"foo", "bar"},
   220  							Session:        sess,
   221  							Form:           url.Values{"foo": []string{"bar"}},
   222  							RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour),
   223  						})
   224  						require.NoError(t, err)
   225  					},
   226  					expectErr: fosite.ErrScopeNotGranted,
   227  				},
   228  				{
   229  					description: "should pass without offline scope when configured to allow refresh tokens",
   230  					setup: func() {
   231  						h.RefreshTokenScopes = []string{}
   232  						areq.GrantTypes = fosite.Arguments{"refresh_token"}
   233  						areq.Client = &fosite.DefaultClient{
   234  							ID:         "foo",
   235  							GrantTypes: fosite.Arguments{"refresh_token"},
   236  							Scopes:     []string{"foo", "bar"},
   237  						}
   238  
   239  						token, sig, err := strategy.GenerateRefreshToken(nil, nil)
   240  						require.NoError(t, err)
   241  
   242  						areq.Form.Add("refresh_token", token)
   243  						err = store.CreateRefreshTokenSession(nil, sig, &fosite.Request{
   244  							Client:         areq.Client,
   245  							GrantedScope:   fosite.Arguments{"foo"},
   246  							RequestedScope: fosite.Arguments{"foo", "bar"},
   247  							Session:        sess,
   248  							Form:           url.Values{"foo": []string{"bar"}},
   249  							RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour),
   250  						})
   251  						require.NoError(t, err)
   252  					},
   253  					expect: func(t *testing.T) {
   254  						assert.NotEqual(t, sess, areq.Session)
   255  						assert.NotEqual(t, time.Now().UTC().Add(-time.Hour).Round(time.Hour), areq.RequestedAt)
   256  						assert.Equal(t, fosite.Arguments{"foo"}, areq.GrantedScope)
   257  						assert.Equal(t, fosite.Arguments{"foo", "bar"}, areq.RequestedScope)
   258  						assert.NotEqual(t, url.Values{"foo": []string{"bar"}}, areq.Form)
   259  						assert.Equal(t, time.Now().Add(time.Hour).UTC().Round(time.Second), areq.GetSession().GetExpiresAt(fosite.AccessToken))
   260  						assert.Equal(t, time.Now().Add(time.Hour).UTC().Round(time.Second), areq.GetSession().GetExpiresAt(fosite.RefreshToken))
   261  					},
   262  				},
   263  				{
   264  					description: "should deny access on token reuse",
   265  					setup: func() {
   266  						areq.GrantTypes = fosite.Arguments{"refresh_token"}
   267  						areq.Client = &fosite.DefaultClient{
   268  							ID:         "foo",
   269  							GrantTypes: fosite.Arguments{"refresh_token"},
   270  							Scopes:     []string{"foo", "bar", "offline"},
   271  						}
   272  
   273  						token, sig, err := strategy.GenerateRefreshToken(nil, nil)
   274  						require.NoError(t, err)
   275  
   276  						areq.Form.Add("refresh_token", token)
   277  						req := &fosite.Request{
   278  							Client:         areq.Client,
   279  							GrantedScope:   fosite.Arguments{"foo", "offline"},
   280  							RequestedScope: fosite.Arguments{"foo", "bar", "offline"},
   281  							Session:        sess,
   282  							Form:           url.Values{"foo": []string{"bar"}},
   283  							RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour),
   284  						}
   285  						err = store.CreateRefreshTokenSession(nil, sig, req)
   286  						require.NoError(t, err)
   287  
   288  						err = store.RevokeRefreshToken(nil, req.ID)
   289  						require.NoError(t, err)
   290  					},
   291  					expectErr: fosite.ErrInactiveToken,
   292  				},
   293  			} {
   294  				t.Run("case="+c.description, func(t *testing.T) {
   295  					h = RefreshTokenGrantHandler{
   296  						TokenRevocationStorage:   store,
   297  						RefreshTokenStrategy:     strategy,
   298  						AccessTokenLifespan:      time.Hour,
   299  						RefreshTokenLifespan:     time.Hour,
   300  						ScopeStrategy:            fosite.HierarchicScopeStrategy,
   301  						AudienceMatchingStrategy: fosite.DefaultAudienceMatchingStrategy,
   302  						RefreshTokenScopes:       []string{"offline"},
   303  					}
   304  
   305  					areq = fosite.NewAccessRequest(&fosite.DefaultSession{})
   306  					areq.Form = url.Values{}
   307  					c.setup()
   308  
   309  					err := h.HandleTokenEndpointRequest(nil, areq)
   310  					if c.expectErr != nil {
   311  						require.EqualError(t, err, c.expectErr.Error())
   312  					} else {
   313  						require.NoError(t, err)
   314  					}
   315  
   316  					if c.expect != nil {
   317  						c.expect(t)
   318  					}
   319  				})
   320  			}
   321  		})
   322  	}
   323  }
   324  
   325  func TestRefreshFlowTransactional_HandleTokenEndpointRequest(t *testing.T) {
   326  	var mockTransactional *internal.MockTransactional
   327  	var mockRevocationStore *internal.MockTokenRevocationStorage
   328  	request := fosite.NewAccessRequest(&fosite.DefaultSession{})
   329  	propagatedContext := context.Background()
   330  
   331  	type transactionalStore struct {
   332  		storage.Transactional
   333  		TokenRevocationStorage
   334  	}
   335  
   336  	for _, testCase := range []struct {
   337  		description string
   338  		setup       func()
   339  		expectError error
   340  	}{
   341  		{
   342  			description: "should revoke session on token reuse",
   343  			setup: func() {
   344  				request.GrantTypes = fosite.Arguments{"refresh_token"}
   345  				request.Client = &fosite.DefaultClient{
   346  					ID:         "foo",
   347  					GrantTypes: fosite.Arguments{"refresh_token"},
   348  				}
   349  				mockRevocationStore.
   350  					EXPECT().
   351  					GetRefreshTokenSession(propagatedContext, gomock.Any(), gomock.Any()).
   352  					Return(request, fosite.ErrInactiveToken).
   353  					Times(1)
   354  				mockTransactional.
   355  					EXPECT().
   356  					BeginTX(propagatedContext).
   357  					Return(propagatedContext, nil).
   358  					Times(1)
   359  				mockRevocationStore.
   360  					EXPECT().
   361  					DeleteRefreshTokenSession(propagatedContext, gomock.Any()).
   362  					Return(nil).
   363  					Times(1)
   364  				mockRevocationStore.
   365  					EXPECT().
   366  					RevokeRefreshToken(propagatedContext, gomock.Any()).
   367  					Return(nil).
   368  					Times(1)
   369  				mockRevocationStore.
   370  					EXPECT().
   371  					RevokeAccessToken(propagatedContext, gomock.Any()).
   372  					Return(nil).
   373  					Times(1)
   374  				mockTransactional.
   375  					EXPECT().
   376  					Commit(propagatedContext).
   377  					Return(nil).
   378  					Times(1)
   379  			},
   380  			expectError: fosite.ErrInactiveToken,
   381  		},
   382  	} {
   383  		t.Run(fmt.Sprintf("scenario=%s", testCase.description), func(t *testing.T) {
   384  			ctrl := gomock.NewController(t)
   385  			defer ctrl.Finish()
   386  
   387  			mockTransactional = internal.NewMockTransactional(ctrl)
   388  			mockRevocationStore = internal.NewMockTokenRevocationStorage(ctrl)
   389  			testCase.setup()
   390  
   391  			handler := RefreshTokenGrantHandler{
   392  				TokenRevocationStorage: transactionalStore{
   393  					mockTransactional,
   394  					mockRevocationStore,
   395  				},
   396  				AccessTokenStrategy:      &hmacshaStrategy,
   397  				RefreshTokenStrategy:     &hmacshaStrategy,
   398  				AccessTokenLifespan:      time.Hour,
   399  				ScopeStrategy:            fosite.HierarchicScopeStrategy,
   400  				AudienceMatchingStrategy: fosite.DefaultAudienceMatchingStrategy,
   401  			}
   402  
   403  			if err := handler.HandleTokenEndpointRequest(propagatedContext, request); testCase.expectError != nil {
   404  				assert.EqualError(t, err, testCase.expectError.Error())
   405  			}
   406  		})
   407  	}
   408  }
   409  
   410  func TestRefreshFlow_PopulateTokenEndpointResponse(t *testing.T) {
   411  	var areq *fosite.AccessRequest
   412  	var aresp *fosite.AccessResponse
   413  
   414  	for k, strategy := range map[string]CoreStrategy{
   415  		"hmac": &hmacshaStrategy,
   416  	} {
   417  		t.Run("strategy="+k, func(t *testing.T) {
   418  			store := storage.NewMemoryStore()
   419  
   420  			h := RefreshTokenGrantHandler{
   421  				TokenRevocationStorage:   store,
   422  				RefreshTokenStrategy:     strategy,
   423  				AccessTokenStrategy:      strategy,
   424  				AccessTokenLifespan:      time.Hour,
   425  				ScopeStrategy:            fosite.HierarchicScopeStrategy,
   426  				AudienceMatchingStrategy: fosite.DefaultAudienceMatchingStrategy,
   427  			}
   428  			for _, c := range []struct {
   429  				description string
   430  				setup       func()
   431  				check       func()
   432  				expectErr   error
   433  			}{
   434  				{
   435  					description: "should fail because not responsible",
   436  					expectErr:   fosite.ErrUnknownRequest,
   437  					setup: func() {
   438  						areq.GrantTypes = fosite.Arguments{"313"}
   439  					},
   440  				},
   441  				{
   442  					description: "should pass",
   443  					setup: func() {
   444  						areq.ID = "req-id"
   445  						areq.GrantTypes = fosite.Arguments{"refresh_token"}
   446  						areq.RequestedScope = fosite.Arguments{"foo", "bar"}
   447  						areq.GrantedScope = fosite.Arguments{"foo", "bar"}
   448  
   449  						token, signature, err := strategy.GenerateRefreshToken(nil, nil)
   450  						require.NoError(t, err)
   451  						require.NoError(t, store.CreateRefreshTokenSession(nil, signature, areq))
   452  						areq.Form.Add("refresh_token", token)
   453  					},
   454  					check: func() {
   455  						signature := strategy.RefreshTokenSignature(areq.Form.Get("refresh_token"))
   456  
   457  						// The old refresh token should be deleted
   458  						_, err := store.GetRefreshTokenSession(nil, signature, nil)
   459  						require.Error(t, err)
   460  
   461  						assert.Equal(t, "req-id", areq.ID)
   462  						require.NoError(t, strategy.ValidateAccessToken(nil, areq, aresp.GetAccessToken()))
   463  						require.NoError(t, strategy.ValidateRefreshToken(nil, areq, aresp.ToMap()["refresh_token"].(string)))
   464  						assert.Equal(t, "bearer", aresp.GetTokenType())
   465  						assert.NotEmpty(t, aresp.ToMap()["expires_in"])
   466  						assert.Equal(t, "foo bar", aresp.ToMap()["scope"])
   467  					},
   468  				},
   469  			} {
   470  				t.Run("case="+c.description, func(t *testing.T) {
   471  					areq = fosite.NewAccessRequest(&fosite.DefaultSession{})
   472  					aresp = fosite.NewAccessResponse()
   473  					areq.Client = &fosite.DefaultClient{}
   474  					areq.Form = url.Values{}
   475  
   476  					c.setup()
   477  
   478  					err := h.PopulateTokenEndpointResponse(nil, areq, aresp)
   479  					if c.expectErr != nil {
   480  						assert.EqualError(t, err, c.expectErr.Error())
   481  					} else {
   482  						assert.NoError(t, err)
   483  					}
   484  
   485  					if c.check != nil {
   486  						c.check()
   487  					}
   488  				})
   489  			}
   490  		})
   491  	}
   492  }
   493  
   494  func TestRefreshFlowTransactional_PopulateTokenEndpointResponse(t *testing.T) {
   495  	var mockTransactional *internal.MockTransactional
   496  	var mockRevocationStore *internal.MockTokenRevocationStorage
   497  	request := fosite.NewAccessRequest(&fosite.DefaultSession{})
   498  	response := fosite.NewAccessResponse()
   499  	propagatedContext := context.Background()
   500  
   501  	// some storage implementation that has support for transactions, notice the embedded type `storage.Transactional`
   502  	type transactionalStore struct {
   503  		storage.Transactional
   504  		TokenRevocationStorage
   505  	}
   506  
   507  	for _, testCase := range []struct {
   508  		description string
   509  		setup       func()
   510  		expectError error
   511  	}{
   512  		{
   513  			description: "transaction should be committed successfully if no errors occur",
   514  			setup: func() {
   515  				request.GrantTypes = fosite.Arguments{"refresh_token"}
   516  				mockTransactional.
   517  					EXPECT().
   518  					BeginTX(propagatedContext).
   519  					Return(propagatedContext, nil).
   520  					Times(1)
   521  				mockRevocationStore.
   522  					EXPECT().
   523  					GetRefreshTokenSession(propagatedContext, gomock.Any(), nil).
   524  					Return(request, nil).
   525  					Times(1)
   526  				mockRevocationStore.
   527  					EXPECT().
   528  					RevokeAccessToken(propagatedContext, gomock.Any()).
   529  					Return(nil).
   530  					Times(1)
   531  				mockRevocationStore.
   532  					EXPECT().
   533  					RevokeRefreshTokenMaybeGracePeriod(propagatedContext, gomock.Any(), gomock.Any()).
   534  					Return(nil).
   535  					Times(1)
   536  				mockRevocationStore.
   537  					EXPECT().
   538  					CreateAccessTokenSession(propagatedContext, gomock.Any(), gomock.Any()).
   539  					Return(nil).
   540  					Times(1)
   541  				mockRevocationStore.
   542  					EXPECT().
   543  					CreateRefreshTokenSession(propagatedContext, gomock.Any(), gomock.Any()).
   544  					Return(nil).
   545  					Times(1)
   546  				mockTransactional.
   547  					EXPECT().
   548  					Commit(propagatedContext).
   549  					Return(nil).
   550  					Times(1)
   551  			},
   552  		},
   553  		{
   554  			description: "transaction should be rolled back if call to `GetRefreshTokenSession` results in an error",
   555  			setup: func() {
   556  				request.GrantTypes = fosite.Arguments{"refresh_token"}
   557  				mockTransactional.
   558  					EXPECT().
   559  					BeginTX(propagatedContext).
   560  					Return(propagatedContext, nil).
   561  					Times(1)
   562  				mockRevocationStore.
   563  					EXPECT().
   564  					GetRefreshTokenSession(propagatedContext, gomock.Any(), nil).
   565  					Return(nil, errors.New("Whoops, a nasty database error occurred!")).
   566  					Times(1)
   567  				mockTransactional.
   568  					EXPECT().
   569  					Rollback(propagatedContext).
   570  					Return(nil).
   571  					Times(1)
   572  			},
   573  			expectError: fosite.ErrServerError,
   574  		},
   575  		{
   576  			description: "should result in a fosite.ErrInvalidRequest if `GetRefreshTokenSession` results in a " +
   577  				"fosite.ErrNotFound error",
   578  			setup: func() {
   579  				request.GrantTypes = fosite.Arguments{"refresh_token"}
   580  				mockTransactional.
   581  					EXPECT().
   582  					BeginTX(propagatedContext).
   583  					Return(propagatedContext, nil).
   584  					Times(1)
   585  				mockRevocationStore.
   586  					EXPECT().
   587  					GetRefreshTokenSession(propagatedContext, gomock.Any(), nil).
   588  					Return(nil, fosite.ErrNotFound).
   589  					Times(1)
   590  				mockTransactional.
   591  					EXPECT().
   592  					Rollback(propagatedContext).
   593  					Return(nil).
   594  					Times(1)
   595  			},
   596  			expectError: fosite.ErrInvalidRequest,
   597  		},
   598  		{
   599  			description: "transaction should be rolled back if call to `RevokeAccessToken` results in an error",
   600  			setup: func() {
   601  				request.GrantTypes = fosite.Arguments{"refresh_token"}
   602  				mockTransactional.
   603  					EXPECT().
   604  					BeginTX(propagatedContext).
   605  					Return(propagatedContext, nil).
   606  					Times(1)
   607  				mockRevocationStore.
   608  					EXPECT().
   609  					GetRefreshTokenSession(propagatedContext, gomock.Any(), nil).
   610  					Return(request, nil).
   611  					Times(1)
   612  				mockRevocationStore.
   613  					EXPECT().
   614  					RevokeAccessToken(propagatedContext, gomock.Any()).
   615  					Return(errors.New("Whoops, a nasty database error occurred!")).
   616  					Times(1)
   617  				mockTransactional.
   618  					EXPECT().
   619  					Rollback(propagatedContext).
   620  					Return(nil).
   621  					Times(1)
   622  			},
   623  			expectError: fosite.ErrServerError,
   624  		},
   625  		{
   626  			description: "should result in a fosite.ErrInvalidRequest if call to `RevokeAccessToken` results in a " +
   627  				"fosite.ErrSerializationFailure error",
   628  			setup: func() {
   629  				request.GrantTypes = fosite.Arguments{"refresh_token"}
   630  				mockTransactional.
   631  					EXPECT().
   632  					BeginTX(propagatedContext).
   633  					Return(propagatedContext, nil).
   634  					Times(1)
   635  				mockRevocationStore.
   636  					EXPECT().
   637  					GetRefreshTokenSession(propagatedContext, gomock.Any(), nil).
   638  					Return(request, nil).
   639  					Times(1)
   640  				mockRevocationStore.
   641  					EXPECT().
   642  					RevokeAccessToken(propagatedContext, gomock.Any()).
   643  					Return(fosite.ErrSerializationFailure).
   644  					Times(1)
   645  				mockTransactional.
   646  					EXPECT().
   647  					Rollback(propagatedContext).
   648  					Return(nil).
   649  					Times(1)
   650  			},
   651  			expectError: fosite.ErrInvalidRequest,
   652  		},
   653  		{
   654  			description: "should result in a fosite.ErrInactiveToken if call to `RevokeAccessToken` results in a " +
   655  				"fosite.ErrInvalidRequest error",
   656  			setup: func() {
   657  				request.GrantTypes = fosite.Arguments{"refresh_token"}
   658  				mockTransactional.
   659  					EXPECT().
   660  					BeginTX(propagatedContext).
   661  					Return(propagatedContext, nil).
   662  					Times(1)
   663  				mockRevocationStore.
   664  					EXPECT().
   665  					GetRefreshTokenSession(propagatedContext, gomock.Any(), nil).
   666  					Return(nil, fosite.ErrInactiveToken).
   667  					Times(1)
   668  				mockTransactional.
   669  					EXPECT().
   670  					Rollback(propagatedContext).
   671  					Return(nil).
   672  					Times(1)
   673  			},
   674  			expectError: fosite.ErrInvalidRequest,
   675  		},
   676  		{
   677  			description: "transaction should be rolled back if call to `RevokeRefreshTokenMaybeGracePeriod` results in an error",
   678  			setup: func() {
   679  				request.GrantTypes = fosite.Arguments{"refresh_token"}
   680  				mockTransactional.
   681  					EXPECT().
   682  					BeginTX(propagatedContext).
   683  					Return(propagatedContext, nil).
   684  					Times(1)
   685  				mockRevocationStore.
   686  					EXPECT().
   687  					GetRefreshTokenSession(propagatedContext, gomock.Any(), nil).
   688  					Return(request, nil).
   689  					Times(1)
   690  				mockRevocationStore.
   691  					EXPECT().
   692  					RevokeAccessToken(propagatedContext, gomock.Any()).
   693  					Return(nil).
   694  					Times(1)
   695  				mockRevocationStore.
   696  					EXPECT().
   697  					RevokeRefreshTokenMaybeGracePeriod(propagatedContext, gomock.Any(), gomock.Any()).
   698  					Return(errors.New("Whoops, a nasty database error occurred!")).
   699  					Times(1)
   700  				mockTransactional.
   701  					EXPECT().
   702  					Rollback(propagatedContext).
   703  					Return(nil).
   704  					Times(1)
   705  			},
   706  			expectError: fosite.ErrServerError,
   707  		},
   708  		{
   709  			description: "should result in a fosite.ErrInvalidRequest if call to `RevokeRefreshTokenMaybeGracePeriod` results in a " +
   710  				"fosite.ErrSerializationFailure error",
   711  			setup: func() {
   712  				request.GrantTypes = fosite.Arguments{"refresh_token"}
   713  				mockTransactional.
   714  					EXPECT().
   715  					BeginTX(propagatedContext).
   716  					Return(propagatedContext, nil).
   717  					Times(1)
   718  				mockRevocationStore.
   719  					EXPECT().
   720  					GetRefreshTokenSession(propagatedContext, gomock.Any(), nil).
   721  					Return(request, nil).
   722  					Times(1)
   723  				mockRevocationStore.
   724  					EXPECT().
   725  					RevokeAccessToken(propagatedContext, gomock.Any()).
   726  					Return(nil).
   727  					Times(1)
   728  				mockRevocationStore.
   729  					EXPECT().
   730  					RevokeRefreshTokenMaybeGracePeriod(propagatedContext, gomock.Any(), gomock.Any()).
   731  					Return(fosite.ErrSerializationFailure).
   732  					Times(1)
   733  				mockTransactional.
   734  					EXPECT().
   735  					Rollback(propagatedContext).
   736  					Return(nil).
   737  					Times(1)
   738  			},
   739  			expectError: fosite.ErrInvalidRequest,
   740  		},
   741  		{
   742  			description: "should result in a fosite.ErrInvalidRequest if call to `CreateAccessTokenSession` results in " +
   743  				"a fosite.ErrSerializationFailure error",
   744  			setup: func() {
   745  				mockTransactional.
   746  					EXPECT().
   747  					BeginTX(propagatedContext).
   748  					Return(propagatedContext, nil).
   749  					Times(1)
   750  				mockRevocationStore.
   751  					EXPECT().
   752  					GetRefreshTokenSession(propagatedContext, gomock.Any(), nil).
   753  					Return(request, nil).
   754  					Times(1)
   755  				mockRevocationStore.
   756  					EXPECT().
   757  					RevokeAccessToken(propagatedContext, gomock.Any()).
   758  					Return(nil).
   759  					Times(1)
   760  				mockRevocationStore.
   761  					EXPECT().
   762  					RevokeRefreshTokenMaybeGracePeriod(propagatedContext, gomock.Any(), gomock.Any()).
   763  					Return(nil).
   764  					Times(1)
   765  				mockRevocationStore.
   766  					EXPECT().
   767  					CreateAccessTokenSession(propagatedContext, gomock.Any(), gomock.Any()).
   768  					Return(fosite.ErrSerializationFailure).
   769  					Times(1)
   770  				mockTransactional.
   771  					EXPECT().
   772  					Rollback(propagatedContext).
   773  					Return(nil).
   774  					Times(1)
   775  			},
   776  			expectError: fosite.ErrInvalidRequest,
   777  		},
   778  		{
   779  			description: "transaction should be rolled back if call to `CreateAccessTokenSession` results in an error",
   780  			setup: func() {
   781  				mockTransactional.
   782  					EXPECT().
   783  					BeginTX(propagatedContext).
   784  					Return(propagatedContext, nil).
   785  					Times(1)
   786  				mockRevocationStore.
   787  					EXPECT().
   788  					GetRefreshTokenSession(propagatedContext, gomock.Any(), nil).
   789  					Return(request, nil).
   790  					Times(1)
   791  				mockRevocationStore.
   792  					EXPECT().
   793  					RevokeAccessToken(propagatedContext, gomock.Any()).
   794  					Return(nil).
   795  					Times(1)
   796  				mockRevocationStore.
   797  					EXPECT().
   798  					RevokeRefreshTokenMaybeGracePeriod(propagatedContext, gomock.Any(), gomock.Any()).
   799  					Return(nil).
   800  					Times(1)
   801  				mockRevocationStore.
   802  					EXPECT().
   803  					CreateAccessTokenSession(propagatedContext, gomock.Any(), gomock.Any()).
   804  					Return(errors.New("Whoops, a nasty database error occurred!")).
   805  					Times(1)
   806  				mockTransactional.
   807  					EXPECT().
   808  					Rollback(propagatedContext).
   809  					Return(nil).
   810  					Times(1)
   811  			},
   812  			expectError: fosite.ErrServerError,
   813  		},
   814  		{
   815  			description: "transaction should be rolled back if call to `CreateRefreshTokenSession` results in an error",
   816  			setup: func() {
   817  				request.GrantTypes = fosite.Arguments{"refresh_token"}
   818  				mockTransactional.
   819  					EXPECT().
   820  					BeginTX(propagatedContext).
   821  					Return(propagatedContext, nil).
   822  					Times(1)
   823  				mockRevocationStore.
   824  					EXPECT().
   825  					GetRefreshTokenSession(propagatedContext, gomock.Any(), nil).
   826  					Return(request, nil).
   827  					Times(1)
   828  				mockRevocationStore.
   829  					EXPECT().
   830  					RevokeAccessToken(propagatedContext, gomock.Any()).
   831  					Return(nil).
   832  					Times(1)
   833  				mockRevocationStore.
   834  					EXPECT().
   835  					RevokeRefreshTokenMaybeGracePeriod(propagatedContext, gomock.Any(), gomock.Any()).
   836  					Return(nil).
   837  					Times(1)
   838  				mockRevocationStore.
   839  					EXPECT().
   840  					CreateAccessTokenSession(propagatedContext, gomock.Any(), gomock.Any()).
   841  					Return(nil).
   842  					Times(1)
   843  				mockRevocationStore.
   844  					EXPECT().
   845  					CreateRefreshTokenSession(propagatedContext, gomock.Any(), gomock.Any()).
   846  					Return(errors.New("Whoops, a nasty database error occurred!")).
   847  					Times(1)
   848  				mockTransactional.
   849  					EXPECT().
   850  					Rollback(propagatedContext).
   851  					Return(nil).
   852  					Times(1)
   853  			},
   854  			expectError: fosite.ErrServerError,
   855  		},
   856  		{
   857  			description: "should result in a fosite.ErrInvalidRequest if call to `CreateRefreshTokenSession` results in " +
   858  				"a fosite.ErrSerializationFailure error",
   859  			setup: func() {
   860  				request.GrantTypes = fosite.Arguments{"refresh_token"}
   861  				mockTransactional.
   862  					EXPECT().
   863  					BeginTX(propagatedContext).
   864  					Return(propagatedContext, nil).
   865  					Times(1)
   866  				mockRevocationStore.
   867  					EXPECT().
   868  					GetRefreshTokenSession(propagatedContext, gomock.Any(), nil).
   869  					Return(request, nil).
   870  					Times(1)
   871  				mockRevocationStore.
   872  					EXPECT().
   873  					RevokeAccessToken(propagatedContext, gomock.Any()).
   874  					Return(nil).
   875  					Times(1)
   876  				mockRevocationStore.
   877  					EXPECT().
   878  					RevokeRefreshTokenMaybeGracePeriod(propagatedContext, gomock.Any(), gomock.Any()).
   879  					Return(nil).
   880  					Times(1)
   881  				mockRevocationStore.
   882  					EXPECT().
   883  					CreateAccessTokenSession(propagatedContext, gomock.Any(), gomock.Any()).
   884  					Return(nil).
   885  					Times(1)
   886  				mockRevocationStore.
   887  					EXPECT().
   888  					CreateRefreshTokenSession(propagatedContext, gomock.Any(), gomock.Any()).
   889  					Return(fosite.ErrSerializationFailure).
   890  					Times(1)
   891  				mockTransactional.
   892  					EXPECT().
   893  					Rollback(propagatedContext).
   894  					Return(nil).
   895  					Times(1)
   896  			},
   897  			expectError: fosite.ErrInvalidRequest,
   898  		},
   899  		{
   900  			description: "should result in a server error if transaction cannot be created",
   901  			setup: func() {
   902  				request.GrantTypes = fosite.Arguments{"refresh_token"}
   903  				mockTransactional.
   904  					EXPECT().
   905  					BeginTX(propagatedContext).
   906  					Return(nil, errors.New("Could not create transaction!")).
   907  					Times(1)
   908  			},
   909  			expectError: fosite.ErrServerError,
   910  		},
   911  		{
   912  			description: "should result in a server error if transaction cannot be rolled back",
   913  			setup: func() {
   914  				request.GrantTypes = fosite.Arguments{"refresh_token"}
   915  				mockTransactional.
   916  					EXPECT().
   917  					BeginTX(propagatedContext).
   918  					Return(propagatedContext, nil).
   919  					Times(1)
   920  				mockRevocationStore.
   921  					EXPECT().
   922  					GetRefreshTokenSession(propagatedContext, gomock.Any(), nil).
   923  					Return(nil, fosite.ErrNotFound).
   924  					Times(1)
   925  				mockTransactional.
   926  					EXPECT().
   927  					Rollback(propagatedContext).
   928  					Return(errors.New("Could not rollback transaction!")).
   929  					Times(1)
   930  			},
   931  			expectError: fosite.ErrServerError,
   932  		},
   933  		{
   934  			description: "should result in a server error if transaction cannot be committed",
   935  			setup: func() {
   936  				request.GrantTypes = fosite.Arguments{"refresh_token"}
   937  				mockTransactional.
   938  					EXPECT().
   939  					BeginTX(propagatedContext).
   940  					Return(propagatedContext, nil).
   941  					Times(1)
   942  				mockRevocationStore.
   943  					EXPECT().
   944  					GetRefreshTokenSession(propagatedContext, gomock.Any(), nil).
   945  					Return(request, nil).
   946  					Times(1)
   947  				mockRevocationStore.
   948  					EXPECT().
   949  					RevokeAccessToken(propagatedContext, gomock.Any()).
   950  					Return(nil).
   951  					Times(1)
   952  				mockRevocationStore.
   953  					EXPECT().
   954  					RevokeRefreshTokenMaybeGracePeriod(propagatedContext, gomock.Any(), gomock.Any()).
   955  					Return(nil).
   956  					Times(1)
   957  				mockRevocationStore.
   958  					EXPECT().
   959  					CreateAccessTokenSession(propagatedContext, gomock.Any(), gomock.Any()).
   960  					Return(nil).
   961  					Times(1)
   962  				mockRevocationStore.
   963  					EXPECT().
   964  					CreateRefreshTokenSession(propagatedContext, gomock.Any(), gomock.Any()).
   965  					Return(nil).
   966  					Times(1)
   967  				mockTransactional.
   968  					EXPECT().
   969  					Commit(propagatedContext).
   970  					Return(errors.New("Could not commit transaction!")).
   971  					Times(1)
   972  				mockTransactional.
   973  					EXPECT().
   974  					Rollback(propagatedContext).
   975  					Return(nil).
   976  					Times(1)
   977  			},
   978  			expectError: fosite.ErrServerError,
   979  		},
   980  		{
   981  			description: "should result in a `fosite.ErrInvalidRequest` if transaction fails to commit due to a " +
   982  				"`fosite.ErrSerializationFailure` error",
   983  			setup: func() {
   984  				request.GrantTypes = fosite.Arguments{"refresh_token"}
   985  				mockTransactional.
   986  					EXPECT().
   987  					BeginTX(propagatedContext).
   988  					Return(propagatedContext, nil).
   989  					Times(1)
   990  				mockRevocationStore.
   991  					EXPECT().
   992  					GetRefreshTokenSession(propagatedContext, gomock.Any(), nil).
   993  					Return(request, nil).
   994  					Times(1)
   995  				mockRevocationStore.
   996  					EXPECT().
   997  					RevokeAccessToken(propagatedContext, gomock.Any()).
   998  					Return(nil).
   999  					Times(1)
  1000  				mockRevocationStore.
  1001  					EXPECT().
  1002  					RevokeRefreshTokenMaybeGracePeriod(propagatedContext, gomock.Any(), gomock.Any()).
  1003  					Return(nil).
  1004  					Times(1)
  1005  				mockRevocationStore.
  1006  					EXPECT().
  1007  					CreateAccessTokenSession(propagatedContext, gomock.Any(), gomock.Any()).
  1008  					Return(nil).
  1009  					Times(1)
  1010  				mockRevocationStore.
  1011  					EXPECT().
  1012  					CreateRefreshTokenSession(propagatedContext, gomock.Any(), gomock.Any()).
  1013  					Return(nil).
  1014  					Times(1)
  1015  				mockTransactional.
  1016  					EXPECT().
  1017  					Commit(propagatedContext).
  1018  					Return(fosite.ErrSerializationFailure).
  1019  					Times(1)
  1020  				mockTransactional.
  1021  					EXPECT().
  1022  					Rollback(propagatedContext).
  1023  					Return(nil).
  1024  					Times(1)
  1025  			},
  1026  			expectError: fosite.ErrInvalidRequest,
  1027  		},
  1028  	} {
  1029  		t.Run(fmt.Sprintf("scenario=%s", testCase.description), func(t *testing.T) {
  1030  			ctrl := gomock.NewController(t)
  1031  			defer ctrl.Finish()
  1032  
  1033  			mockTransactional = internal.NewMockTransactional(ctrl)
  1034  			mockRevocationStore = internal.NewMockTokenRevocationStorage(ctrl)
  1035  			testCase.setup()
  1036  
  1037  			handler := RefreshTokenGrantHandler{
  1038  				// Notice how we are passing in a store that has support for transactions!
  1039  				TokenRevocationStorage: transactionalStore{
  1040  					mockTransactional,
  1041  					mockRevocationStore,
  1042  				},
  1043  				AccessTokenStrategy:      &hmacshaStrategy,
  1044  				RefreshTokenStrategy:     &hmacshaStrategy,
  1045  				AccessTokenLifespan:      time.Hour,
  1046  				ScopeStrategy:            fosite.HierarchicScopeStrategy,
  1047  				AudienceMatchingStrategy: fosite.DefaultAudienceMatchingStrategy,
  1048  			}
  1049  
  1050  			if err := handler.PopulateTokenEndpointResponse(propagatedContext, request, response); testCase.expectError != nil {
  1051  				assert.EqualError(t, err, testCase.expectError.Error())
  1052  			}
  1053  		})
  1054  	}
  1055  }
  1056  

View as plain text