diff --git a/handler/oauth2/flow_authorize_code_token_test.go b/handler/oauth2/flow_authorize_code_token_test.go index df15fae8..dd01a7fa 100644 --- a/handler/oauth2/flow_authorize_code_token_test.go +++ b/handler/oauth2/flow_authorize_code_token_test.go @@ -21,6 +21,205 @@ import ( "github.com/stretchr/testify/require" ) +func TestAuthorizeCode_PopulateTokenEndpointResponse(t *testing.T) { + for k, strategy := range map[string]CoreStrategy{ + "hmac": &hmacshaStrategy, + } { + t.Run("strategy="+k, func(t *testing.T) { + store := storage.NewMemoryStore() + + var h GenericCodeTokenEndpointHandler + + testCases := []struct { + areq *fosite.AccessRequest + description string + setup func(t *testing.T, areq *fosite.AccessRequest, config *fosite.Config) + check func(t *testing.T, aresp *fosite.AccessResponse) + expectErr error + }{ + { + description: "should fail because not responsible", + areq: &fosite.AccessRequest{ + GrantTypes: fosite.Arguments{"123"}, + }, + expectErr: fosite.ErrUnknownRequest, + }, + { + description: "should fail because authorization code cannot be retrieved", + areq: &fosite.AccessRequest{ + GrantTypes: fosite.Arguments{"authorization_code"}, + Request: fosite.Request{ + Form: url.Values{}, + Client: &fosite.DefaultClient{ + GrantTypes: fosite.Arguments{"authorization_code"}, + }, + Session: &fosite.DefaultSession{}, + RequestedAt: time.Now().UTC(), + }, + }, + setup: func(t *testing.T, areq *fosite.AccessRequest, config *fosite.Config) { + code, _, err := strategy.GenerateAuthorizeCode(context.Background(), nil) + require.NoError(t, err) + areq.Form.Set("code", code) + }, + expectErr: fosite.ErrServerError, + }, + { + description: "should fail because authorization code is expired", + areq: &fosite.AccessRequest{ + GrantTypes: fosite.Arguments{"authorization_code"}, + Request: fosite.Request{ + Form: url.Values{"code": []string{"foo.bar"}}, + Client: &fosite.DefaultClient{ + GrantTypes: fosite.Arguments{"authorization_code"}, + }, + Session: &fosite.DefaultSession{ + ExpiresAt: map[fosite.TokenType]time.Time{ + fosite.AuthorizeCode: time.Now().Add(-time.Hour).UTC(), + }, + }, + RequestedAt: time.Now().Add(-2 * time.Hour).UTC(), + }, + }, + setup: func(t *testing.T, areq *fosite.AccessRequest, config *fosite.Config) { + require.NoError(t, store.CreateAuthorizeCodeSession(context.Background(), "bar", areq)) + }, + expectErr: fosite.ErrInvalidRequest, + }, + { + description: "should pass with offline scope and refresh token", + areq: &fosite.AccessRequest{ + GrantTypes: fosite.Arguments{"authorization_code"}, + Request: fosite.Request{ + Form: url.Values{}, + Client: &fosite.DefaultClient{ + GrantTypes: fosite.Arguments{"authorization_code", "refresh_token"}, + }, + GrantedScope: fosite.Arguments{"foo", "offline"}, + Session: &fosite.DefaultSession{}, + RequestedAt: time.Now().UTC(), + }, + }, + setup: func(t *testing.T, areq *fosite.AccessRequest, config *fosite.Config) { + code, signature, err := strategy.GenerateAuthorizeCode(context.Background(), nil) + require.NoError(t, err) + areq.Form.Add("code", code) + + require.NoError(t, store.CreateAuthorizeCodeSession(context.Background(), signature, areq)) + }, + check: func(t *testing.T, aresp *fosite.AccessResponse) { + assert.NotEmpty(t, aresp.AccessToken) + assert.Equal(t, "bearer", aresp.TokenType) + assert.NotEmpty(t, aresp.GetExtra("refresh_token")) + assert.NotEmpty(t, aresp.GetExtra("expires_in")) + assert.Equal(t, "foo offline", aresp.GetExtra("scope")) + }, + }, + { + description: "should pass with refresh token always provided", + areq: &fosite.AccessRequest{ + GrantTypes: fosite.Arguments{"authorization_code"}, + Request: fosite.Request{ + Form: url.Values{}, + Client: &fosite.DefaultClient{ + GrantTypes: fosite.Arguments{"authorization_code", "refresh_token"}, + }, + GrantedScope: fosite.Arguments{"foo"}, + Session: &fosite.DefaultSession{}, + RequestedAt: time.Now().UTC(), + }, + }, + setup: func(t *testing.T, areq *fosite.AccessRequest, config *fosite.Config) { + config.RefreshTokenScopes = []string{} + code, signature, err := strategy.GenerateAuthorizeCode(context.Background(), nil) + require.NoError(t, err) + areq.Form.Add("code", code) + + require.NoError(t, store.CreateAuthorizeCodeSession(context.Background(), signature, areq)) + }, + check: func(t *testing.T, aresp *fosite.AccessResponse) { + assert.NotEmpty(t, aresp.AccessToken) + assert.Equal(t, "bearer", aresp.TokenType) + assert.NotEmpty(t, aresp.GetExtra("refresh_token")) + assert.NotEmpty(t, aresp.GetExtra("expires_in")) + assert.Equal(t, "foo", aresp.GetExtra("scope")) + }, + }, + { + description: "pass and response should not have refresh token", + areq: &fosite.AccessRequest{ + GrantTypes: fosite.Arguments{"authorization_code"}, + Request: fosite.Request{ + Form: url.Values{}, + Client: &fosite.DefaultClient{ + GrantTypes: fosite.Arguments{"authorization_code"}, + }, + GrantedScope: fosite.Arguments{"foo"}, + Session: &fosite.DefaultSession{}, + RequestedAt: time.Now().UTC(), + }, + }, + setup: func(t *testing.T, areq *fosite.AccessRequest, config *fosite.Config) { + code, sig, err := strategy.GenerateAuthorizeCode(context.Background(), nil) + require.NoError(t, err) + areq.Form.Add("code", code) + + require.NoError(t, store.CreateAuthorizeCodeSession(context.Background(), sig, areq)) + }, + check: func(t *testing.T, aresp *fosite.AccessResponse) { + assert.NotEmpty(t, aresp.AccessToken) + assert.Equal(t, "bearer", aresp.TokenType) + assert.Empty(t, aresp.GetExtra("refresh_token")) + assert.NotEmpty(t, aresp.GetExtra("expires_in")) + assert.Equal(t, "foo", aresp.GetExtra("scope")) + }, + }, + } + + for _, testCase := range testCases { + t.Run("case="+testCase.description, func(t *testing.T) { + config := &fosite.Config{ + ScopeStrategy: fosite.HierarchicScopeStrategy, + AudienceMatchingStrategy: fosite.DefaultAudienceMatchingStrategy, + AccessTokenLifespan: time.Minute, + RefreshTokenScopes: []string{"offline"}, + } + h = GenericCodeTokenEndpointHandler{ + AccessRequestValidator: &AuthorizeExplicitGrantAccessRequestValidator{}, + CodeHandler: &AuthorizeCodeHandler{ + AuthorizeCodeStrategy: strategy, + }, + SessionHandler: &AuthorizeExplicitGrantSessionHandler{ + AuthorizeCodeStorage: store, + }, + AccessTokenStrategy: strategy, + RefreshTokenStrategy: strategy, + CoreStorage: store, + Config: config, + } + + if testCase.setup != nil { + testCase.setup(t, testCase.areq, config) + } + + aresp := fosite.NewAccessResponse() + err := h.PopulateTokenEndpointResponse(context.Background(), testCase.areq, aresp) + + if testCase.expectErr != nil { + require.EqualError(t, err, testCase.expectErr.Error(), "%+v", err) + } else { + require.NoError(t, err, "%+v", err) + } + + if testCase.check != nil { + testCase.check(t, aresp) + } + }) + } + }) + } +} + func TestAuthorizeCode_HandleTokenEndpointRequest(t *testing.T) { for k, strategy := range map[string]CoreStrategy{ "hmac": &hmacshaStrategy, @@ -62,7 +261,7 @@ func TestAuthorizeCode_HandleTokenEndpointRequest(t *testing.T) { { description: "should fail because client is not granted the correct grant type", areq: &fosite.AccessRequest{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)}, + GrantTypes: fosite.Arguments{"authorization_code"}, Request: fosite.Request{ Client: &fosite.DefaultClient{ID: "foo", GrantTypes: []string{""}}, Session: &fosite.DefaultSession{}, @@ -74,9 +273,9 @@ func TestAuthorizeCode_HandleTokenEndpointRequest(t *testing.T) { { description: "should fail because authorization code cannot be retrieved", areq: &fosite.AccessRequest{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)}, + GrantTypes: fosite.Arguments{"authorization_code"}, Request: fosite.Request{ - Client: &fosite.DefaultClient{GrantTypes: []string{string(fosite.GrantTypeAuthorizationCode)}}, + Client: &fosite.DefaultClient{GrantTypes: []string{"authorization_code"}}, Session: &fosite.DefaultSession{}, RequestedAt: time.Now().UTC(), }, @@ -91,10 +290,10 @@ func TestAuthorizeCode_HandleTokenEndpointRequest(t *testing.T) { { description: "should fail because authorization code is expired", areq: &fosite.AccessRequest{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)}, + GrantTypes: fosite.Arguments{"authorization_code"}, Request: fosite.Request{ Form: url.Values{"code": {"foo.bar"}}, - Client: &fosite.DefaultClient{ID: "foo", GrantTypes: []string{string(fosite.GrantTypeAuthorizationCode)}}, + Client: &fosite.DefaultClient{ID: "foo", GrantTypes: []string{"authorization_code"}}, Session: &fosite.DefaultSession{}, RequestedAt: time.Now().UTC(), }, @@ -122,9 +321,9 @@ func TestAuthorizeCode_HandleTokenEndpointRequest(t *testing.T) { { description: "should fail because client mismatch", areq: &fosite.AccessRequest{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)}, + GrantTypes: fosite.Arguments{"authorization_code"}, Request: fosite.Request{ - Client: &fosite.DefaultClient{ID: "foo", GrantTypes: []string{string(fosite.GrantTypeAuthorizationCode)}}, + Client: &fosite.DefaultClient{ID: "foo", GrantTypes: []string{"authorization_code"}}, Session: &fosite.DefaultSession{}, RequestedAt: time.Now().UTC(), }, @@ -151,16 +350,16 @@ func TestAuthorizeCode_HandleTokenEndpointRequest(t *testing.T) { { description: "should fail because redirect uri was set during /authorize call, but not in /token call", areq: &fosite.AccessRequest{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)}, + GrantTypes: fosite.Arguments{"authorization_code"}, Request: fosite.Request{ - Client: &fosite.DefaultClient{ID: "foo", GrantTypes: []string{string(fosite.GrantTypeAuthorizationCode)}}, + Client: &fosite.DefaultClient{ID: "foo", GrantTypes: []string{"authorization_code"}}, Session: &fosite.DefaultSession{}, RequestedAt: time.Now().UTC(), }, }, authreq: &fosite.AuthorizeRequest{ Request: fosite.Request{ - Client: &fosite.DefaultClient{ID: "foo", GrantTypes: []string{string(fosite.GrantTypeAuthorizationCode)}}, + Client: &fosite.DefaultClient{ID: "foo", GrantTypes: []string{"authorization_code"}}, Form: url.Values{"redirect_uri": []string{"request-redir"}}, Session: &fosite.DefaultSession{ ExpiresAt: map[fosite.TokenType]time.Time{ @@ -181,9 +380,9 @@ func TestAuthorizeCode_HandleTokenEndpointRequest(t *testing.T) { { description: "should pass", areq: &fosite.AccessRequest{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)}, + GrantTypes: fosite.Arguments{"authorization_code"}, Request: fosite.Request{ - Client: &fosite.DefaultClient{ID: "foo", GrantTypes: []string{string(fosite.GrantTypeAuthorizationCode)}}, + Client: &fosite.DefaultClient{ID: "foo", GrantTypes: []string{"authorization_code"}}, Form: url.Values{"redirect_uri": []string{"request-redir"}}, Session: &fosite.DefaultSession{}, RequestedAt: time.Now().UTC(), @@ -191,7 +390,7 @@ func TestAuthorizeCode_HandleTokenEndpointRequest(t *testing.T) { }, authreq: &fosite.AuthorizeRequest{ Request: fosite.Request{ - Client: &fosite.DefaultClient{ID: "foo", GrantTypes: []string{string(fosite.GrantTypeAuthorizationCode)}}, + Client: &fosite.DefaultClient{ID: "foo", GrantTypes: []string{"authorization_code"}}, Session: &fosite.DefaultSession{}, RequestedAt: time.Now().UTC(), }, @@ -207,17 +406,17 @@ func TestAuthorizeCode_HandleTokenEndpointRequest(t *testing.T) { { description: "should fail because code has been used already", areq: &fosite.AccessRequest{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)}, + GrantTypes: fosite.Arguments{"authorization_code"}, Request: fosite.Request{ Form: url.Values{}, - Client: &fosite.DefaultClient{ID: "foo", GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)}}, + Client: &fosite.DefaultClient{ID: "foo", GrantTypes: fosite.Arguments{"authorization_code"}}, Session: &fosite.DefaultSession{}, RequestedAt: time.Now().UTC(), }, }, authreq: &fosite.AuthorizeRequest{ Request: fosite.Request{ - Client: &fosite.DefaultClient{ID: "foo", GrantTypes: []string{string(fosite.GrantTypeAuthorizationCode)}}, + Client: &fosite.DefaultClient{ID: "foo", GrantTypes: []string{"authorization_code"}}, Session: &fosite.DefaultSession{}, RequestedAt: time.Now().UTC(), }, @@ -257,215 +456,16 @@ func TestAuthorizeCode_HandleTokenEndpointRequest(t *testing.T) { } } -func TestAuthorizeCode_PopulateTokenEndpointResponse(t *testing.T) { - for k, strategy := range map[string]CoreStrategy{ - "hmac": &hmacshaStrategy, - } { - t.Run("strategy="+k, func(t *testing.T) { - store := storage.NewMemoryStore() - - var h GenericCodeTokenEndpointHandler - - testCases := []struct { - areq *fosite.AccessRequest - description string - setup func(t *testing.T, areq *fosite.AccessRequest, config *fosite.Config) - check func(t *testing.T, aresp *fosite.AccessResponse) - expectErr error - }{ - { - description: "should fail because not responsible", - areq: &fosite.AccessRequest{ - GrantTypes: fosite.Arguments{"123"}, - }, - expectErr: fosite.ErrUnknownRequest, - }, - { - description: "should fail because authorization code cannot be retrieved", - areq: &fosite.AccessRequest{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)}, - Request: fosite.Request{ - Form: url.Values{}, - Client: &fosite.DefaultClient{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)}, - }, - Session: &fosite.DefaultSession{}, - RequestedAt: time.Now().UTC(), - }, - }, - setup: func(t *testing.T, areq *fosite.AccessRequest, config *fosite.Config) { - code, _, err := strategy.GenerateAuthorizeCode(context.Background(), nil) - require.NoError(t, err) - areq.Form.Set("code", code) - }, - expectErr: fosite.ErrServerError, - }, - { - description: "should fail because authorization code is expired", - areq: &fosite.AccessRequest{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)}, - Request: fosite.Request{ - Form: url.Values{"code": []string{"foo.bar"}}, - Client: &fosite.DefaultClient{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)}, - }, - Session: &fosite.DefaultSession{ - ExpiresAt: map[fosite.TokenType]time.Time{ - fosite.AuthorizeCode: time.Now().Add(-time.Hour).UTC(), - }, - }, - RequestedAt: time.Now().Add(-2 * time.Hour).UTC(), - }, - }, - setup: func(t *testing.T, areq *fosite.AccessRequest, config *fosite.Config) { - require.NoError(t, store.CreateAuthorizeCodeSession(context.Background(), "bar", areq)) - }, - expectErr: fosite.ErrInvalidRequest, - }, - { - description: "should pass with offline scope and refresh token", - areq: &fosite.AccessRequest{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)}, - Request: fosite.Request{ - Form: url.Values{}, - Client: &fosite.DefaultClient{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode), string(fosite.GrantTypeRefreshToken)}, - }, - GrantedScope: fosite.Arguments{"foo", "offline"}, - Session: &fosite.DefaultSession{}, - RequestedAt: time.Now().UTC(), - }, - }, - setup: func(t *testing.T, areq *fosite.AccessRequest, config *fosite.Config) { - code, signature, err := strategy.GenerateAuthorizeCode(context.Background(), nil) - require.NoError(t, err) - areq.Form.Add("code", code) - - require.NoError(t, store.CreateAuthorizeCodeSession(context.Background(), signature, areq)) - }, - check: func(t *testing.T, aresp *fosite.AccessResponse) { - assert.NotEmpty(t, aresp.AccessToken) - assert.Equal(t, "bearer", aresp.TokenType) - assert.NotEmpty(t, aresp.GetExtra("refresh_token")) - assert.NotEmpty(t, aresp.GetExtra("expires_in")) - assert.Equal(t, "foo offline", aresp.GetExtra("scope")) - }, - }, - { - description: "should pass with refresh token always provided", - areq: &fosite.AccessRequest{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)}, - Request: fosite.Request{ - Form: url.Values{}, - Client: &fosite.DefaultClient{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode), string(fosite.GrantTypeRefreshToken)}, - }, - GrantedScope: fosite.Arguments{"foo"}, - Session: &fosite.DefaultSession{}, - RequestedAt: time.Now().UTC(), - }, - }, - setup: func(t *testing.T, areq *fosite.AccessRequest, config *fosite.Config) { - config.RefreshTokenScopes = []string{} - code, signature, err := strategy.GenerateAuthorizeCode(context.Background(), nil) - require.NoError(t, err) - areq.Form.Add("code", code) - - require.NoError(t, store.CreateAuthorizeCodeSession(context.Background(), signature, areq)) - }, - check: func(t *testing.T, aresp *fosite.AccessResponse) { - assert.NotEmpty(t, aresp.AccessToken) - assert.Equal(t, "bearer", aresp.TokenType) - assert.NotEmpty(t, aresp.GetExtra("refresh_token")) - assert.NotEmpty(t, aresp.GetExtra("expires_in")) - assert.Equal(t, "foo", aresp.GetExtra("scope")) - }, - }, - { - description: "pass and response should not have refresh token", - areq: &fosite.AccessRequest{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)}, - Request: fosite.Request{ - Form: url.Values{}, - Client: &fosite.DefaultClient{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)}, - }, - GrantedScope: fosite.Arguments{"foo"}, - Session: &fosite.DefaultSession{}, - RequestedAt: time.Now().UTC(), - }, - }, - setup: func(t *testing.T, areq *fosite.AccessRequest, config *fosite.Config) { - code, sig, err := strategy.GenerateAuthorizeCode(context.Background(), nil) - require.NoError(t, err) - areq.Form.Add("code", code) - - require.NoError(t, store.CreateAuthorizeCodeSession(context.Background(), sig, areq)) - }, - check: func(t *testing.T, aresp *fosite.AccessResponse) { - assert.NotEmpty(t, aresp.AccessToken) - assert.Equal(t, "bearer", aresp.TokenType) - assert.Empty(t, aresp.GetExtra("refresh_token")) - assert.NotEmpty(t, aresp.GetExtra("expires_in")) - assert.Equal(t, "foo", aresp.GetExtra("scope")) - }, - }, - } - - for _, testCase := range testCases { - t.Run("case="+testCase.description, func(t *testing.T) { - config := &fosite.Config{ - ScopeStrategy: fosite.HierarchicScopeStrategy, - AudienceMatchingStrategy: fosite.DefaultAudienceMatchingStrategy, - AccessTokenLifespan: time.Minute, - RefreshTokenScopes: []string{"offline"}, - } - h = GenericCodeTokenEndpointHandler{ - AccessRequestValidator: &AuthorizeExplicitGrantAccessRequestValidator{}, - CodeHandler: &AuthorizeCodeHandler{ - AuthorizeCodeStrategy: strategy, - }, - SessionHandler: &AuthorizeExplicitGrantSessionHandler{ - AuthorizeCodeStorage: store, - }, - AccessTokenStrategy: strategy, - RefreshTokenStrategy: strategy, - CoreStorage: store, - Config: config, - } - - if testCase.setup != nil { - testCase.setup(t, testCase.areq, config) - } - - aresp := fosite.NewAccessResponse() - err := h.PopulateTokenEndpointResponse(context.Background(), testCase.areq, aresp) - - if testCase.expectErr != nil { - require.EqualError(t, err, testCase.expectErr.Error(), "%+v", err) - } else { - require.NoError(t, err, "%+v", err) - } - - if testCase.check != nil { - testCase.check(t, aresp) - } - }) - } - }) - } -} - func TestAuthorizeCodeTransactional_HandleTokenEndpointRequest(t *testing.T) { var mockTransactional *internal.MockTransactional var mockCoreStore *internal.MockCoreStorage var mockAuthorizeStore *internal.MockAuthorizeCodeStorage strategy := hmacshaStrategy request := &fosite.AccessRequest{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)}, + GrantTypes: fosite.Arguments{"authorization_code"}, Request: fosite.Request{ Client: &fosite.DefaultClient{ - GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode), string(fosite.GrantTypeRefreshToken)}, + GrantTypes: fosite.Arguments{"authorization_code", "refresh_token"}, }, GrantedScope: fosite.Arguments{"offline"}, Session: &fosite.DefaultSession{},