From f1930124e072153f9d5ec8dc4f14733f9bdc20a1 Mon Sep 17 00:00:00 2001 From: "Aeneas Rekkas (arekkas)" Date: Fri, 14 Oct 2016 11:42:39 +0200 Subject: [PATCH] docs: document reasoning for interface{} in compose package - closes #94 --- .travis.yml | 1 + access_error.go | 2 +- client.go | 2 +- compose/compose.go | 14 +-- compose/compose_oauth2.go | 15 +++ generate-mocks.sh | 2 +- glide.lock | 7 +- handler/oauth2/flow_refresh_test.go | 6 +- handler/oauth2/introspector.go | 93 ++++++++++++++++ ...validator_test.go => introspector_test.go} | 12 +++ .../oauth2/{flow_revoke.go => revocation.go} | 3 +- ...evoke_storage.go => revocation_storage.go} | 4 +- ...flow_revoke_test.go => revocation_test.go} | 4 +- handler/oauth2/validator.go | 71 ------------- handler/openid/flow_hybrid_test.go | 4 +- handler/openid/flow_implicit_test.go | 2 +- integration/helper_endpoints_test.go | 38 ++++++- integration/helper_setup_test.go | 5 +- integration/introspect_token_test.go | 91 ++++++++++++++++ integration/revoke_token_test.go | 46 ++++++++ internal/oauth2_revoke_storage.go | 12 ++- internal/validator.go | 41 ------- introspect_test.go | 4 +- introspection_request_handler.go | 36 ++++--- introspection_request_handler_test.go | 100 ++++++++++++++++++ introspection_response_writer.go | 35 +++--- introspection_response_writer_test.go | 40 +++++++ request_test.go | 4 +- revoke_handler.go | 3 +- storage/memory.go | 23 ++-- token/hmac/hmacsha.go | 2 +- 31 files changed, 534 insertions(+), 188 deletions(-) create mode 100644 handler/oauth2/introspector.go rename handler/oauth2/{validator_test.go => introspector_test.go} (69%) rename handler/oauth2/{flow_revoke.go => revocation.go} (94%) rename handler/oauth2/{flow_revoke_storage.go => revocation_storage.go} (87%) rename handler/oauth2/{flow_revoke_test.go => revocation_test.go} (98%) delete mode 100644 handler/oauth2/validator.go create mode 100644 integration/introspect_token_test.go create mode 100644 integration/revoke_token_test.go delete mode 100644 internal/validator.go create mode 100644 introspection_request_handler_test.go create mode 100644 introspection_response_writer_test.go diff --git a/.travis.yml b/.travis.yml index fc9b5d77..27aaf298 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: go go: - 1.5 - 1.6 + - 1.7 env: - GO15VENDOREXPERIMENT=1 diff --git a/access_error.go b/access_error.go index f2e0ef3f..51008b6a 100644 --- a/access_error.go +++ b/access_error.go @@ -22,4 +22,4 @@ func writeJsonError(rw http.ResponseWriter, err error) { rw.WriteHeader(rfcerr.StatusCode) rw.Write(js) -} \ No newline at end of file +} diff --git a/client.go b/client.go index 3365585b..c4f910c1 100644 --- a/client.go +++ b/client.go @@ -32,7 +32,7 @@ type DefaultClient struct { GrantTypes []string `json:"grant_types"` ResponseTypes []string `json:"response_types"` Scopes []string `json:"scopes"` - Public bool `json:"public"` + Public bool `json:"public"` } func (c *DefaultClient) GetID() string { diff --git a/compose/compose.go b/compose/compose.go index 8a2f48ca..0dc650bd 100644 --- a/compose/compose.go +++ b/compose/compose.go @@ -28,15 +28,17 @@ type handler func(config *Config, storage interface{}, strategy interface{}) int // OAuth2ClientCredentialsGrantFactory, // // for a complete list refer to the docs of this package // ) +// +// Compose makes use of interface{} types in order to be able to handle a all types of stores, strategies and handlers. func Compose(config *Config, storage interface{}, strategy interface{}, handlers ...handler) fosite.OAuth2Provider { f := &fosite.Fosite{ Store: storage.(fosite.Storage), - AuthorizeEndpointHandlers: fosite.AuthorizeEndpointHandlers{}, - TokenEndpointHandlers: fosite.TokenEndpointHandlers{}, - TokenIntrospectionHandlers: fosite.TokenIntrospectionHandlers{}, - RevocationHandlers: fosite.RevocationHandlers{}, - Hasher: &fosite.BCrypt{WorkFactor: config.GetHashCost()}, - ScopeStrategy: fosite.HierarchicScopeStrategy, + AuthorizeEndpointHandlers: fosite.AuthorizeEndpointHandlers{}, + TokenEndpointHandlers: fosite.TokenEndpointHandlers{}, + TokenIntrospectionHandlers: fosite.TokenIntrospectionHandlers{}, + RevocationHandlers: fosite.RevocationHandlers{}, + Hasher: &fosite.BCrypt{WorkFactor: config.GetHashCost()}, + ScopeStrategy: fosite.HierarchicScopeStrategy, } for _, h := range handlers { diff --git a/compose/compose_oauth2.go b/compose/compose_oauth2.go index d10bbd01..3a45b572 100644 --- a/compose/compose_oauth2.go +++ b/compose/compose_oauth2.go @@ -9,7 +9,10 @@ import ( // an access token, refresh token and authorize code validator. func OAuth2AuthorizeExplicitFactory(config *Config, storage interface{}, strategy interface{}) interface{} { return &struct { + // register the handler *oauth2.AuthorizeExplicitGrantHandler + + // also register the validator for access tokens *oauth2.CoreValidator }{ AuthorizeExplicitGrantHandler: &oauth2.AuthorizeExplicitGrantHandler{ @@ -33,7 +36,10 @@ func OAuth2AuthorizeExplicitFactory(config *Config, storage interface{}, strateg // an access token, refresh token and authorize code validator. func OAuth2ClientCredentialsGrantFactory(config *Config, storage interface{}, strategy interface{}) interface{} { return &struct { + // register the handler *oauth2.ClientCredentialsGrantHandler + + // also register the validator for access tokens *oauth2.CoreValidator }{ ClientCredentialsGrantHandler: &oauth2.ClientCredentialsGrantHandler{ @@ -56,7 +62,10 @@ func OAuth2ClientCredentialsGrantFactory(config *Config, storage interface{}, st // an access token, refresh token and authorize code validator. func OAuth2RefreshTokenGrantFactory(config *Config, storage interface{}, strategy interface{}) interface{} { return &struct { + // register the handler *oauth2.RefreshTokenGrantHandler + + // also register the validator for access tokens *oauth2.CoreValidator }{ RefreshTokenGrantHandler: &oauth2.RefreshTokenGrantHandler{ @@ -77,7 +86,10 @@ func OAuth2RefreshTokenGrantFactory(config *Config, storage interface{}, strateg // an access token, refresh token and authorize code validator. func OAuth2AuthorizeImplicitFactory(config *Config, storage interface{}, strategy interface{}) interface{} { return &struct { + // register the handler *oauth2.AuthorizeImplicitGrantTypeHandler + + // also register the validator for access tokens *oauth2.CoreValidator }{ AuthorizeImplicitGrantTypeHandler: &oauth2.AuthorizeImplicitGrantTypeHandler{ @@ -98,7 +110,10 @@ func OAuth2AuthorizeImplicitFactory(config *Config, storage interface{}, strateg // an access token, refresh token and authorize code validator. func OAuth2ResourceOwnerPasswordCredentialsFactory(config *Config, storage interface{}, strategy interface{}) interface{} { return &struct { + // register the handler *oauth2.ResourceOwnerPasswordCredentialsGrantHandler + + // also register the validator for access tokens *oauth2.CoreValidator }{ ResourceOwnerPasswordCredentialsGrantHandler: &oauth2.ResourceOwnerPasswordCredentialsGrantHandler{ diff --git a/generate-mocks.sh b/generate-mocks.sh index 8e82f63c..576b1961 100755 --- a/generate-mocks.sh +++ b/generate-mocks.sh @@ -21,7 +21,7 @@ mockgen -package internal -destination internal/id_token_strategy.go github.com/ mockgen -package internal -destination internal/authorize_handler.go github.com/ory-am/fosite AuthorizeEndpointHandler mockgen -package internal -destination internal/revoke_handler.go github.com/ory-am/fosite RevocationHandler mockgen -package internal -destination internal/token_handler.go github.com/ory-am/fosite TokenEndpointHandler -mockgen -package internal -destination internal/validator.go github.com/ory-am/fosite TokenValidator +mockgen -package internal -destination internal/introspector.go github.com/ory-am/fosite TokenIntrospector mockgen -package internal -destination internal/client.go github.com/ory-am/fosite Client mockgen -package internal -destination internal/request.go github.com/ory-am/fosite Requester mockgen -package internal -destination internal/access_request.go github.com/ory-am/fosite AccessRequester diff --git a/glide.lock b/glide.lock index d204c4da..13a392d1 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: 765b5e035d3037868b068d86ba286c974ce9d29b38ba4a69194bac62d32898cf -updated: 2016-10-14T11:38:29.5300255+02:00 +updated: 2016-10-16T18:44:47.5114091+02:00 imports: - name: github.com/asaskevich/govalidator version: 7b3beb6df3c42abd3509abfc3bcacc0fbfb7c877 @@ -26,6 +26,7 @@ imports: version: 075e191f18186a8ff2becaf64478e30f4545cdad subpackages: - context + - publicsuffix testImports: - name: github.com/davecgh/go-spew version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9 @@ -41,12 +42,16 @@ testImports: version: 1ea25387ff6f684839d82767c1733ff4d4d15d0a - name: github.com/gorilla/mux version: 0eeaf8392f5b04950925b8a69fe70f110fa7cbfc +- name: github.com/moul/http2curl + version: b1479103caacaa39319f75e7f57fc545287fca0d - name: github.com/oleiade/reflections version: ec27669d960a245738b87ffa688dac28fa288c33 - name: github.com/ory-am/common version: d93c852f2d09c219fd058756caf67bbdf8cf4be4 subpackages: - pkg +- name: github.com/parnurzeal/gorequest + version: b64673b971a1742b8ba91f228f1c029632d4b686 - name: github.com/pmezard/go-difflib version: d8ed2627bdf02c080bf22230dbb337003b7aba2d subpackages: diff --git a/handler/oauth2/flow_refresh_test.go b/handler/oauth2/flow_refresh_test.go index a7629200..d9276133 100644 --- a/handler/oauth2/flow_refresh_test.go +++ b/handler/oauth2/flow_refresh_test.go @@ -56,7 +56,7 @@ func TestRefreshFlow_HandleTokenEndpointRequest(t *testing.T) { description: "should fail because validation failed", setup: func() { store.EXPECT().GetRefreshTokenSession(nil, "refreshtokensig", nil).Return(&fosite.Request{ - GrantedScopes:[]string{"offline"}, + GrantedScopes: []string{"offline"}, }, nil) chgen.EXPECT().ValidateRefreshToken(nil, areq, "some.refreshtokensig").Return(errors.New("")) }, @@ -70,8 +70,8 @@ func TestRefreshFlow_HandleTokenEndpointRequest(t *testing.T) { GrantTypes: fosite.Arguments{"refresh_token"}, } store.EXPECT().GetRefreshTokenSession(nil, "refreshtokensig", nil).Return(&fosite.Request{ - Client: &fosite.DefaultClient{ID: ""}, - GrantedScopes:[]string{"offline"}, + Client: &fosite.DefaultClient{ID: ""}, + GrantedScopes: []string{"offline"}, }, nil) chgen.EXPECT().ValidateRefreshToken(nil, areq, "some.refreshtokensig").AnyTimes().Return(nil) }, diff --git a/handler/oauth2/introspector.go b/handler/oauth2/introspector.go new file mode 100644 index 00000000..ab1f84c4 --- /dev/null +++ b/handler/oauth2/introspector.go @@ -0,0 +1,93 @@ +package oauth2 + +import ( + "github.com/ory-am/fosite" + "github.com/pkg/errors" + "golang.org/x/net/context" +) + +type CoreValidator struct { + CoreStrategy + CoreStorage + ScopeStrategy fosite.ScopeStrategy +} + +func (c *CoreValidator) IntrospectToken(ctx context.Context, token string, tokenType fosite.TokenType, accessRequest fosite.AccessRequester, scopes []string) (err error) { + switch tokenType { + case fosite.RefreshToken: + if err = c.introspectRefreshToken(ctx, token, accessRequest); err == nil { + return err + } else if err = c.introspectAuthorizeCode(ctx, token, accessRequest); err == nil { + return err + } else if err = c.introspectAccessToken(ctx, token, accessRequest, scopes); err == nil { + return err + } + return err + case fosite.AuthorizeCode: + if err = c.introspectAuthorizeCode(ctx, token, accessRequest); err == nil { + return err + } else if err := c.introspectAccessToken(ctx, token, accessRequest, scopes); err == nil { + return err + } else if err := c.introspectRefreshToken(ctx, token, accessRequest); err == nil { + return err + } + return err + } + if err = c.introspectAccessToken(ctx, token, accessRequest, scopes); err == nil { + return err + } else if err := c.introspectRefreshToken(ctx, token, accessRequest); err == nil { + return err + } else if err := c.introspectAuthorizeCode(ctx, token, accessRequest); err == nil { + return err + } + return err +} + +func (c *CoreValidator) introspectAccessToken(ctx context.Context, token string, accessRequest fosite.AccessRequester, scopes []string) error { + sig := c.CoreStrategy.AccessTokenSignature(token) + or, err := c.CoreStorage.GetAccessTokenSession(ctx, sig, accessRequest.GetSession()) + if err != nil { + return errors.Wrap(fosite.ErrRequestUnauthorized, err.Error()) + } else if err := c.CoreStrategy.ValidateAccessToken(ctx, or, token); err != nil { + return err + } + + for _, scope := range scopes { + if scope == "" { + continue + } + + if !c.ScopeStrategy(or.GetGrantedScopes(), scope) { + return errors.Wrap(fosite.ErrInvalidScope, "") + } + } + + accessRequest.Merge(or) + return nil +} + +func (c *CoreValidator) introspectRefreshToken(ctx context.Context, token string, accessRequest fosite.AccessRequester) error { + sig := c.CoreStrategy.RefreshTokenSignature(token) + if or, err := c.CoreStorage.GetRefreshTokenSession(ctx, sig, accessRequest.GetSession()); err != nil { + return errors.Wrap(fosite.ErrRequestUnauthorized, err.Error()) + } else if err := c.CoreStrategy.ValidateRefreshToken(ctx, or, token); err != nil { + return err + } else { + accessRequest.Merge(or) + } + + return nil +} + +func (c *CoreValidator) introspectAuthorizeCode(ctx context.Context, token string, accessRequest fosite.AccessRequester) error { + sig := c.CoreStrategy.AuthorizeCodeSignature(token) + if or, err := c.CoreStorage.GetAuthorizeCodeSession(ctx, sig, accessRequest.GetSession()); err != nil { + return errors.Wrap(err, fosite.ErrRequestUnauthorized.Error()) + } else if err := c.CoreStrategy.ValidateAuthorizeCode(ctx, or, token); err != nil { + return err + } else { + accessRequest.Merge(or) + } + + return nil +} diff --git a/handler/oauth2/validator_test.go b/handler/oauth2/introspector_test.go similarity index 69% rename from handler/oauth2/validator_test.go rename to handler/oauth2/introspector_test.go index 86379c5f..966b54f9 100644 --- a/handler/oauth2/validator_test.go +++ b/handler/oauth2/introspector_test.go @@ -35,6 +35,10 @@ func TestIntrospectToken(t *testing.T) { httpreq.Header.Set("Authorization", "bearer") chgen.EXPECT().AccessTokenSignature("").Return("") store.EXPECT().GetAccessTokenSession(nil, "", nil).Return(nil, errors.New("")) + chgen.EXPECT().RefreshTokenSignature("").Return("") + store.EXPECT().GetRefreshTokenSession(nil, "", nil).Return(nil, errors.New("")) + chgen.EXPECT().AuthorizeCodeSignature("").Return("") + store.EXPECT().GetAuthorizeCodeSession(nil, "", nil).Return(nil, errors.New("")) }, expectErr: fosite.ErrRequestUnauthorized, }, @@ -44,6 +48,10 @@ func TestIntrospectToken(t *testing.T) { httpreq.Header.Set("Authorization", "bearer 1234") chgen.EXPECT().AccessTokenSignature("1234").AnyTimes().Return("asdf") store.EXPECT().GetAccessTokenSession(nil, "asdf", nil).Return(nil, errors.New("")) + chgen.EXPECT().RefreshTokenSignature("1234").Return("asdf") + store.EXPECT().GetRefreshTokenSession(nil, "asdf", nil).Return(nil, errors.New("")) + chgen.EXPECT().AuthorizeCodeSignature("1234").Return("asdf") + store.EXPECT().GetAuthorizeCodeSession(nil, "asdf", nil).Return(nil, errors.New("")) }, expectErr: fosite.ErrRequestUnauthorized, }, @@ -52,6 +60,10 @@ func TestIntrospectToken(t *testing.T) { setup: func() { store.EXPECT().GetAccessTokenSession(nil, "asdf", nil).AnyTimes().Return(areq, nil) chgen.EXPECT().ValidateAccessToken(nil, areq, "1234").Return(errors.Wrap(fosite.ErrTokenExpired, "")) + chgen.EXPECT().RefreshTokenSignature("1234").Return("asdf") + store.EXPECT().GetRefreshTokenSession(nil, "asdf", nil).Return(nil, errors.New("")) + chgen.EXPECT().AuthorizeCodeSignature("1234").Return("asdf") + store.EXPECT().GetAuthorizeCodeSession(nil, "asdf", nil).Return(nil, errors.New("")) }, expectErr: fosite.ErrTokenExpired, }, diff --git a/handler/oauth2/flow_revoke.go b/handler/oauth2/revocation.go similarity index 94% rename from handler/oauth2/flow_revoke.go rename to handler/oauth2/revocation.go index ce321470..f603b536 100644 --- a/handler/oauth2/flow_revoke.go +++ b/handler/oauth2/revocation.go @@ -2,7 +2,6 @@ package oauth2 import ( "github.com/ory-am/fosite" - "github.com/pkg/errors" "golang.org/x/net/context" ) @@ -39,7 +38,7 @@ func (r *TokenRevocationHandler) RevokeToken(ctx context.Context, token string, ar, err = discoveryFuncs[1]() } if err != nil { - return errors.Wrap(fosite.ErrNotFound, "Nothing to revoke") + return nil } requestID := ar.GetID() diff --git a/handler/oauth2/flow_revoke_storage.go b/handler/oauth2/revocation_storage.go similarity index 87% rename from handler/oauth2/flow_revoke_storage.go rename to handler/oauth2/revocation_storage.go index ed2ad978..69cdc3e9 100644 --- a/handler/oauth2/flow_revoke_storage.go +++ b/handler/oauth2/revocation_storage.go @@ -17,12 +17,12 @@ type TokenRevocationStorage interface { // revocation of access tokens, then the authorization server SHOULD // also invalidate all access tokens based on the same authorization // grant (see Implementation Note). - RevokeRefreshToken(ctx context.Context, requestID string) + RevokeRefreshToken(ctx context.Context, requestID string) error // RevokeAccessToken revokes an access token as specified in: // https://tools.ietf.org/html/rfc7009#section-2.1 // If the token passed to the request // is an access token, the server MAY revoke the respective refresh // token as well. - RevokeAccessToken(ctx context.Context, requestID string) + RevokeAccessToken(ctx context.Context, requestID string) error } diff --git a/handler/oauth2/flow_revoke_test.go b/handler/oauth2/revocation_test.go similarity index 98% rename from handler/oauth2/flow_revoke_test.go rename to handler/oauth2/revocation_test.go index 03af5735..cd35059d 100644 --- a/handler/oauth2/flow_revoke_test.go +++ b/handler/oauth2/revocation_test.go @@ -92,7 +92,7 @@ func TestRevokeToken(t *testing.T) { }, { description: "should pass - refresh token discovery first; both tokens not found", - expectErr: fosite.ErrNotFound, + expectErr: nil, mock: func() { token = "foo" tokenType = fosite.RefreshToken @@ -105,7 +105,7 @@ func TestRevokeToken(t *testing.T) { }, { description: "should pass - access token discovery first; both tokens not found", - expectErr: fosite.ErrNotFound, + expectErr: nil, mock: func() { token = "foo" tokenType = fosite.AccessToken diff --git a/handler/oauth2/validator.go b/handler/oauth2/validator.go deleted file mode 100644 index 0afd1c6a..00000000 --- a/handler/oauth2/validator.go +++ /dev/null @@ -1,71 +0,0 @@ -package oauth2 - -import ( - "github.com/ory-am/fosite" - "github.com/pkg/errors" - "golang.org/x/net/context" -) - -type CoreValidator struct { - CoreStrategy - CoreStorage - ScopeStrategy fosite.ScopeStrategy -} - -func (c *CoreValidator) IntrospectToken(ctx context.Context, token string, tokenType fosite.TokenType, accessRequest fosite.AccessRequester, scopes []string) error { - switch tokenType { - case fosite.AccessToken: - return c.validateAccessToken(ctx, token, accessRequest, scopes) - case fosite.RefreshToken: - return c.validateRefreshToken(ctx, token, accessRequest) - case fosite.AuthorizeCode: - return c.validateAuthorizeCode(ctx, token, accessRequest) - default: - return errors.Wrap(fosite.ErrUnknownRequest, "") - } -} - -func (c *CoreValidator) validateAccessToken(ctx context.Context, token string, accessRequest fosite.AccessRequester, scopes []string) error { - sig := c.CoreStrategy.AccessTokenSignature(token) - or, err := c.CoreStorage.GetAccessTokenSession(ctx, sig, accessRequest.GetSession()) - if err != nil { - return errors.Wrap(fosite.ErrRequestUnauthorized, err.Error()) - } else if err := c.CoreStrategy.ValidateAccessToken(ctx, or, token); err != nil { - return err - } - - for _, scope := range scopes { - if !c.ScopeStrategy(or.GetGrantedScopes(), scope) { - return errors.Wrap(fosite.ErrInvalidScope, "") - } - } - - accessRequest.Merge(or) - return nil -} - -func (c *CoreValidator) validateRefreshToken(ctx context.Context, token string, accessRequest fosite.AccessRequester) error { - sig := c.CoreStrategy.AccessTokenSignature(token) - if or, err := c.CoreStorage.GetAccessTokenSession(ctx, sig, accessRequest.GetSession()); err != nil { - return errors.Wrap(fosite.ErrRequestUnauthorized, err.Error()) - } else if err := c.CoreStrategy.ValidateAccessToken(ctx, or, token); err != nil { - return err - } else { - accessRequest.Merge(or) - } - - return nil -} - -func (c *CoreValidator) validateAuthorizeCode(ctx context.Context, token string, accessRequest fosite.AccessRequester) error { - sig := c.CoreStrategy.AccessTokenSignature(token) - if or, err := c.CoreStorage.GetAccessTokenSession(ctx, sig, accessRequest.GetSession()); err != nil { - return errors.Wrap(err, fosite.ErrRequestUnauthorized.Error()) - } else if err := c.CoreStrategy.ValidateAccessToken(ctx, or, token); err != nil { - return err - } else { - accessRequest.Merge(or) - } - - return nil -} diff --git a/handler/openid/flow_hybrid_test.go b/handler/openid/flow_hybrid_test.go index f6a578d6..fbcd72a6 100644 --- a/handler/openid/flow_hybrid_test.go +++ b/handler/openid/flow_hybrid_test.go @@ -10,11 +10,11 @@ import ( "github.com/ory-am/fosite" "github.com/ory-am/fosite/handler/oauth2" "github.com/ory-am/fosite/internal" + "github.com/ory-am/fosite/storage" "github.com/ory-am/fosite/token/hmac" "github.com/ory-am/fosite/token/jwt" "github.com/pkg/errors" "github.com/stretchr/testify/assert" - "github.com/ory-am/fosite/storage" ) var idStrategy = &DefaultStrategy{ @@ -67,7 +67,7 @@ func TestHybrid_HandleAuthorizeEndpointRequest(t *testing.T) { AuthorizeImplicitGrantTypeHandler: &oauth2.AuthorizeImplicitGrantTypeHandler{ AccessTokenLifespan: time.Hour, AccessTokenStrategy: hmacStrategy, - AccessTokenStorage: storage.NewMemoryStore(), + AccessTokenStorage: storage.NewMemoryStore(), }, IDTokenHandleHelper: &IDTokenHandleHelper{ IDTokenStrategy: idStrategy, diff --git a/handler/openid/flow_implicit_test.go b/handler/openid/flow_implicit_test.go index 8254920a..a26e8285 100644 --- a/handler/openid/flow_implicit_test.go +++ b/handler/openid/flow_implicit_test.go @@ -9,10 +9,10 @@ import ( "github.com/golang/mock/gomock" "github.com/ory-am/fosite" "github.com/ory-am/fosite/handler/oauth2" + "github.com/ory-am/fosite/storage" "github.com/ory-am/fosite/token/jwt" "github.com/pkg/errors" "github.com/stretchr/testify/assert" - "github.com/ory-am/fosite/storage" ) func TestImplicit_HandleAuthorizeEndpointRequest(t *testing.T) { diff --git a/integration/helper_endpoints_test.go b/integration/helper_endpoints_test.go index f2d82131..4050923f 100644 --- a/integration/helper_endpoints_test.go +++ b/integration/helper_endpoints_test.go @@ -19,13 +19,40 @@ type mySessionData struct { *foauth.HMACSession } +func tokenRevocationHandler(t *testing.T, oauth2 fosite.OAuth2Provider, session interface{}) func(rw http.ResponseWriter, req *http.Request) { + return func(rw http.ResponseWriter, req *http.Request) { + ctx := fosite.NewContext() + err := oauth2.NewRevocationRequest(ctx, req) + if err != nil { + t.Logf("Revoke request failed because %s.", err.Error()) + t.Logf("Stack: %v", err.(stackTracer).StackTrace()) + } + oauth2.WriteRevocationResponse(rw, err) + } +} + +func tokenIntrospectionHandler(t *testing.T, oauth2 fosite.OAuth2Provider, session interface{}) func(rw http.ResponseWriter, req *http.Request) { + return func(rw http.ResponseWriter, req *http.Request) { + ctx := fosite.NewContext() + ar, err := oauth2.NewIntrospectionRequest(ctx, req, session) + if err != nil { + t.Logf("Introspection request failed because %s.", err.Error()) + t.Logf("Stack: %s", err.(stackTracer).StackTrace()) + oauth2.WriteIntrospectionError(rw, err) + return + } + + oauth2.WriteIntrospectionResponse(rw, ar) + } +} + func tokenInfoHandler(t *testing.T, oauth2 fosite.OAuth2Provider, session interface{}) func(rw http.ResponseWriter, req *http.Request) { return func(rw http.ResponseWriter, req *http.Request) { ctx := fosite.NewContext() if _, err := oauth2.IntrospectToken(ctx, fosite.AccessTokenFromRequest(req), fosite.AccessToken, session); err != nil { rfcerr := fosite.ErrorToRFC6749Error(err) - t.Logf("Info request failed because %s.", err.Error()) - t.Logf("Stack: %s.", err.(stackTracer).StackTrace()) + t.Logf("Info request failed because `%s`.", err.Error()) + t.Logf("Stack: %s", err.(stackTracer).StackTrace()) http.Error(rw, rfcerr.Description, rfcerr.StatusCode) return } @@ -47,6 +74,9 @@ func authEndpointHandler(t *testing.T, oauth2 fosite.OAuth2Provider, session int return } + if ar.GetRequestedScopes().Has("fosite") { + ar.GrantScope("fosite") + } if ar.GetRequestedScopes().Has("offline") { ar.GrantScope("offline") } @@ -105,6 +135,10 @@ func tokenEndpointHandler(t *testing.T, oauth2 fosite.OAuth2Provider) func(rw ht return } + if accessRequest.GetRequestedScopes().Has("fosite") { + accessRequest.GrantScope("fosite") + } + response, err := oauth2.NewAccessResponse(ctx, req, accessRequest) if err != nil { t.Logf("Access request failed because %s.", err.Error()) diff --git a/integration/helper_setup_test.go b/integration/helper_setup_test.go index 608d3f3e..b396dd54 100644 --- a/integration/helper_setup_test.go +++ b/integration/helper_setup_test.go @@ -9,10 +9,10 @@ import ( "github.com/ory-am/fosite" "github.com/ory-am/fosite/handler/oauth2" "github.com/ory-am/fosite/handler/openid" + "github.com/ory-am/fosite/storage" "github.com/ory-am/fosite/token/hmac" goauth "golang.org/x/oauth2" "golang.org/x/oauth2/clientcredentials" - "github.com/ory-am/fosite/storage" ) var fositeStore = &storage.MemoryStore{ @@ -86,6 +86,9 @@ func mockServer(t *testing.T, f fosite.OAuth2Provider, session interface{}) *htt router.HandleFunc("/token", tokenEndpointHandler(t, f)) router.HandleFunc("/callback", authCallbackHandler(t)) router.HandleFunc("/info", tokenInfoHandler(t, f, session)) + router.HandleFunc("/introspect", tokenIntrospectionHandler(t, f, session)) + router.HandleFunc("/revoke", tokenRevocationHandler(t, f, session)) + ts := httptest.NewServer(router) return ts } diff --git a/integration/introspect_token_test.go b/integration/introspect_token_test.go new file mode 100644 index 00000000..1fb47d59 --- /dev/null +++ b/integration/introspect_token_test.go @@ -0,0 +1,91 @@ +package integration_test + +import ( + "testing" + + "encoding/json" + "github.com/ory-am/fosite/compose" + "github.com/ory-am/fosite/handler/oauth2" + "github.com/parnurzeal/gorequest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + goauth "golang.org/x/oauth2" +) + +func TestIntrospectToken(t *testing.T) { + for _, strategy := range []oauth2.AccessTokenStrategy{ + hmacStrategy, + } { + runIntrospectTokenTest(t, strategy) + } +} + +func runIntrospectTokenTest(t *testing.T, strategy oauth2.AccessTokenStrategy) { + f := compose.Compose(new(compose.Config), fositeStore, strategy, compose.OAuth2ClientCredentialsGrantFactory) + ts := mockServer(t, f, &mySessionData{ + HMACSession: new(oauth2.HMACSession), + }) + defer ts.Close() + + oauthClient := newOAuth2AppClient(ts) + a, err := oauthClient.Token(goauth.NoContext) + require.Nil(t, err) + b, err := oauthClient.Token(goauth.NoContext) + require.Nil(t, err) + + for _, c := range []struct { + prepare func(*gorequest.SuperAgent) *gorequest.SuperAgent + isActive bool + scopes string + }{ + { + prepare: func(s *gorequest.SuperAgent) *gorequest.SuperAgent { + return s.SetBasicAuth(oauthClient.ClientID, oauthClient.ClientSecret) + }, + isActive: true, + scopes: "", + }, + { + prepare: func(s *gorequest.SuperAgent) *gorequest.SuperAgent { + return s.Set("Authorization", "bearer "+a.AccessToken) + }, + isActive: true, + scopes: "fosite", + }, + { + prepare: func(s *gorequest.SuperAgent) *gorequest.SuperAgent { + return s.Set("Authorization", "bearer "+a.AccessToken) + }, + isActive: true, + scopes: "", + }, + { + prepare: func(s *gorequest.SuperAgent) *gorequest.SuperAgent { + return s.Set("Authorization", "bearer "+a.AccessToken) + }, + isActive: false, + scopes: "foo", + }, + { + prepare: func(s *gorequest.SuperAgent) *gorequest.SuperAgent { + return s.Set("Authorization", "bearer "+b.AccessToken) + }, + isActive: false, + scopes: "", + }, + } { + res := struct { + Active bool `json:"active"` + }{} + s := gorequest.New() + s = s.Post(ts.URL + "/introspect"). + Type("form"). + SendStruct(map[string]string{"token": b.AccessToken, "scope": c.scopes}) + _, bytes, errs := c.prepare(s).End() + + assert.Nil(t, json.Unmarshal([]byte(bytes), &res)) + t.Logf("Got answer: %s", bytes) + assert.Len(t, errs, 0) + assert.Equal(t, c.isActive, res.Active) + } +} diff --git a/integration/revoke_token_test.go b/integration/revoke_token_test.go new file mode 100644 index 00000000..3f5e38ea --- /dev/null +++ b/integration/revoke_token_test.go @@ -0,0 +1,46 @@ +package integration_test + +import ( + "testing" + + "github.com/ory-am/fosite/compose" + "github.com/ory-am/fosite/handler/oauth2" + "github.com/parnurzeal/gorequest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + goauth "golang.org/x/oauth2" + "net/http" +) + +func TestRevokeToken(t *testing.T) { + for _, strategy := range []oauth2.AccessTokenStrategy{ + hmacStrategy, + } { + runRevokeTokenTest(t, strategy) + } +} + +func runRevokeTokenTest(t *testing.T, strategy oauth2.AccessTokenStrategy) { + f := compose.Compose(new(compose.Config), fositeStore, strategy, compose.OAuth2ClientCredentialsGrantFactory, compose.OAuth2TokenRevocationFactory) + ts := mockServer(t, f, &mySessionData{ + HMACSession: new(oauth2.HMACSession), + }) + defer ts.Close() + + oauthClient := newOAuth2AppClient(ts) + token, err := oauthClient.Token(goauth.NoContext) + assert.Nil(t, err) + + resp, _, errs := gorequest.New().Post(ts.URL+"/revoke"). + SetBasicAuth(oauthClient.ClientID, oauthClient.ClientSecret). + Type("form"). + SendStruct(map[string]string{"token": token.AccessToken}).End() + assert.Len(t, errs, 0) + assert.Equal(t, 200, resp.StatusCode) + + hres, _, errs := gorequest.New().Get(ts.URL+"/info"). + Set("Authorization", "bearer "+token.AccessToken). + End() + require.Len(t, errs, 0) + assert.Equal(t, http.StatusUnauthorized, hres.StatusCode) +} diff --git a/internal/oauth2_revoke_storage.go b/internal/oauth2_revoke_storage.go index 2b580116..00ac2ad7 100644 --- a/internal/oauth2_revoke_storage.go +++ b/internal/oauth2_revoke_storage.go @@ -92,16 +92,20 @@ func (_mr *_MockTokenRevocationStorageRecorder) GetRefreshTokenSession(arg0, arg return _mr.mock.ctrl.RecordCall(_mr.mock, "GetRefreshTokenSession", arg0, arg1, arg2) } -func (_m *MockTokenRevocationStorage) RevokeAccessToken(_param0 context.Context, _param1 string) { - _m.ctrl.Call(_m, "RevokeAccessToken", _param0, _param1) +func (_m *MockTokenRevocationStorage) RevokeAccessToken(_param0 context.Context, _param1 string) error { + ret := _m.ctrl.Call(_m, "RevokeAccessToken", _param0, _param1) + ret0, _ := ret[0].(error) + return ret0 } func (_mr *_MockTokenRevocationStorageRecorder) RevokeAccessToken(arg0, arg1 interface{}) *gomock.Call { return _mr.mock.ctrl.RecordCall(_mr.mock, "RevokeAccessToken", arg0, arg1) } -func (_m *MockTokenRevocationStorage) RevokeRefreshToken(_param0 context.Context, _param1 string) { - _m.ctrl.Call(_m, "RevokeRefreshToken", _param0, _param1) +func (_m *MockTokenRevocationStorage) RevokeRefreshToken(_param0 context.Context, _param1 string) error { + ret := _m.ctrl.Call(_m, "RevokeRefreshToken", _param0, _param1) + ret0, _ := ret[0].(error) + return ret0 } func (_mr *_MockTokenRevocationStorageRecorder) RevokeRefreshToken(arg0, arg1 interface{}) *gomock.Call { diff --git a/internal/validator.go b/internal/validator.go deleted file mode 100644 index 0c13aa9d..00000000 --- a/internal/validator.go +++ /dev/null @@ -1,41 +0,0 @@ -// Automatically generated by MockGen. DO NOT EDIT! -// Source: github.com/ory-am/fosite (interfaces: TokenValidator) - -package internal - -import ( - gomock "github.com/golang/mock/gomock" - fosite "github.com/ory-am/fosite" - context "golang.org/x/net/context" -) - -// Mock of TokenValidator interface -type MockTokenValidator struct { - ctrl *gomock.Controller - recorder *_MockTokenValidatorRecorder -} - -// Recorder for MockTokenValidator (not exported) -type _MockTokenValidatorRecorder struct { - mock *MockTokenValidator -} - -func NewMockTokenValidator(ctrl *gomock.Controller) *MockTokenValidator { - mock := &MockTokenValidator{ctrl: ctrl} - mock.recorder = &_MockTokenValidatorRecorder{mock} - return mock -} - -func (_m *MockTokenValidator) EXPECT() *_MockTokenValidatorRecorder { - return _m.recorder -} - -func (_m *MockTokenValidator) IntrospectToken(_param0 context.Context, _param1 string, _param2 fosite.TokenType, _param3 fosite.AccessRequester, _param4 []string) error { - ret := _m.ctrl.Call(_m, "IntrospectToken", _param0, _param1, _param2, _param3, _param4) - ret0, _ := ret[0].(error) - return ret0 -} - -func (_mr *_MockTokenValidatorRecorder) IntrospectToken(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "IntrospectToken", arg0, arg1, arg2, arg3, arg4) -} diff --git a/introspect_test.go b/introspect_test.go index 1354795a..16d4b4d0 100644 --- a/introspect_test.go +++ b/introspect_test.go @@ -8,8 +8,8 @@ import ( "github.com/golang/mock/gomock" . "github.com/ory-am/fosite" "github.com/ory-am/fosite/compose" - "github.com/ory-am/fosite/storage" "github.com/ory-am/fosite/internal" + "github.com/ory-am/fosite/storage" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "golang.org/x/net/context" @@ -17,7 +17,7 @@ import ( func TestIntrospect(t *testing.T) { ctrl := gomock.NewController(t) - validator := internal.NewMockTokenValidator(ctrl) + validator := internal.NewMockTokenIntrospector(ctrl) defer ctrl.Finish() f := compose.ComposeAllEnabled(new(compose.Config), storage.NewMemoryStore(), []byte{}, nil).(*Fosite) diff --git a/introspection_request_handler.go b/introspection_request_handler.go index 02eebfb0..128f9c15 100644 --- a/introspection_request_handler.go +++ b/introspection_request_handler.go @@ -1,11 +1,11 @@ package fosite import ( - "net/http" - "golang.org/x/net/context" "github.com/pkg/errors" - "time" + "golang.org/x/net/context" + "net/http" "strings" + "time" ) // NewIntrospectionRequest initiates token introspection as defined in @@ -70,7 +70,7 @@ import ( // Accept: application/json // Content-Type: application/x-www-form-urlencoded // Authorization: Bearer 23410913-abewfq.123483 -// +// // token=2YotnFZFEjr1zCsicMWpAA // // In this example, the protected resource uses a client identifier and @@ -85,19 +85,23 @@ import ( // Accept: application/json // Content-Type: application/x-www-form-urlencoded // Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW -// +// // token=mF_9.B5f-4.1JqM&token_type_hint=access_token func (f *Fosite) NewIntrospectionRequest(ctx context.Context, r *http.Request, session interface{}) (IntrospectionResponder, error) { - if err := r.ParseForm(); err != nil { - return nil, errors.Wrap(ErrInvalidRequest, "Could not parse form values") + if r.Method != "POST" { + return nil, errors.Wrap(ErrInvalidRequest, "HTTP method is not POST") + } else if err := r.ParseForm(); err != nil { + return nil, errors.Wrap(ErrInvalidRequest, err.Error()) } token := r.PostForm.Get("token") - tokenType := r.PostForm.Get("token_type") + tokenType := r.PostForm.Get("token_type_hint") scope := r.PostForm.Get("scope") - - if clientToken := AccessTokenFromRequest(r); clientToken != "" { + if token == clientToken { + return nil, errors.Wrap(ErrRequestUnauthorized, "Bearer and introspection token are identical") + } + if _, err := f.IntrospectToken(ctx, clientToken, AccessToken, session); err != nil { return nil, errors.Wrap(ErrRequestUnauthorized, "HTTP Authorization header missing, malformed or credentials used are invalid") } @@ -120,27 +124,29 @@ func (f *Fosite) NewIntrospectionRequest(ctx context.Context, r *http.Request, s ar, err := f.IntrospectToken(ctx, token, TokenType(tokenType), session, strings.Split(scope, " ")...) if err != nil { - return &IntrospectionResponse{Active: false}, err + return &IntrospectionResponse{Active: false}, nil } return &IntrospectionResponse{ - Active: true, + Active: true, AccessRequester: ar, }, nil } type IntrospectionResponse struct { - Active bool + Active bool AccessRequester AccessRequester - ExpiresAt time.Time + ExpiresAt time.Time } func (r *IntrospectionResponse) IsActive() bool { return r.Active } + func (r *IntrospectionResponse) GetAccessRequester() AccessRequester { return r.AccessRequester } + func (r *IntrospectionResponse) GetExpiresAt() time.Time { return r.ExpiresAt -} \ No newline at end of file +} diff --git a/introspection_request_handler_test.go b/introspection_request_handler_test.go new file mode 100644 index 00000000..1728e715 --- /dev/null +++ b/introspection_request_handler_test.go @@ -0,0 +1,100 @@ +package fosite_test + +import ( + "github.com/golang/mock/gomock" + "github.com/ory-am/fosite" + "github.com/ory-am/fosite/compose" + "github.com/ory-am/fosite/internal" + "github.com/stretchr/testify/assert" + "net/http" + "net/url" + "testing" + "time" + + . "github.com/ory-am/fosite" + "github.com/ory-am/fosite/storage" + "github.com/pkg/errors" +) + +func TestIntrospectionResponse(t *testing.T) { + r := &fosite.IntrospectionResponse{ + AccessRequester: fosite.NewAccessRequest(nil), + ExpiresAt: time.Now(), + Active: true, + } + + assert.Equal(t, r.AccessRequester, r.GetAccessRequester()) + assert.Equal(t, r.ExpiresAt, r.GetExpiresAt()) + assert.Equal(t, r.Active, r.IsActive()) +} + +func TestNewIntrospectionRequest(t *testing.T) { + ctrl := gomock.NewController(t) + validator := internal.NewMockTokenIntrospector(ctrl) + defer ctrl.Finish() + + f := compose.ComposeAllEnabled(new(compose.Config), storage.NewMemoryStore(), []byte{}, nil).(*Fosite) + httpreq := &http.Request{ + Method: "POST", + Header: http.Header{}, + Form: url.Values{}, + } + + for k, c := range []struct { + description string + setup func() + expectErr error + isActive bool + }{ + { + description: "should fail", + setup: func() { + }, + expectErr: ErrInvalidRequest, + }, + { + description: "should pass", + setup: func() { + f.TokenIntrospectionHandlers = TokenIntrospectionHandlers{validator} + httpreq = &http.Request{ + Method: "POST", + Header: http.Header{ + "Authorization": []string{"bearer some-token"}, + }, + PostForm: url.Values{ + "token": []string{"introspect-token"}, + }, + } + validator.EXPECT().IntrospectToken(nil, "some-token", gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + validator.EXPECT().IntrospectToken(nil, "introspect-token", gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("")) + }, + isActive: false, + }, + { + description: "should pass", + setup: func() { + f.TokenIntrospectionHandlers = TokenIntrospectionHandlers{validator} + httpreq = &http.Request{ + Method: "POST", + Header: http.Header{ + "Authorization": []string{"bearer some-token"}, + }, + PostForm: url.Values{ + "token": []string{"introspect-token"}, + }, + } + validator.EXPECT().IntrospectToken(nil, "some-token", gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + validator.EXPECT().IntrospectToken(nil, "introspect-token", gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + }, + isActive: true, + }, + } { + c.setup() + res, err := f.NewIntrospectionRequest(nil, httpreq, nil) + assert.True(t, errors.Cause(err) == c.expectErr, "(%d) %s\n%s\n%s", k, c.description, err, c.expectErr) + if res != nil { + assert.Equal(t, c.isActive, res.IsActive()) + } + t.Logf("Passed test case %d", k) + } +} diff --git a/introspection_response_writer.go b/introspection_response_writer.go index 7db9e2cf..4d95fefc 100644 --- a/introspection_response_writer.go +++ b/introspection_response_writer.go @@ -1,9 +1,9 @@ package fosite import ( - "net/http" "github.com/pkg/errors" "github.com/square/go-jose/json" + "net/http" "strings" ) @@ -29,12 +29,18 @@ import ( // respond with an introspection response with the "active" field set to // "false" as described in Section 2.2. func (f *Fosite) WriteIntrospectionError(rw http.ResponseWriter, err error) { + if err == nil { + return + } + if errors.Cause(err) == ErrRequestUnauthorized { writeJsonError(rw, err) return } - _ = json.NewEncoder(rw).Encode(struct{ Active bool `json:"active"` }{Active:false}) + _ = json.NewEncoder(rw).Encode(struct { + Active bool `json:"active"` + }{Active: false}) } // WriteIntrospectionError responds with an error if token introspection failed as defined in @@ -166,22 +172,25 @@ func (f *Fosite) WriteIntrospectionError(rw http.ResponseWriter, err error) { // } func (f *Fosite) WriteIntrospectionResponse(rw http.ResponseWriter, r IntrospectionResponder) { if !r.IsActive() { - _ = json.NewEncoder(rw).Encode(struct{ Active bool `json:"active"` }{Active:false}) + _ = json.NewEncoder(rw).Encode(&struct { + Active bool `json:"active"` + }{Active: false}) + return } _ = json.NewEncoder(rw).Encode(struct { - Active bool `json:"active"` - ClientID string `json:"client_id"` - Scope string `json:"scope"` - ExpiresAt int64 `json:"exp"` - IssuedAt int64 `json:"iat"` + Active bool `json:"active"` + ClientID string `json:"client_id"` + Scope string `json:"scope"` + ExpiresAt int64 `json:"exp"` + IssuedAt int64 `json:"iat"` Extra interface{} `json:",inline"` }{ - Active:true, - ClientID: r.GetAccessRequester().GetClient().GetID(), - Scope: strings.Join(r.GetAccessRequester().GetGrantedScopes(), " "), + Active: true, + ClientID: r.GetAccessRequester().GetClient().GetID(), + Scope: strings.Join(r.GetAccessRequester().GetGrantedScopes(), " "), ExpiresAt: r.GetExpiresAt().Unix(), - IssuedAt: r.GetAccessRequester().GetRequestedAt().Unix(), - Extra: r.GetAccessRequester().GetSession(), + IssuedAt: r.GetAccessRequester().GetRequestedAt().Unix(), + Extra: r.GetAccessRequester().GetSession(), }) } diff --git a/introspection_response_writer_test.go b/introspection_response_writer_test.go new file mode 100644 index 00000000..1bb612b2 --- /dev/null +++ b/introspection_response_writer_test.go @@ -0,0 +1,40 @@ +package fosite_test + +import ( + "github.com/golang/mock/gomock" + . "github.com/ory-am/fosite" + "github.com/ory-am/fosite/internal" + "github.com/pkg/errors" + "net/http" + "testing" +) + +func TestWriteIntrospectionError(t *testing.T) { + f := new(Fosite) + c := gomock.NewController(t) + defer c.Finish() + + rw := internal.NewMockResponseWriter(c) + + rw.EXPECT().WriteHeader(http.StatusUnauthorized) //[]byte("{\"active\":\"false\"}")) + rw.EXPECT().Header().Return(http.Header{}) + rw.EXPECT().Write(gomock.Any()) + f.WriteIntrospectionError(rw, errors.Wrap(ErrRequestUnauthorized, "")) + + rw.EXPECT().Write([]byte("{\"active\":false}\n")) + f.WriteIntrospectionError(rw, errors.New("")) + + f.WriteIntrospectionError(rw, nil) +} + +func TestWriteIntrospectionResponse(t *testing.T) { + f := new(Fosite) + c := gomock.NewController(t) + defer c.Finish() + + rw := internal.NewMockResponseWriter(c) + rw.EXPECT().Write(gomock.Any()).AnyTimes() + f.WriteIntrospectionResponse(rw, &IntrospectionResponse{ + AccessRequester: NewAccessRequest(nil), + }) +} diff --git a/request_test.go b/request_test.go index 2eba6f4e..2027ad7e 100644 --- a/request_test.go +++ b/request_test.go @@ -30,7 +30,7 @@ func TestRequest(t *testing.T) { func TestMergeRequest(t *testing.T) { a := &Request{ RequestedAt: time.Now(), - Client: &DefaultClient{ID:"123"}, + Client: &DefaultClient{ID: "123"}, Scopes: Arguments{"asdff"}, GrantedScopes: []string{"asdf"}, Form: url.Values{"foo": []string{"fasdf"}}, @@ -52,4 +52,4 @@ func TestMergeRequest(t *testing.T) { assert.EqualValues(t, a.GrantedScopes, b.GrantedScopes) assert.EqualValues(t, a.Form, b.Form) assert.EqualValues(t, a.Session, b.Session) -} \ No newline at end of file +} diff --git a/revoke_handler.go b/revoke_handler.go index 9650436d..b37f8727 100644 --- a/revoke_handler.go +++ b/revoke_handler.go @@ -48,7 +48,6 @@ func (f *Fosite) NewRevocationRequest(ctx context.Context, r *http.Request) erro return errors.Wrap(ErrInvalidClient, err.Error()) } - r.ParseForm() token := r.PostForm.Get("token") tokenTypeHint := TokenType(r.PostForm.Get("token_type_hint")) @@ -82,7 +81,7 @@ func (f *Fosite) NewRevocationRequest(ctx context.Context, r *http.Request) erro // purpose of the revocation request, invalidating the particular token, // is already achieved. func (f *Fosite) WriteRevocationResponse(rw http.ResponseWriter, err error) { - switch err { + switch errors.Cause(err) { case ErrInvalidRequest: fallthrough case ErrInvalidClient: diff --git a/storage/memory.go b/storage/memory.go index 3d946f84..e9e94149 100644 --- a/storage/memory.go +++ b/storage/memory.go @@ -12,13 +12,13 @@ type MemoryUserRelation struct { } type MemoryStore struct { - Clients map[string]*fosite.DefaultClient - AuthorizeCodes map[string]fosite.Requester - IDSessions map[string]fosite.Requester - AccessTokens map[string]fosite.Requester - Implicit map[string]fosite.Requester - RefreshTokens map[string]fosite.Requester - Users map[string]MemoryUserRelation + Clients map[string]*fosite.DefaultClient + AuthorizeCodes map[string]fosite.Requester + IDSessions map[string]fosite.Requester + AccessTokens map[string]fosite.Requester + Implicit map[string]fosite.Requester + RefreshTokens map[string]fosite.Requester + Users map[string]MemoryUserRelation // In-memory request ID to token signatures AccessTokenRequestIDs map[string]string RefreshTokenRequestIDs map[string]string @@ -190,17 +190,16 @@ func (s *MemoryStore) PersistRefreshTokenGrantSession(ctx context.Context, origi return nil } -func (s *MemoryStore) RevokeRefreshToken(ctx context.Context, requestID string) { +func (s *MemoryStore) RevokeRefreshToken(ctx context.Context, requestID string) error { if signature, exists := s.RefreshTokenRequestIDs[requestID]; exists { s.DeleteRefreshTokenSession(ctx, signature) } - return + return nil } -func (s *MemoryStore) RevokeAccessToken(ctx context.Context, requestID string) { +func (s *MemoryStore) RevokeAccessToken(ctx context.Context, requestID string) error { if signature, exists := s.AccessTokenRequestIDs[requestID]; exists { s.DeleteAccessTokenSession(ctx, signature) } - return + return nil } - diff --git a/token/hmac/hmacsha.go b/token/hmac/hmacsha.go index d840572c..e0fa90f6 100644 --- a/token/hmac/hmacsha.go +++ b/token/hmac/hmacsha.go @@ -9,8 +9,8 @@ import ( "fmt" "strings" - "github.com/pkg/errors" "github.com/ory-am/fosite" + "github.com/pkg/errors" ) // HMACStrategy is responsible for generating and validating challenges.