diff --git a/README.md b/README.md index 3aedb0c29..e60355e67 100644 --- a/README.md +++ b/README.md @@ -22,20 +22,22 @@ During development, we reviewed the following open specifications: **Table of Contents** -- [Motivation](#motivation) -- [Example](#example) -- [A word on quality](#a-word-on-quality) -- [A word on security](#a-word-on-security) -- [A word on extensibility](#a-word-on-extensibility) -- [Usage](#usage) - - [Installation](#installation) - - [Exemplary Authorization Endpoint](#exemplary-authorization-endpoint) - - [Exemplary Token Endpoint](#exemplary-token-endpoint) - - [Extensible handlers](#extensible-handlers) - - [Replaceable storage](#replaceable-storage) -- [Develop fosite](#develop-fosite) - - [Useful commands](#useful-commands) -- [Hall of Fame](#hall-of-fame) + - [Motivation](#motivation) + - [Example](#example) + - [A word on quality](#a-word-on-quality) + - [A word on security](#a-word-on-security) + - [A word on extensibility](#a-word-on-extensibility) + - [Usage](#usage) + - [Installation](#installation) + - [Exemplary Server Implementation](#exemplary-server-implementation) + - [Exemplary [Authorization Endpoint](https://tools.ietf.org/html/rfc6749#section-3.1)](#exemplary-authorization-endpointhttpstoolsietforghtmlrfc6749section-31) +- [Please log in](#please-log-in) + - [Exemplary [Token Endpoint](https://tools.ietf.org/html/rfc6749#section-3.2)](#exemplary-token-endpointhttpstoolsietforghtmlrfc6749section-32) + - [Exemplary Storage Implementation](#exemplary-storage-implementation) + - [Extensible handlers](#extensible-handlers) + - [Develop fosite](#develop-fosite) + - [Useful commands](#useful-commands) + - [Hall of Fame](#hall-of-fame) @@ -61,7 +63,7 @@ fosite-example ``` There should be a server listening on [localhost:3846](https://localhost:3846/). You can check out the example's -source code [here](/fosite-example/main.go). +source code [here](fosite-example/main.go). ## A word on quality @@ -142,55 +144,126 @@ Right now, there is only an unstable release versioned as the v0 branch: go get gopkg.in/ory-am/fosite.v0/... ``` -### Exemplary [Authorization Endpoint](https://tools.ietf.org/html/rfc6749#section-3.1) +**Before you read ahead.** +Take a look at these real-life implementations: +* [tests](oauth2_integration_helper_test.go) +* [example app](fosite/example/main.go) + +### Exemplary Server Implementation ```go package main import( - "github.com/ory-am/fosite" - "github.com/ory-am/handler/core/explicit" - "golang.org/x/net/context" + "github.com/go-errors/errors" + + . "github.com/ory-am/fosite" + "github.com/ory-am/fosite/enigma" + "github.com/ory-am/fosite/handler/core/explicit" + "github.com/ory-am/fosite/handler/core/implicit" + "github.com/ory-am/fosite/handler/core/owner" + "github.com/ory-am/fosite/handler/core/refresh" + "github.com/ory-am/fosite/handler/core/strategy" + "github.com/ory-am/fosite/handler/core/client" + "log" + "net/http" + "time" ) -func fositeFactory() fosite.OAuth2Provider { +var hmacStrategy = &strategy.HMACSHAStrategy{ + Enigma: &enigma.HMACSHAEnigma{ + GlobalSecret: []byte("some-super-cool-secret-that-nobody-knows"), + }, +} + +var oauth2 = fositeFactory() + +func main() { + // Note that you MUST use http over TLS if you use OAuth2. Do not use OAuth2 otherwise. + // This example does not implement TLS for simplicity. + http.HandleFunc("/auth", authEndpoint) + http.HandleFunc("/token", tokenEndpoint) + log.Fatal(http.ListenAndServe(":3846", nil)) +} + +func fositeFactory() OAuth2Provider { // NewMyStorageImplementation should implement all storage interfaces. + // You can find an exemplary implementation in ./fosite-example/internal/store.go var store = newMyStorageImplementation() - f := fosite.NewFosite(store) - accessTokenLifespan := time.Hour - - // Let's enable the explicit authorize code grant! - explicitHandler := &explicit.AuthorizeExplicitGrantTypeHandler struct { - Enigma: &enigma.HMACSHAEnigma{GlobalSecret: []byte("some-super-cool-secret-that-nobody-knows")}, - Store: store, - AuthCodeLifespan: time.Minute * 10, - } - f.AuthorizeEndpointHandlers.Add("code", explicitHandler) - f.TokenEndpointHandlers.Add("code", explicitHandler) - - // Next let's enable the implicit one! - implicitHandler := &implicit.AuthorizeImplicitGrantTypeHandler struct { - Enigma: &enigma.HMACSHAEnigma{GlobalSecret: []byte("some-super-cool-secret-that-nobody-knows")}, - Store: store, - AccessTokenLifespan: accessTokenLifespan, - } - f.AuthorizeEndpointHandlers.Add("implicit", implicitHandler) + f := NewFosite(store) + accessTokenLifespan := time.Hour + + // Let's enable the explicit authorize code grant! + explicitHandler := &explicit.AuthorizeExplicitGrantTypeHandler{ + AccessTokenStrategy: hmacStrategy, + RefreshTokenStrategy: hmacStrategy, + AuthorizeCodeStrategy: hmacStrategy, + Store: store, + AuthCodeLifespan: time.Minute * 10, + AccessTokenLifespan: accessTokenLifespan, + } + f.AuthorizeEndpointHandlers.Add("code", explicitHandler) + f.TokenEndpointHandlers.Add("code", explicitHandler) + + // Implicit grant type + implicitHandler := &implicit.AuthorizeImplicitGrantTypeHandler{ + AccessTokenStrategy: hmacStrategy, + Store: store, + AccessTokenLifespan: accessTokenLifespan, + } + f.AuthorizeEndpointHandlers.Add("implicit", implicitHandler) + + // Client credentials grant type + clientHandler := &coreclient.ClientCredentialsGrantHandler{ + AccessTokenStrategy: hmacStrategy, + Store: store, + AccessTokenLifespan: accessTokenLifespan, + } + f.TokenEndpointHandlers.Add("client", clientHandler) + + // Resource owner password credentials grant type + ownerHandler := &owner.ResourceOwnerPasswordCredentialsGrantHandler{ + AccessTokenStrategy: hmacStrategy, + Store: store, + AccessTokenLifespan: accessTokenLifespan, + } + f.TokenEndpointHandlers.Add("owner", ownerHandler) + + // Refresh grant type + refreshHandler := &refresh.RefreshTokenGrantHandler{ + AccessTokenStrategy: hmacStrategy, + RefreshTokenStrategy: hmacStrategy, + Store: store, + AccessTokenLifespan: accessTokenLifespan, + } + f.TokenEndpointHandlers.Add("refresh", refreshHandler) return f } +// ... +``` -// Let's assume that we're in a http handler -func handleAuth(rw http.ResponseWriter, r *http.Request) { - ctx := fosite.NewContext() +### Exemplary [Authorization Endpoint](https://tools.ietf.org/html/rfc6749#section-3.1) + +```go +// ... +type session struct { + User string +} + +func authEndpoint(rw http.ResponseWriter, req *http.Request) { + // This context will be passed to all methods. + ctx := NewContext() // Let's create an AuthorizeRequest object! // It will analyze the request and extract important information like scopes, response type and others. - authorizeRequest, err := oauth2.NewAuthorizeRequest(ctx, r) - if err != nil { - oauth2.WriteAuthorizeError(rw, req, err) - return - } + ar, err := oauth2.NewAuthorizeRequest(ctx, req) + if err != nil { + log.Printf("Error occurred in NewAuthorizeRequest: %s\nStack: \n%s", err, err.(*errors.Error).ErrorStack()) + oauth2.WriteAuthorizeError(rw, ar, err) + return + } // you have now access to authorizeRequest, Code ResponseTypes, Scopes ... // and can show the user agent a login or consent page @@ -201,55 +274,36 @@ func handleAuth(rw http.ResponseWriter, r *http.Request) { // return // } - // it would also be possible to redirect the user to an identity provider (google, microsoft live, ...) here - // and do fancy stuff like OpenID Connect amongst others - - // Once you have confirmed the users identity and consent that he indeed wants to give app XYZ authorization, - // you will use the user's id to create an authorize session - user := "12345" - - // mySessionData is going to be persisted alongside the other data. Note that mySessionData is arbitrary. - // You will however absolutely need the user id later on, so at least store that! - mySessionData := struct { - User string - UsingIdentityProvider string - Foo string - } { - User: user, - UsingIdentityProvider: "google", - Foo: "bar", - } - - // if you want to support OpenID Connect, this would be a good place to do stuff like - // user := getUserFromCookie() - // mySessionData := NewImplementsOpenIDSession() - // if authorizeRequest.GetScopes().Has("openid") { - // if authorizeRequest.GetScopes().Has("email") { - // mySessionData.AddField("email", user.Email) - // } - // mySessionData.AddField("id", user.ID) - // } - // - - // Now is the time to handle the response types - // You can use a custom list of response type handlers by setting - // oauth2.AuthorizeEndpointHandlers = []fosite.AuthorizeEndpointHandler{} - // - // Each AuthorizeEndpointHandler is responsible for managing his own state data. For example, the code response type - // handler stores the access token and the session data in a database backend and retrieves it later on - // when handling a grant type. - // - // If you use advanced AuthorizeEndpointHandlers it is a good idea to read the README first and check if your - // session object needs to implement any interface. Think of the session as a persistent context - // for the handlers. - response, err := oauth2.NewAuthorizeResponse(ctx, req, authorizeRequest, &mySessionData) - if err != nil { - oauth2.WriteAuthorizeError(rw, req, err) - return - } - - // The next step is going to redirect the user by either using implicit or explicit grant or both (for OpenID connect) - oauth2.WriteAuthorizeResponse(rw, authorizeRequest, response) + // Normally, this would be the place where you would check if the user is logged in and gives his consent. + // We're simplifying things and just checking if the request includes a valid username and password + if req.Form.Get("username") != "peter" || req.Form.Get("password") != "secret password" { + rw.Write([]byte(`

Please log in

`)) + // ... + return + } + + // You MUST also get the user's consent which is left out here for simplicity. + + // Now it's time to persist some data. This session will be later available to us in the token endpoint. + // So make sure to store things like the user id here. + // The authorize request will be stored additionally, so no need to save scopes or similar things. + sess := &session{User: "peter"} + + // Now we need to get an response. + // This is the place where the AuthorizeEndpointHandlers kick in and start processing the request. + // In our case (let's assume response_type=code), the AuthorizeExplicitGrantTypeHandler is going to handle the request. + // + // NewAuthorizeResponse is capable of running multiple response type handlers which in turn enables this library + // to support open id connect. + response, err := oauth2.NewAuthorizeResponse(ctx, req, ar, sess) + if err != nil { + log.Printf("Error occurred in NewAuthorizeResponse: %s\nStack: \n%s", err, err.(*errors.Error).ErrorStack()) + oauth2.WriteAuthorizeError(rw, ar, err) + return + } + + // Last but not least, send the response! + oauth2.WriteAuthorizeResponse(rw, ar, response) // Done! The client should now have a valid authorize code! } @@ -261,38 +315,150 @@ func handleAuth(rw http.ResponseWriter, r *http.Request) { ```go // ... +func tokenEndpoint(rw http.ResponseWriter, req *http.Request) { + // This context will be passed to all methods. + ctx := NewContext() + + // Remember the sesion data from before? Yup, that's going to be saved in here! + var mySessionData session -func handleToken(rw http.ResponseWriter, req *http.Request) { - ctx := NewContext() - - // First we need to define a session object. Some handlers might require the session to implement - // a specific interface, so keep that in mind when using them. - var mySessionData struct { - User string - UsingIdentityProvider string - Foo string - } - - // This will create an access request object and iterate through the registered TokenEndpointHandlers. - // These might populate mySessionData so do not pass nils. - accessRequest, err := oauth2.NewAccessRequest(ctx, req, &mySessionData) - if err != nil { - oauth2.WriteAccessError(rw, accessRequest, err) - return - } + // This will create an access request object and iterate through the registered TokenEndpointHandlers to validate the request. + accessRequest, err := oauth2.NewAccessRequest(ctx, req, &mySessionData) + if err != nil { + log.Printf("Error occurred in NewAccessRequest: %s\nStack: \n%s", err, err.(*errors.Error).ErrorStack()) + oauth2.WriteAccessError(rw, accessRequest, err) + return + } // Now we have access to mySessionData's populated values and can do crazy things. // Next we create a response for the access request. Again, we iterate through the TokenEndpointHandlers // and aggregate the result in response. - response, err := oauth2.NewAccessResponse(ctx, req, accessRequest, &mySessionData) - if err != nil { - oauth2.WriteAccessError(rw, accessRequest, err) - return - } + response, err := oauth2.NewAccessResponse(ctx, req, accessRequest) + if err != nil { + log.Printf("Error occurred in NewAccessResponse: %s\nStack: \n%s", err, err.(*errors.Error).ErrorStack()) + oauth2.WriteAccessError(rw, accessRequest, err) + return + } // All done, send the response. - oauth2.WriteAccessResponse(rw, accessRequest, response) + oauth2.WriteAccessResponse(rw, accessRequest, response) + + // Your client does now have a valid access token +} +``` + +### Exemplary Storage Implementation + +Fosite does not ship a storage implementation yet. To get fosite running, you need to implement `github.com/ory-am/fosite.Storage`. +Additionally, most of the token / authorize endpoint handlers require a store as well. You could however use one struct +to implement all the signatures. + +The following code is taken from [fosite-example/internal/store.go](fosite-example/internal/store.go) and a working example +of such a struct. This store is capable of supplying storage methods to all the OAuth2 [core handlers](handler/core). + + +```go +package internal + +import ( + "github.com/go-errors/errors" + "github.com/ory-am/common/pkg" + "github.com/ory-am/fosite" + "github.com/ory-am/fosite/client" +) + +type UserRelation struct { + Username string + Password string +} + +// Store is an in memory storage. +type Store struct { + Clients map[string]client.Client + AuthorizeCodes map[string]fosite.Requester + AccessTokens map[string]fosite.Requester + Implicit map[string]fosite.Requester + RefreshTokens map[string]fosite.Requester + Users map[string]UserRelation +} + +func (s *Store) GetClient(id string) (client.Client, error) { + cl, ok := s.Clients[id] + if !ok { + return nil, pkg.ErrNotFound + } + return cl, nil +} + +func (s *Store) CreateAuthorizeCodeSession(code string, req fosite.Requester) error { + s.AuthorizeCodes[code] = req + return nil +} + +func (s *Store) GetAuthorizeCodeSession(code string, _ interface{}) (fosite.Requester, error) { + rel, ok := s.AuthorizeCodes[code] + if !ok { + return nil, pkg.ErrNotFound + } + return rel, nil +} + +func (s *Store) DeleteAuthorizeCodeSession(code string) error { + delete(s.AuthorizeCodes, code) + return nil +} + +func (s *Store) CreateAccessTokenSession(signature string, req fosite.Requester) error { + s.AccessTokens[signature] = req + return nil +} + +func (s *Store) GetAccessTokenSession(signature string, _ interface{}) (fosite.Requester, error) { + rel, ok := s.AccessTokens[signature] + if !ok { + return nil, pkg.ErrNotFound + } + return rel, nil +} + +func (s *Store) DeleteAccessTokenSession(signature string) error { + delete(s.AccessTokens, signature) + return nil +} + +func (s *Store) CreateRefreshTokenSession(signature string, req fosite.Requester) error { + s.RefreshTokens[signature] = req + return nil +} + +func (s *Store) GetRefreshTokenSession(signature string, _ interface{}) (fosite.Requester, error) { + rel, ok := s.RefreshTokens[signature] + if !ok { + return nil, pkg.ErrNotFound + } + return rel, nil +} + +func (s *Store) DeleteRefreshTokenSession(signature string) error { + delete(s.RefreshTokens, signature) + return nil +} + +func (s *Store) CreateImplicitAccessTokenSession(code string, req fosite.Requester) error { + s.Implicit[code] = req + return nil +} + +func (s *Store) DoCredentialsAuthenticate(name string, secret string) error { + rel, ok := s.Users[name] + if !ok { + return pkg.ErrNotFound + } + if rel.Password != secret { + return errors.New("Invalid credentials") + } + return nil } ``` @@ -307,18 +473,28 @@ Let's take the explicit authorize handler. He is responsible for handling the If you want to enable the handler able to handle this workflow, you can do this: ```go -handler := &explicit.AuthorizeExplicitGrantTypeHandler{ - Generator: &enigma.HMACSHAEnigma{GlobalSecret: []byte("some-super-cool-secret-that-nobody-knows")}, - Store: myCodeStore, // Needs to implement CodeResponseTypeStorage -} -oauth2 := &fosite.Fosite{ - AuthorizeEndpointHandlers: fosite.AuthorizeEndpointHandlers{ - handler, - }, - TokenEndpointHandlers: fosite.TokenEndpointHandlers{ - handler, +var hmacStrategy = &strategy.HMACSHAStrategy{ + Enigma: &enigma.HMACSHAEnigma{ + GlobalSecret: []byte("some-super-cool-secret-that-nobody-knows"), }, } + +// var store = ... + +f := NewFosite(store) +accessTokenLifespan := time.Hour + +// Let's enable the explicit authorize code grant! +explicitHandler := &explicit.AuthorizeExplicitGrantTypeHandler{ + AccessTokenStrategy: hmacStrategy, + RefreshTokenStrategy: hmacStrategy, + AuthorizeCodeStrategy: hmacStrategy, + Store: store, + AuthCodeLifespan: time.Minute * 10, + AccessTokenLifespan: accessTokenLifespan, +} +f.AuthorizeEndpointHandlers.Add("code", explicitHandler) +f.TokenEndpointHandlers.Add("code", explicitHandler) ``` As you probably noticed, there are two types of handlers, one for the [authorization */auth* endpoint](https://tools.ietf.org/html/rfc6749#section-3.1) and one for the [token @@ -326,7 +502,7 @@ As you probably noticed, there are two types of handlers, one for the [authoriza API requirements for both endpoints, while, for example, the `AuthorizeImplicitEndpointHandler` only implements the `AuthorizeEndpointHandler` API. -You can find a complete list of handlers inside the [handler directory](). A short list is documented here: +You can find a complete list of handlers inside the [handler directory](handler). A short list is documented here: * `github.com/ory-am/fosite/handler/core/explicit.AuthorizeExplicitEndpointHandler` implements the [Authorization Code Grant](https://tools.ietf.org/html/rfc6749#section-4.1) @@ -337,12 +513,6 @@ You can find a complete list of handlers inside the [handler directory](). A sho * `github.com/ory-am/fosite/handler/core/token/client.TokenClientCredentialsEndpointHandler` implements the [Client Credentials Grant](https://tools.ietf.org/html/rfc6749#section-4.4) -### Replaceable storage - -Fosite does not ship a storage implementation yet. To get fosite running, you need to implement `github.com/ory-am/fosite.Storage`. -Additionally, most of the token / authorize endpoint handlers require a store as well. It is probably smart to -implement all of those interfaces in one struct. - ## Develop fosite You need git and golang installed on your system. @@ -361,7 +531,7 @@ rather sooner than later. ### Useful commands **Create storage mocks** -``` +```sh mockgen -destination internal/storage.go github.com/ory-am/fosite Storage mockgen -destination internal/core_client_storage.go github.com/ory-am/fosite/handler/core/client ClientCredentialsGrantStorage mockgen -destination internal/core_explicit_storage.go github.com/ory-am/fosite/handler/core/explicit AuthorizeCodeGrantStorage @@ -370,15 +540,23 @@ mockgen -destination internal/core_owner_storage.go github.com/ory-am/fosite/han mockgen -destination internal/core_refresh_storage.go github.com/ory-am/fosite/handler/core/refresh RefreshTokenGrantStorage ``` -**Create handler mocks** +**Create strategy mocks** +```sh +mockgen -destination internal/access_token_strategy.go github.com/ory-am/fosite/handler/core AccessTokenStrategy +mockgen -destination internal/refresh_token_strategy.go github.com/ory-am/fosite/handler/core RefreshTokenStrategy +mockgen -destination internal/authorize_code_strategy.go github.com/ory-am/fosite/handler/core AuthorizeCodeStrategy ``` + +**Create handler mocks** +```sh mockgen -destination internal/authorize_handler.go github.com/ory-am/fosite AuthorizeEndpointHandler mockgen -destination internal/token_handler.go github.com/ory-am/fosite TokenEndpointHandler ``` **Create stateful "context" mocks** -``` +```sh mockgen -destination internal/client.go github.com/ory-am/fosite/client Client +mockgen -destination internal/request.go github.com/ory-am/fosite Requester mockgen -destination internal/access_request.go github.com/ory-am/fosite AccessRequester mockgen -destination internal/access_response.go github.com/ory-am/fosite AccessResponder mockgen -destination internal/authorize_request.go github.com/ory-am/fosite AuthorizeRequester @@ -392,9 +570,6 @@ This place is reserved for the fearless bug hunters, reviewers and contributors * [agtorre](https://github.com/agtorre): [contributions](https://github.com/ory-am/fosite/issues?q=author%3Aagtorre), [participations](https://github.com/ory-am/fosite/issues?q=commenter%3Aagtorre). -* [cristiangraz](https://github.com/cristiangraz): - [contributions](https://github.com/ory-am/fosite/issues?q=author%3Aacristiangraz), - [participations](https://github.com/ory-am/fosite/issues?q=commenter%3Acristiangraz). * [danielchatfield](https://github.com/danielchatfield): [contributions](https://github.com/ory-am/fosite/issues?q=author%3Adanielchatfield), [participations](https://github.com/ory-am/fosite/issues?q=commenter%3Adanielchatfield). diff --git a/access_request.go b/access_request.go index 4d2d6de50..c608eb444 100644 --- a/access_request.go +++ b/access_request.go @@ -1,53 +1,15 @@ package fosite import ( - "github.com/ory-am/fosite/client" "time" ) -type AccessRequester interface { - // GetGrantType returns the requests grant type. - GetGrantType() string - - // GetClient returns the requests client. - GetClient() client.Client - - // GetRequestedAt returns the time the request was created. - GetRequestedAt() time.Time - - // GetScopes returns the request's scopes. - GetScopes() Arguments - - // SetScopes sets the request's scopes. - SetScopes(Arguments) - - // GetGrantScopes returns all granted scopes. - GetGrantedScopes() Arguments - - // GrantScope marks a request's scope as granted. - GrantScope(string) - - // SetGrantTypeHandled marks a grant type as handled indicating that the response type is supported. - SetGrantTypeHandled(string) - - // DidHandleGrantType returns if the requested grant type has been handled correctly. - DidHandleGrantType() bool -} - type AccessRequest struct { GrantType string HandledGrantType []string RequestedAt time.Time - Client client.Client - Scopes Arguments - GrantedScopes []string -} -func NewAccessRequest() *AccessRequest { - return &AccessRequest{ - RequestedAt: time.Now(), - HandledGrantType: []string{}, - } + Request } func (a *AccessRequest) DidHandleGrantType() bool { @@ -61,27 +23,3 @@ func (a *AccessRequest) SetGrantTypeHandled(name string) { func (a *AccessRequest) GetGrantType() string { return a.GrantType } - -func (a *AccessRequest) GetRequestedAt() time.Time { - return a.RequestedAt -} - -func (a *AccessRequest) GetClient() client.Client { - return a.Client -} - -func (a *AccessRequest) GetScopes() Arguments { - return a.Scopes -} - -func (a *AccessRequest) SetScopes(s Arguments) { - a.Scopes = s -} - -func (a *AccessRequest) GetGrantedScopes() Arguments { - return Arguments(a.GrantedScopes) -} - -func (a *AccessRequest) GrantScope(scope string) { - a.GrantedScopes = append(a.GrantedScopes, scope) -} diff --git a/access_request_handler.go b/access_request_handler.go index 6fdde9009..2074461e8 100644 --- a/access_request_handler.go +++ b/access_request_handler.go @@ -5,6 +5,7 @@ import ( "golang.org/x/net/context" "net/http" "strings" + "time" ) // Implements @@ -33,9 +34,16 @@ import ( // client MUST authenticate with the authorization server as described // in Section 3.2.1. func (f *Fosite) NewAccessRequest(ctx context.Context, r *http.Request, session interface{}) (AccessRequester, error) { - ar := NewAccessRequest() + accessRequest := &AccessRequest{ + Request: Request{ + Scopes: Arguments{}, + Session: session, + RequestedAt: time.Now(), + }, + } + if r.Method != "POST" { - return ar, errors.New(ErrInvalidRequest) + return accessRequest, errors.New(ErrInvalidRequest) } if f.RequiredScope == "" { @@ -43,49 +51,51 @@ func (f *Fosite) NewAccessRequest(ctx context.Context, r *http.Request, session } if err := r.ParseForm(); err != nil { - return ar, errors.New(ErrInvalidRequest) + return accessRequest, errors.New(ErrInvalidRequest) } + accessRequest.Form = r.PostForm + if session == nil { - return ar, errors.New("Session must not be nil") + return accessRequest, errors.New("Session must not be nil") } - ar.Scopes = removeEmpty(strings.Split(r.Form.Get("scope"), " ")) - ar.GrantType = r.Form.Get("grant_type") - if ar.GrantType == "" { - return ar, errors.New(ErrInvalidRequest) + accessRequest.Scopes = removeEmpty(strings.Split(r.Form.Get("scope"), " ")) + accessRequest.GrantType = r.Form.Get("grant_type") + if accessRequest.GrantType == "" { + return accessRequest, errors.New(ErrInvalidRequest) } clientID, clientSecret, ok := r.BasicAuth() if !ok { - return ar, errors.New(ErrInvalidRequest) + return accessRequest, errors.New(ErrInvalidRequest) } client, err := f.Store.GetClient(clientID) if err != nil { - return ar, errors.New(ErrInvalidClient) + return accessRequest, errors.New(ErrInvalidClient) } // Enforce client authentication if err := f.Hasher.Compare(client.GetHashedSecret(), []byte(clientSecret)); err != nil { - return ar, errors.New(ErrInvalidClient) + return accessRequest, errors.New(ErrInvalidClient) } - ar.Client = client + accessRequest.Client = client for _, loader := range f.TokenEndpointHandlers { - if err := loader.ValidateTokenEndpointRequest(ctx, r, ar, session); err != nil { - return ar, err + if err := loader.ValidateTokenEndpointRequest(ctx, r, accessRequest); err != nil { + return accessRequest, err } } - if !ar.DidHandleGrantType() { - return ar, errors.New(ErrUnsupportedGrantType) + if !accessRequest.DidHandleGrantType() { + return accessRequest, errors.New(ErrUnsupportedGrantType) } - if !ar.GetScopes().Has(f.RequiredScope) { - return ar, errors.New(ErrInvalidScope) + if !accessRequest.GetScopes().Has(f.RequiredScope) { + return accessRequest, errors.New(ErrInvalidScope) } - ar.GrantScope(f.RequiredScope) - return ar, nil + accessRequest.GrantScope(f.RequiredScope) + return accessRequest, nil } diff --git a/access_request_handler_test.go b/access_request_handler_test.go index 5945a3fd0..46ad74dc7 100644 --- a/access_request_handler_test.go +++ b/access_request_handler_test.go @@ -123,7 +123,7 @@ func TestNewAccessRequest(t *testing.T) { store.EXPECT().GetClient(gomock.Eq("foo")).Return(client, nil) client.EXPECT().GetHashedSecret().Return([]byte("foo")) hasher.EXPECT().Compare(gomock.Eq([]byte("foo")), gomock.Eq([]byte("bar"))).Return(nil) - handler.EXPECT().ValidateTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(ErrServerError) + handler.EXPECT().ValidateTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(ErrServerError) }, handlers: TokenEndpointHandlers{"a": handler}, }, @@ -140,7 +140,7 @@ func TestNewAccessRequest(t *testing.T) { store.EXPECT().GetClient(gomock.Eq("foo")).Return(client, nil) client.EXPECT().GetHashedSecret().Return([]byte("foo")) hasher.EXPECT().Compare(gomock.Eq([]byte("foo")), gomock.Eq([]byte("bar"))).Return(nil) - handler.EXPECT().ValidateTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + handler.EXPECT().ValidateTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) }, handlers: TokenEndpointHandlers{"a": handler}, }, @@ -157,7 +157,7 @@ func TestNewAccessRequest(t *testing.T) { store.EXPECT().GetClient(gomock.Eq("foo")).Return(client, nil) client.EXPECT().GetHashedSecret().Return([]byte("foo")) hasher.EXPECT().Compare(gomock.Eq([]byte("foo")), gomock.Eq([]byte("bar"))).Return(nil) - handler.EXPECT().ValidateTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(_ context.Context, _ *http.Request, a AccessRequester, _ interface{}) { + handler.EXPECT().ValidateTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(_ context.Context, _ *http.Request, a AccessRequester) { a.SetGrantTypeHandled("bar") }).Return(nil) }, @@ -175,7 +175,7 @@ func TestNewAccessRequest(t *testing.T) { store.EXPECT().GetClient(gomock.Eq("foo")).Return(client, nil) client.EXPECT().GetHashedSecret().Return([]byte("foo")) hasher.EXPECT().Compare(gomock.Eq([]byte("foo")), gomock.Eq([]byte("bar"))).Return(nil) - handler.EXPECT().ValidateTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(_ context.Context, _ *http.Request, a AccessRequester, _ interface{}) { + handler.EXPECT().ValidateTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(_ context.Context, _ *http.Request, a AccessRequester) { a.SetGrantTypeHandled("foo") a.SetScopes([]string{"asdfasdf"}) }).Return(nil) @@ -195,7 +195,7 @@ func TestNewAccessRequest(t *testing.T) { store.EXPECT().GetClient(gomock.Eq("foo")).Return(client, nil) client.EXPECT().GetHashedSecret().Return([]byte("foo")) hasher.EXPECT().Compare(gomock.Eq([]byte("foo")), gomock.Eq([]byte("bar"))).Return(nil) - handler.EXPECT().ValidateTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(_ context.Context, _ *http.Request, a AccessRequester, _ interface{}) { + handler.EXPECT().ValidateTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(_ context.Context, _ *http.Request, a AccessRequester) { a.SetGrantTypeHandled("foo") a.SetScopes([]string{DefaultRequiredScopeName}) }).Return(nil) @@ -204,7 +204,9 @@ func TestNewAccessRequest(t *testing.T) { expect: &AccessRequest{ GrantType: "foo", HandledGrantType: []string{"foo"}, - Client: client, + Request: Request{ + Client: client, + }, }, }, } { diff --git a/access_request_test.go b/access_request_test.go index d6f1dfb39..fddcce30c 100644 --- a/access_request_test.go +++ b/access_request_test.go @@ -7,7 +7,7 @@ import ( ) func TestAccessRequest(t *testing.T) { - ar := NewAccessRequest() + ar := &AccessRequest{} ar.GrantType = "foobar" ar.Client = &client.SecureClient{} ar.GrantScope("foo") diff --git a/access_response.go b/access_response.go index dc3950e5e..ef7e9c745 100644 --- a/access_response.go +++ b/access_response.go @@ -1,21 +1,5 @@ package fosite -type AccessResponder interface { - SetExtra(key string, value interface{}) - - GetExtra(key string) interface{} - - SetAccessToken(string) - - SetTokenType(string) - - GetAccessToken() string - - GetTokenType() string - - ToMap() map[string]interface{} -} - func NewAccessResponse() AccessResponder { return &AccessResponse{ Extra: map[string]interface{}{}, diff --git a/access_response_handler.go b/access_response_handler.go index 0becf526a..696ea7282 100644 --- a/access_response_handler.go +++ b/access_response_handler.go @@ -6,13 +6,13 @@ import ( "net/http" ) -func (f *Fosite) NewAccessResponse(ctx context.Context, req *http.Request, requester AccessRequester, session interface{}) (AccessResponder, error) { +func (f *Fosite) NewAccessResponse(ctx context.Context, req *http.Request, requester AccessRequester) (AccessResponder, error) { var err error var tk TokenEndpointHandler response := NewAccessResponse() for _, tk = range f.TokenEndpointHandlers { - if err = tk.HandleTokenEndpointRequest(ctx, req, requester, response, session); err != nil { + if err = tk.HandleTokenEndpointRequest(ctx, req, requester, response); err != nil { return nil, errors.Wrap(err, 1) } } diff --git a/access_response_handler_test.go b/access_response_handler_test.go index 32de050e5..266330fc4 100644 --- a/access_response_handler_test.go +++ b/access_response_handler_test.go @@ -30,21 +30,21 @@ func TestNewAccessResponse(t *testing.T) { }, { mock: func() { - handler.EXPECT().HandleTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(ErrServerError) + handler.EXPECT().HandleTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(ErrServerError) }, handlers: TokenEndpointHandlers{"a": handler}, expectErr: ErrServerError, }, { mock: func() { - handler.EXPECT().HandleTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + handler.EXPECT().HandleTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) }, handlers: TokenEndpointHandlers{"a": handler}, expectErr: ErrUnsupportedGrantType, }, { mock: func() { - handler.EXPECT().HandleTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(_ context.Context, _ *http.Request, _ AccessRequester, resp AccessResponder, _param4 interface{}) { + handler.EXPECT().HandleTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(_ context.Context, _ *http.Request, _ AccessRequester, resp AccessResponder) { resp.SetAccessToken("foo") }).Return(nil) }, @@ -53,7 +53,7 @@ func TestNewAccessResponse(t *testing.T) { }, { mock: func() { - handler.EXPECT().HandleTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(_ context.Context, _ *http.Request, _ AccessRequester, resp AccessResponder, _param4 interface{}) { + handler.EXPECT().HandleTokenEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(_ context.Context, _ *http.Request, _ AccessRequester, resp AccessResponder) { resp.SetAccessToken("foo") resp.SetTokenType("bar") }).Return(nil) @@ -68,7 +68,7 @@ func TestNewAccessResponse(t *testing.T) { } { f.TokenEndpointHandlers = c.handlers c.mock() - ar, err := f.NewAccessResponse(nil, nil, nil, struct{}{}) + ar, err := f.NewAccessResponse(nil, nil, nil) assert.True(t, errors.Is(c.expectErr, err), "%d", k) assert.Equal(t, ar, c.expect) t.Logf("Passed test case %d", k) diff --git a/authorize_request.go b/authorize_request.go index fa3bf85f3..898800a12 100644 --- a/authorize_request.go +++ b/authorize_request.go @@ -1,68 +1,17 @@ package fosite import ( - . "github.com/ory-am/fosite/client" "net/url" - "time" ) -// AuthorizeRequester represents an authorize request -type AuthorizeRequester interface { - // GetResponseTypes returns the requested response types - GetResponseTypes() Arguments - - // SetResponseTypeHandled marks a response_type (e.g. token or code) as handled indicating that the response type - // is supported. - SetResponseTypeHandled(string) - - // DidHandleAllResponseTypes returns if all requested response types have been handled correctly - DidHandleAllResponseTypes() bool - - // GetClient returns this request's client or nil - GetClient() Client - - // GetScopes returns this request's scopes - GetScopes() Arguments - - // GetState returns the request's state - GetState() string - - // GetRequestedAt returns the time the request was issued - GetRequestedAt() time.Time - - // GetRedirectURI returns the requested redirect URI - GetRedirectURI() *url.URL - - // IsRedirectURIValid returns false if the redirect is not rfc-conform (i.e. missing client, not on white list, - // or malformed) - IsRedirectURIValid() bool - - // SetScopes sets the request's scopes. - SetScopes(Arguments) - - // GetGrantScopes returns all granted scopes. - GetGrantedScopes() Arguments -} - -func NewAuthorizeRequest() *AuthorizeRequest { - return &AuthorizeRequest{ - ResponseTypes: Arguments{}, - Scopes: Arguments{}, - HandledResponseTypes: Arguments{}, - GrantedScopes: []string{}, - } -} - // AuthorizeRequest is an implementation of AuthorizeRequester type AuthorizeRequest struct { ResponseTypes Arguments - Client Client - Scopes Arguments RedirectURI *url.URL State string - RequestedAt time.Time HandledResponseTypes Arguments - GrantedScopes []string + + Request } func (d *AuthorizeRequest) IsRedirectURIValid() bool { @@ -86,14 +35,6 @@ func (d *AuthorizeRequest) GetResponseTypes() Arguments { return d.ResponseTypes } -func (d *AuthorizeRequest) GetClient() Client { - return d.Client -} - -func (d *AuthorizeRequest) GetScopes() Arguments { - return d.Scopes -} - func (d *AuthorizeRequest) GetState() string { return d.State } @@ -102,10 +43,6 @@ func (d *AuthorizeRequest) GetRedirectURI() *url.URL { return d.RedirectURI } -func (d *AuthorizeRequest) GetRequestedAt() time.Time { - return d.RequestedAt -} - func (d *AuthorizeRequest) SetResponseTypeHandled(name string) { d.HandledResponseTypes = append(d.HandledResponseTypes, name) } @@ -119,15 +56,3 @@ func (d *AuthorizeRequest) DidHandleAllResponseTypes() bool { return len(d.ResponseTypes) > 0 } - -func (a *AuthorizeRequest) GetGrantedScopes() Arguments { - return Arguments(a.GrantedScopes) -} - -func (a *AuthorizeRequest) GrantScope(scope string) { - a.GrantedScopes = append(a.GrantedScopes, scope) -} - -func (a *AuthorizeRequest) SetScopes(s Arguments) { - a.Scopes = s -} diff --git a/authorize_request_handler.go b/authorize_request_handler.go index 0bd76c377..e551d95c1 100644 --- a/authorize_request_handler.go +++ b/authorize_request_handler.go @@ -8,21 +8,26 @@ import ( "time" ) -func (c *Fosite) NewAuthorizeRequest(_ context.Context, r *http.Request) (AuthorizeRequester, error) { +func (c *Fosite) NewAuthorizeRequest(ctx context.Context, r *http.Request) (AuthorizeRequester, error) { if c.RequiredScope == "" { c.RequiredScope = DefaultRequiredScopeName } request := &AuthorizeRequest{ - RequestedAt: time.Now(), - ResponseTypes: Arguments{}, - Scopes: Arguments{}, + ResponseTypes: Arguments{}, + HandledResponseTypes: Arguments{}, + Request: Request{ + Scopes: Arguments{}, + RequestedAt: time.Now(), + }, } if err := r.ParseForm(); err != nil { return request, errors.New(ErrInvalidRequest) } + request.Form = r.Form + client, err := c.Store.GetClient(r.Form.Get("client_id")) if err != nil { return request, errors.New(ErrInvalidClient) diff --git a/authorize_request_handler_test.go b/authorize_request_handler_test.go index 1a7ee83e7..07b617333 100644 --- a/authorize_request_handler_test.go +++ b/authorize_request_handler_test.go @@ -178,10 +178,12 @@ func TestNewAuthorizeRequest(t *testing.T) { }, expect: &AuthorizeRequest{ RedirectURI: redir, - Client: &SecureClient{RedirectURIs: []string{"https://foo.bar/cb"}}, ResponseTypes: []string{"code"}, State: "strong-state", - Scopes: []string{DefaultRequiredScopeName, "foo", "bar"}, + Request: Request{ + Scopes: []string{DefaultRequiredScopeName, "foo", "bar"}, + Client: &SecureClient{RedirectURIs: []string{"https://foo.bar/cb"}}, + }, }, }, { @@ -199,10 +201,12 @@ func TestNewAuthorizeRequest(t *testing.T) { }, expect: &AuthorizeRequest{ RedirectURI: redir, - Client: &SecureClient{RedirectURIs: []string{"https://foo.bar/cb"}}, ResponseTypes: []string{"code", "token"}, State: "strong-state", - Scopes: []string{DefaultRequiredScopeName, "foo", "bar"}, + Request: Request{ + Client: &SecureClient{RedirectURIs: []string{"https://foo.bar/cb"}}, + Scopes: []string{DefaultRequiredScopeName, "foo", "bar"}, + }, }, }, } { diff --git a/authorize_request_test.go b/authorize_request_test.go index 89e5bebd1..2f0c07ded 100644 --- a/authorize_request_test.go +++ b/authorize_request_test.go @@ -18,10 +18,6 @@ func TestAuthorizeRequest(t *testing.T) { ar *AuthorizeRequest isRedirValid bool }{ - { - ar: NewAuthorizeRequest(), - isRedirValid: false, - }, { ar: &AuthorizeRequest{}, isRedirValid: false, @@ -34,46 +30,58 @@ func TestAuthorizeRequest(t *testing.T) { }, { ar: &AuthorizeRequest{ - Client: &client.SecureClient{RedirectURIs: []string{""}}, RedirectURI: urlparse("https://foobar"), + Request: Request{ + Client: &client.SecureClient{RedirectURIs: []string{""}}, + }, }, isRedirValid: false, }, { ar: &AuthorizeRequest{ - Client: &client.SecureClient{RedirectURIs: []string{""}}, + Request: Request{ + Client: &client.SecureClient{RedirectURIs: []string{""}}, + }, RedirectURI: urlparse(""), }, isRedirValid: false, }, { ar: &AuthorizeRequest{ - Client: &client.SecureClient{RedirectURIs: []string{}}, + Request: Request{ + Client: &client.SecureClient{RedirectURIs: []string{""}}, + }, RedirectURI: urlparse(""), }, isRedirValid: false, }, { ar: &AuthorizeRequest{ - Client: &client.SecureClient{RedirectURIs: []string{"https://foobar.com#123"}}, RedirectURI: urlparse("https://foobar.com#123"), + Request: Request{ + Client: &client.SecureClient{RedirectURIs: []string{"https://foobar.com#123"}}, + }, }, isRedirValid: false, }, { ar: &AuthorizeRequest{ - Client: &client.SecureClient{RedirectURIs: []string{"https://foobar.com"}}, + Request: Request{ + Client: &client.SecureClient{RedirectURIs: []string{"https://foobar.com"}}, + }, RedirectURI: urlparse("https://foobar.com#123"), }, isRedirValid: false, }, { ar: &AuthorizeRequest{ - Client: &client.SecureClient{RedirectURIs: []string{"https://foobar.com/cb"}}, + Request: Request{ + Client: &client.SecureClient{RedirectURIs: []string{"https://foobar.com/cb"}}, + RequestedAt: time.Now(), + Scopes: []string{"foo", "bar"}, + }, RedirectURI: urlparse("https://foobar.com/cb"), - RequestedAt: time.Now(), ResponseTypes: []string{"foo", "bar"}, - Scopes: []string{"foo", "bar"}, State: "foobar", }, isRedirValid: true, @@ -86,9 +94,12 @@ func TestAuthorizeRequest(t *testing.T) { assert.Equal(t, c.ar.Scopes, c.ar.GetScopes(), "%d", k) assert.Equal(t, c.ar.State, c.ar.GetState(), "%d", k) assert.Equal(t, c.isRedirValid, c.ar.IsRedirectURIValid(), "%d", k) + c.ar.GrantScope("foo") + c.ar.SetSession(&struct{}{}) c.ar.SetScopes([]string{"foo"}) assert.True(t, c.ar.GetGrantedScopes().Has("foo")) assert.True(t, c.ar.GetScopes().Has("foo")) + assert.Equal(t, &struct{}{}, c.ar.GetSession()) } } diff --git a/authorize_response.go b/authorize_response.go index a605b4f9d..95cd61c98 100644 --- a/authorize_response.go +++ b/authorize_response.go @@ -5,36 +5,15 @@ import ( "net/url" ) -// AuthorizeResponder defines fosite's response model -type AuthorizeResponder interface { - GetHeader() http.Header - AddHeader(key, value string) - - GetQuery() url.Values - AddQuery(key, value string) - - GetFragment() url.Values - AddFragment(key, value string) -} - -// NewAuthorizeResponse creates a new AuthorizeResponse -func NewAuthorizeResponse() *AuthorizeResponse { - return &AuthorizeResponse{ - Header: &http.Header{}, - Query: &url.Values{}, - Fragment: &url.Values{}, - } -} - // AuthorizeResponse is an implementation of AuthorizeResponder type AuthorizeResponse struct { - Header *http.Header - Query *url.Values - Fragment *url.Values + Header http.Header + Query url.Values + Fragment url.Values } func (a *AuthorizeResponse) GetHeader() http.Header { - return *a.Header + return a.Header } func (a *AuthorizeResponse) AddHeader(key, value string) { @@ -42,11 +21,11 @@ func (a *AuthorizeResponse) AddHeader(key, value string) { } func (a *AuthorizeResponse) GetQuery() url.Values { - return *a.Query + return a.Query } func (a *AuthorizeResponse) GetFragment() url.Values { - return *a.Fragment + return a.Fragment } func (a *AuthorizeResponse) AddQuery(key, value string) { diff --git a/authorize_response_handler.go b/authorize_response_handler.go index 748b84646..9c10870eb 100644 --- a/authorize_response_handler.go +++ b/authorize_response_handler.go @@ -4,16 +4,19 @@ import ( "github.com/go-errors/errors" "golang.org/x/net/context" "net/http" + "net/url" ) func (o *Fosite) NewAuthorizeResponse(ctx context.Context, r *http.Request, ar AuthorizeRequester, session interface{}) (AuthorizeResponder, error) { - if session == nil { - return nil, errors.New("Session must not be nil") + var resp = &AuthorizeResponse{ + Header: http.Header{}, + Query: url.Values{}, + Fragment: url.Values{}, } - var resp = NewAuthorizeResponse() + ar.SetSession(session) for _, h := range o.AuthorizeEndpointHandlers { - if err := h.HandleAuthorizeEndpointRequest(ctx, r, ar, resp, session); err != nil { + if err := h.HandleAuthorizeEndpointRequest(ctx, r, ar, resp); err != nil { return nil, err } } diff --git a/authorize_response_handler_test.go b/authorize_response_handler_test.go index ba8a4944e..3d873d740 100644 --- a/authorize_response_handler_test.go +++ b/authorize_response_handler_test.go @@ -24,6 +24,7 @@ func TestNewAuthorizeResponse(t *testing.T) { duo := &Fosite{ AuthorizeEndpointHandlers: AuthorizeEndpointHandlers{"a": handlers[0], "b": handlers[0]}, } + ar.EXPECT().SetSession(gomock.Eq(struct{}{})).AnyTimes() fooErr := errors.New("foo") for k, c := range []struct { isErr bool @@ -32,14 +33,14 @@ func TestNewAuthorizeResponse(t *testing.T) { }{ { mock: func() { - handlers[0].EXPECT().HandleAuthorizeEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(fooErr) + handlers[0].EXPECT().HandleAuthorizeEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(fooErr) }, isErr: true, expectErr: fooErr, }, { mock: func() { - handlers[0].EXPECT().HandleAuthorizeEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + handlers[0].EXPECT().HandleAuthorizeEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) ar.EXPECT().DidHandleAllResponseTypes().Return(true) }, isErr: false, @@ -47,8 +48,8 @@ func TestNewAuthorizeResponse(t *testing.T) { { mock: func() { oauth2 = duo - handlers[0].EXPECT().HandleAuthorizeEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - handlers[0].EXPECT().HandleAuthorizeEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + handlers[0].EXPECT().HandleAuthorizeEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + handlers[0].EXPECT().HandleAuthorizeEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) ar.EXPECT().DidHandleAllResponseTypes().Return(true) }, isErr: false, @@ -56,15 +57,15 @@ func TestNewAuthorizeResponse(t *testing.T) { { mock: func() { oauth2 = duo - handlers[0].EXPECT().HandleAuthorizeEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - handlers[0].EXPECT().HandleAuthorizeEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(fooErr) + handlers[0].EXPECT().HandleAuthorizeEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + handlers[0].EXPECT().HandleAuthorizeEndpointRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(fooErr) }, isErr: true, expectErr: fooErr, }, } { c.mock() - responder, err := oauth2.NewAuthorizeResponse(ctx, &http.Request{}, ar, &struct{}{}) + responder, err := oauth2.NewAuthorizeResponse(ctx, &http.Request{}, ar, struct{}{}) assert.Equal(t, c.isErr, err != nil, "%d: %s", k, err) if err != nil { assert.Equal(t, c.expectErr, err, "%d: %s", k, err) diff --git a/authorize_response_test.go b/authorize_response_test.go index 7176d5914..7ab7c901b 100644 --- a/authorize_response_test.go +++ b/authorize_response_test.go @@ -2,11 +2,17 @@ package fosite import ( "github.com/stretchr/testify/assert" + "net/http" + "net/url" "testing" ) func TestAuthorizeResponse(t *testing.T) { - ar := NewAuthorizeResponse() + ar := &AuthorizeResponse{ + Header: http.Header{}, + Query: url.Values{}, + Fragment: url.Values{}, + } ar.AddFragment("foo", "bar") ar.AddQuery("foo", "baz") ar.AddHeader("foo", "foo") diff --git a/enigma/challenge.go b/enigma/challenge.go deleted file mode 100644 index 4a0589b0b..000000000 --- a/enigma/challenge.go +++ /dev/null @@ -1,42 +0,0 @@ -package enigma - -import "strings" - -// Challenge represents an validatable token. -type Challenge struct { - // Key is the messages's key - Key string - - // Signature is the messages's signature - Signature string -} - -// FromString extracts key and signature from ".". -func (a *Challenge) FromString(data string) { - a.Key = "" - a.Signature = "" - - if data == "" { - return - } - - parts := strings.Split(data, ".") - if len(parts) != 2 { - return - } - - key := strings.TrimSpace(parts[0]) - sig := strings.TrimSpace(parts[1]) - if key == "" || sig == "" { - return - } - - a.Key = key - a.Signature = sig - return -} - -// String will return the Challenge as ".". -func (a *Challenge) String() string { - return a.Key + "." + a.Signature -} diff --git a/enigma/challenge_test.go b/enigma/challenge_test.go deleted file mode 100644 index 8ca5a8584..000000000 --- a/enigma/challenge_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package enigma - -import ( - "github.com/stretchr/testify/assert" - "testing" -) - -func TestChallengeToString(t *testing.T) { - ac := &Challenge{ - Key: "foo", - Signature: "bar", - } - assert.Equal(t, "foo.bar", ac.String()) -} - -func TestChallengeFromString(t *testing.T) { - ac := new(Challenge) - for k, c := range [][]string{ - {"foo.bar", "foo", "bar"}, - {"foo.", "", ""}, - {"foo", "", ""}, - {".bar", "", ""}, - } { - ac.FromString(c[0]) - assert.Equal(t, c[1], ac.Key) - assert.Equal(t, c[2], ac.Signature) - t.Logf("Passed test case %d", k) - } -} diff --git a/enigma/enigma.go b/enigma/enigma.go deleted file mode 100644 index 399de511a..000000000 --- a/enigma/enigma.go +++ /dev/null @@ -1,12 +0,0 @@ -package enigma - -// Enigma provides a set of methods to create access, refresh and authorize tokens. -type Enigma interface { - - // GenerateChallenge generates a challenge (comparable to an opaque token). - GenerateChallenge(secret []byte) (*Challenge, error) - - // ValidateSignature verifies that the challenge key matches the challenge signature, making the challenge - // valid or returning an error if it is invalid. - ValidateChallenge(secret []byte, challenge *Challenge) error -} diff --git a/enigma/hmacsha.go b/enigma/hmacsha.go index 838bf3519..057999c31 100644 --- a/enigma/hmacsha.go +++ b/enigma/hmacsha.go @@ -4,8 +4,10 @@ import ( "crypto/hmac" "crypto/sha256" "encoding/base64" + "fmt" "github.com/go-errors/errors" "github.com/ory-am/fosite/rand" + "strings" ) // HMACSHAEnigma is the default implementation for generating and validating challenges. It uses HMAC-SHA256 to @@ -23,11 +25,11 @@ const minimumSecretLength = 32 var b64 = base64.StdEncoding.WithPadding(base64.NoPadding) -// GenerateAuthorizeCode generates a new authorize code or returns an error. set secret +// Generate generates a token and a matching signature or returns an error. // This method implements rfc6819 Section 5.1.4.2.2: Use High Entropy for Secrets. -func (c *HMACSHAEnigma) GenerateChallenge(secret []byte) (*Challenge, error) { +func (c *HMACSHAEnigma) Generate(secret []byte) (string, string, error) { if len(secret) < minimumSecretLength/2 || len(c.GlobalSecret) < minimumSecretLength/2 { - return nil, errors.New("Secret or GlobalSecret are not strong enough") + return "", "", errors.New("Secret or GlobalSecret are not strong enough") } if c.AuthCodeEntropy < minimumEntropy { @@ -41,57 +43,62 @@ func (c *HMACSHAEnigma) GenerateChallenge(secret []byte) (*Challenge, error) { // constructed from a cryptographically strong random or pseudo-random // number sequence (see [RFC4086] for best current practice) generated // by the authorization server. - randomBytes, err := rand.RandomBytes(c.AuthCodeEntropy) + key, err := rand.RandomBytes(c.AuthCodeEntropy) if err != nil { - return nil, errors.New(err) + return "", "", errors.New(err) } - if len(randomBytes) < c.AuthCodeEntropy { - return nil, errors.New("Could not read enough random data for key generation") + if len(key) < c.AuthCodeEntropy { + return "", "", errors.New("Could not read enough random data for key generation") } useSecret := append([]byte{}, c.GlobalSecret...) mac := hmac.New(sha256.New, append(useSecret, secret...)) - _, err = mac.Write(randomBytes) + _, err = mac.Write(key) if err != nil { - return nil, errors.New(err) + return "", "", errors.New(err) } - signature := mac.Sum([]byte{}) - return &Challenge{ - Key: b64.EncodeToString(randomBytes), - Signature: b64.EncodeToString(signature), - }, nil + signature := mac.Sum([]byte{}) + encodedSignature := b64.EncodeToString(signature) + encodedToken := fmt.Sprintf("%s.%s", b64.EncodeToString(key), encodedSignature) + return encodedToken, encodedSignature, nil } -// ValidateAuthorizeCodeSignature returns an AuthorizeCode, if the code argument is a valid authorize code -// and the signature matches the key. -func (c *HMACSHAEnigma) ValidateChallenge(secret []byte, t *Challenge) (err error) { - if t.Key == "" || t.Signature == "" { - return errors.New("Key and signature must both be not empty") +// Validate validates a token and returns its signature or an error if the token is not valid. +func (c *HMACSHAEnigma) Validate(secret []byte, token string) (string, error) { + split := strings.Split(token, ".") + if len(split) != 2 { + return "", errors.New("Key and signature must both be set") } - signature, err := b64.DecodeString(t.Signature) + key := split[0] + signature := split[1] + if key == "" || signature == "" { + return "", errors.New("Key and signature must both be set") + } + + decodedSignature, err := b64.DecodeString(signature) if err != nil { - return err + return "", err } - key, err := b64.DecodeString(t.Key) + decodedKey, err := b64.DecodeString(key) if err != nil { - return err + return "", err } useSecret := append([]byte{}, c.GlobalSecret...) mac := hmac.New(sha256.New, append(useSecret, secret...)) - _, err = mac.Write(key) + _, err = mac.Write(decodedKey) if err != nil { - return errors.New(err) + return "", errors.New(err) } - if !hmac.Equal(signature, mac.Sum([]byte{})) { + if !hmac.Equal(decodedSignature, mac.Sum([]byte{})) { // Hash is invalid - return errors.New("Key and signature do not match") + return "", errors.New("Key and signature do not match") } - return nil + return signature, nil } diff --git a/enigma/hmacsha_test.go b/enigma/hmacsha_test.go index 97598d6b9..0c6685892 100644 --- a/enigma/hmacsha_test.go +++ b/enigma/hmacsha_test.go @@ -7,23 +7,23 @@ import ( ) func TestGenerateFailsWithShortCredentials(t *testing.T) { - cg := HMACSHAEnigma{ - GlobalSecret: []byte("foo"), - } - - challenge, err := cg.GenerateChallenge([]byte("bar")) + cg := HMACSHAEnigma{GlobalSecret: []byte("foo")} + challenge, signature, err := cg.Generate([]byte("bar")) require.NotNil(t, err, "%s", err) - require.Nil(t, challenge) + require.Empty(t, challenge) + require.Empty(t, signature) cg.GlobalSecret = []byte("12345678901234567890") - challenge, err = cg.GenerateChallenge([]byte("bar")) + challenge, signature, err = cg.Generate([]byte("bar")) require.NotNil(t, err, "%s", err) - require.Nil(t, challenge) + require.Empty(t, challenge) + require.Empty(t, signature) cg.GlobalSecret = []byte("bar") - challenge, err = cg.GenerateChallenge([]byte("12345678901234567890")) + challenge, signature, err = cg.Generate([]byte("12345678901234567890")) require.NotNil(t, err, "%s", err) - require.Nil(t, challenge) + require.Empty(t, challenge) + require.Empty(t, signature) } func TestGenerate(t *testing.T) { @@ -31,27 +31,24 @@ func TestGenerate(t *testing.T) { GlobalSecret: []byte("12345678901234567890"), } - challenge, err := cg.GenerateChallenge([]byte("09876543210987654321")) + token, signature, err := cg.Generate([]byte("09876543210987654321")) require.Nil(t, err, "%s", err) - require.NotNil(t, challenge) - t.Logf("%s.%s", challenge.Key, challenge.Signature) - - err = cg.ValidateChallenge([]byte("09876543210987654321"), challenge) - require.Nil(t, err, "%s", err) - - challenge.FromString(challenge.String()) + require.NotEmpty(t, token) + require.NotEmpty(t, signature) + t.Logf("Token: %s\n Signature: %s", token, signature) - err = cg.ValidateChallenge([]byte("09876543210987654321"), challenge) + validateSignature, err := cg.Validate([]byte("09876543210987654321"), token) require.Nil(t, err, "%s", err) + assert.Equal(t, signature, validateSignature) - err = cg.ValidateChallenge([]byte("bar"), challenge) + _, err = cg.Validate([]byte("bar"), token) require.NotNil(t, err, "%s", err) - err = cg.ValidateChallenge([]byte("baz"), challenge) + _, err = cg.Validate([]byte("baz"), token) require.NotNil(t, err, "%s", err) cg.GlobalSecret = []byte("baz") - err = cg.ValidateChallenge([]byte("bar"), challenge) + _, err = cg.Validate([]byte("bar"), token) require.NotNil(t, err, "%s", err) } @@ -60,7 +57,6 @@ func TestValidateSignatureRejects(t *testing.T) { cg := HMACSHAEnigma{ GlobalSecret: []byte("12345678901234567890"), } - token := new(Challenge) for k, c := range []string{ "", " ", @@ -68,8 +64,7 @@ func TestValidateSignatureRejects(t *testing.T) { "foo.", ".foo", } { - token.FromString(c) - err = cg.ValidateChallenge([]byte("09876543210987654321"), token) + _, err = cg.Validate([]byte("09876543210987654321"), c) assert.NotNil(t, err, "%s", err) t.Logf("Passed test case %d", k) } diff --git a/fosite-example/internal/store.go b/fosite-example/internal/store.go index a73eb5cad..cca1035b0 100644 --- a/fosite-example/internal/store.go +++ b/fosite-example/internal/store.go @@ -5,19 +5,8 @@ import ( "github.com/ory-am/common/pkg" "github.com/ory-am/fosite" "github.com/ory-am/fosite/client" - core "github.com/ory-am/fosite/handler/core" ) -type AuthorizeCodesRelation struct { - request fosite.AuthorizeRequester - session *core.AuthorizeSession -} - -type AccessRelation struct { - access fosite.AccessRequester - session *core.TokenSession -} - type UserRelation struct { Username string Password string @@ -25,24 +14,13 @@ type UserRelation struct { type Store struct { Clients map[string]client.Client - AuthorizeCodes map[string]AuthorizeCodesRelation - AccessTokens map[string]AccessRelation - Implicit map[string]AuthorizeCodesRelation - RefreshTokens map[string]AccessRelation + AuthorizeCodes map[string]fosite.Requester + AccessTokens map[string]fosite.Requester + Implicit map[string]fosite.Requester + RefreshTokens map[string]fosite.Requester Users map[string]UserRelation } -func NewStore() *Store { - return &Store{ - Clients: map[string]client.Client{}, - AuthorizeCodes: map[string]AuthorizeCodesRelation{}, - Implicit: map[string]AuthorizeCodesRelation{}, - AccessTokens: map[string]AccessRelation{}, - RefreshTokens: map[string]AccessRelation{}, - Users: map[string]UserRelation{}, - } -} - func (s *Store) GetClient(id string) (client.Client, error) { cl, ok := s.Clients[id] if !ok { @@ -51,18 +29,17 @@ func (s *Store) GetClient(id string) (client.Client, error) { return cl, nil } -func (s *Store) CreateAuthorizeCodeSession(code string, ar fosite.AuthorizeRequester, sess *core.AuthorizeSession) error { - s.AuthorizeCodes[code] = AuthorizeCodesRelation{request: ar, session: sess} +func (s *Store) CreateAuthorizeCodeSession(code string, req fosite.Requester) error { + s.AuthorizeCodes[code] = req return nil } -func (s *Store) GetAuthorizeCodeSession(code string, sess *core.AuthorizeSession) (fosite.AuthorizeRequester, error) { +func (s *Store) GetAuthorizeCodeSession(code string, _ interface{}) (fosite.Requester, error) { rel, ok := s.AuthorizeCodes[code] if !ok { return nil, pkg.ErrNotFound } - sess = rel.session - return rel.request, nil + return rel, nil } func (s *Store) DeleteAuthorizeCodeSession(code string) error { @@ -70,18 +47,17 @@ func (s *Store) DeleteAuthorizeCodeSession(code string) error { return nil } -func (s *Store) CreateAccessTokenSession(signature string, access fosite.AccessRequester, session *core.TokenSession) error { - s.AccessTokens[signature] = AccessRelation{access: access, session: session} +func (s *Store) CreateAccessTokenSession(signature string, req fosite.Requester) error { + s.AccessTokens[signature] = req return nil } -func (s *Store) GetAccessTokenSession(signature string, session *core.TokenSession) (fosite.AccessRequester, error) { +func (s *Store) GetAccessTokenSession(signature string, _ interface{}) (fosite.Requester, error) { rel, ok := s.AccessTokens[signature] if !ok { return nil, pkg.ErrNotFound } - session = rel.session - return rel.access, nil + return rel, nil } func (s *Store) DeleteAccessTokenSession(signature string) error { @@ -89,18 +65,17 @@ func (s *Store) DeleteAccessTokenSession(signature string) error { return nil } -func (s *Store) CreateRefreshTokenSession(signature string, access fosite.AccessRequester, session *core.TokenSession) error { - s.RefreshTokens[signature] = AccessRelation{access: access, session: session} +func (s *Store) CreateRefreshTokenSession(signature string, req fosite.Requester) error { + s.RefreshTokens[signature] = req return nil } -func (s *Store) GetRefreshTokenSession(signature string, session *core.TokenSession) (fosite.AccessRequester, error) { +func (s *Store) GetRefreshTokenSession(signature string, _ interface{}) (fosite.Requester, error) { rel, ok := s.RefreshTokens[signature] if !ok { return nil, pkg.ErrNotFound } - session = rel.session - return rel.access, nil + return rel, nil } func (s *Store) DeleteRefreshTokenSession(signature string) error { @@ -108,8 +83,8 @@ func (s *Store) DeleteRefreshTokenSession(signature string) error { return nil } -func (s *Store) CreateImplicitAccessTokenSession(code string, ar fosite.AuthorizeRequester, sess *core.AuthorizeSession) error { - s.Implicit[code] = AuthorizeCodesRelation{request: ar, session: sess} +func (s *Store) CreateImplicitAccessTokenSession(code string, req fosite.Requester) error { + s.Implicit[code] = req return nil } diff --git a/fosite-example/main.go b/fosite-example/main.go index 0437b05d6..8057d79a7 100644 --- a/fosite-example/main.go +++ b/fosite-example/main.go @@ -12,6 +12,7 @@ import ( "github.com/ory-am/fosite/handler/core/implicit" "github.com/ory-am/fosite/handler/core/owner" "github.com/ory-am/fosite/handler/core/refresh" + "github.com/ory-am/fosite/handler/core/strategy" "github.com/parnurzeal/gorequest" goauth "golang.org/x/oauth2" "golang.org/x/oauth2/clientcredentials" @@ -30,15 +31,15 @@ var store = &internal.Store{ }, }, Users: map[string]internal.UserRelation{ - "peter": internal.UserRelation{ + "peter": { Username: "peter", Password: "foobar", }, }, - AuthorizeCodes: map[string]internal.AuthorizeCodesRelation{}, - AccessTokens: map[string]internal.AccessRelation{}, - RefreshTokens: map[string]internal.AccessRelation{}, - Implicit: map[string]internal.AuthorizeCodesRelation{}, + AuthorizeCodes: map[string]Requester{}, + Implicit: map[string]Requester{}, + AccessTokens: map[string]Requester{}, + RefreshTokens: map[string]Requester{}, } var oauth2 OAuth2Provider = fositeFactory() var clientConf = goauth.Config{ @@ -57,6 +58,11 @@ var appClientConf = clientcredentials.Config{ Scopes: []string{"fosite"}, TokenURL: "http://localhost:3846/token", } +var hmacStrategy = &strategy.HMACSHAStrategy{ + Enigma: &enigma.HMACSHAEnigma{ + GlobalSecret: []byte("some-super-cool-secret-that-nobody-knows"), + }, +} type session struct { User string @@ -66,12 +72,13 @@ func fositeFactory() OAuth2Provider { // NewMyStorageImplementation should implement all storage interfaces. f := NewFosite(store) - enigmaService := &enigma.HMACSHAEnigma{GlobalSecret: []byte("some-super-cool-secret-that-nobody-knows")} accessTokenLifespan := time.Hour // Let's enable the explicit authorize code grant! explicitHandler := &explicit.AuthorizeExplicitGrantTypeHandler{ - Enigma: enigmaService, + AccessTokenStrategy: hmacStrategy, + RefreshTokenStrategy: hmacStrategy, + AuthorizeCodeStrategy: hmacStrategy, Store: store, AuthCodeLifespan: time.Minute * 10, AccessTokenLifespan: accessTokenLifespan, @@ -81,30 +88,34 @@ func fositeFactory() OAuth2Provider { // Implicit grant type implicitHandler := &implicit.AuthorizeImplicitGrantTypeHandler{ - Enigma: enigmaService, + AccessTokenStrategy: hmacStrategy, Store: store, AccessTokenLifespan: accessTokenLifespan, } f.AuthorizeEndpointHandlers.Add("implicit", implicitHandler) + // Client credentials grant type clientHandler := &coreclient.ClientCredentialsGrantHandler{ - Enigma: enigmaService, + AccessTokenStrategy: hmacStrategy, Store: store, AccessTokenLifespan: accessTokenLifespan, } f.TokenEndpointHandlers.Add("client", clientHandler) + // Resource owner password credentials grant type ownerHandler := &owner.ResourceOwnerPasswordCredentialsGrantHandler{ - Enigma: enigmaService, + AccessTokenStrategy: hmacStrategy, Store: store, AccessTokenLifespan: accessTokenLifespan, } f.TokenEndpointHandlers.Add("owner", ownerHandler) + // Refresh grant type refreshHandler := &refresh.RefreshTokenGrantHandler{ - Enigma: enigmaService, - Store: store, - AccessTokenLifespan: accessTokenLifespan, + AccessTokenStrategy: hmacStrategy, + RefreshTokenStrategy: hmacStrategy, + Store: store, + AccessTokenLifespan: accessTokenLifespan, } f.TokenEndpointHandlers.Add("refresh", refreshHandler) @@ -133,7 +144,7 @@ func tokenEndpoint(rw http.ResponseWriter, req *http.Request) { return } - response, err := oauth2.NewAccessResponse(ctx, req, accessRequest, &mySessionData) + response, err := oauth2.NewAccessResponse(ctx, req, accessRequest) if err != nil { log.Printf("Error occurred in NewAccessResponse: %s\nStack: \n%s", err, err.(*errors.Error).ErrorStack()) oauth2.WriteAccessError(rw, accessRequest, err) diff --git a/handler.go b/handler.go index 6bf4435b2..949cee428 100644 --- a/handler.go +++ b/handler.go @@ -17,7 +17,7 @@ type AuthorizeEndpointHandler interface { // authorization code as described by Section 4.1.1, "token" for // requesting an access token (implicit grant) as described by // Section 4.2.1, or a registered extension value as described by Section 8.4. - HandleAuthorizeEndpointRequest(ctx context.Context, req *http.Request, requester AuthorizeRequester, responder AuthorizeResponder, session interface{}) error + HandleAuthorizeEndpointRequest(ctx context.Context, req *http.Request, requester AuthorizeRequester, responder AuthorizeResponder) error } type TokenEndpointHandler interface { @@ -25,10 +25,10 @@ type TokenEndpointHandler interface { // is passed along, if further information retrieval is required. If the handler feels that he is not responsible for // the authorize request, he must return nil and NOT modify session nor responder neither requester. // - HandleTokenEndpointRequest(ctx context.Context, req *http.Request, requester AccessRequester, responder AccessResponder, session interface{}) error + HandleTokenEndpointRequest(ctx context.Context, req *http.Request, requester AccessRequester, responder AccessResponder) error // ValidateTokenEndpointRequest // If the handler feels that he is not responsible for the authorize request, he must return nil and NOT modify // session nor responder neither requester. - ValidateTokenEndpointRequest(ctx context.Context, req *http.Request, requester AccessRequester, session interface{}) error + ValidateTokenEndpointRequest(ctx context.Context, req *http.Request, requester AccessRequester) error } diff --git a/handler/core/client/client.go b/handler/core/client/client.go index 6d72a1cf9..fe63f56f3 100644 --- a/handler/core/client/client.go +++ b/handler/core/client/client.go @@ -3,7 +3,6 @@ package client import ( "github.com/go-errors/errors" "github.com/ory-am/fosite" - "github.com/ory-am/fosite/enigma" "github.com/ory-am/fosite/handler/core" "golang.org/x/net/context" "net/http" @@ -13,8 +12,8 @@ import ( ) type ClientCredentialsGrantHandler struct { - // Enigma is the algorithm responsible for creating a validatable, opaque string. - Enigma enigma.Enigma + // AccessTokenStrategy is the algorithm responsible for creating a validatable token. + AccessTokenStrategy core.AccessTokenStrategy // Store is used to persist session data across requests. Store ClientCredentialsGrantStorage @@ -24,7 +23,7 @@ type ClientCredentialsGrantHandler struct { } // ValidateTokenEndpointRequest implements https://tools.ietf.org/html/rfc6749#section-4.4.2 -func (c *ClientCredentialsGrantHandler) ValidateTokenEndpointRequest(_ context.Context, req *http.Request, request fosite.AccessRequester, session interface{}) error { +func (c *ClientCredentialsGrantHandler) ValidateTokenEndpointRequest(_ context.Context, r *http.Request, request fosite.AccessRequester) error { // grant_type REQUIRED. // Value MUST be set to "client_credentials". if request.GetGrantType() != "client_credentials" { @@ -42,22 +41,22 @@ func (c *ClientCredentialsGrantHandler) ValidateTokenEndpointRequest(_ context.C } // HandleTokenEndpointRequest implements https://tools.ietf.org/html/rfc6749#section-4.4.3 -func (c *ClientCredentialsGrantHandler) HandleTokenEndpointRequest(ctx context.Context, req *http.Request, requester fosite.AccessRequester, responder fosite.AccessResponder, session interface{}) error { - if requester.GetGrantType() != "client_credentials" { +func (c *ClientCredentialsGrantHandler) HandleTokenEndpointRequest(ctx context.Context, r *http.Request, request fosite.AccessRequester, response fosite.AccessResponder) error { + if request.GetGrantType() != "client_credentials" { return nil } - access, err := c.Enigma.GenerateChallenge(requester.GetClient().GetHashedSecret()) + token, signature, err := c.AccessTokenStrategy.GenerateAccessToken(ctx, r, request) if err != nil { return errors.New(fosite.ErrServerError) - } else if err := c.Store.CreateAccessTokenSession(access.Signature, requester, &core.TokenSession{Extra: session}); err != nil { + } else if err := c.Store.CreateAccessTokenSession(signature, request); err != nil { return errors.New(fosite.ErrServerError) } - responder.SetAccessToken(access.String()) - responder.SetTokenType("bearer") - responder.SetExtra("expires_in", strconv.Itoa(int(c.AccessTokenLifespan/time.Second))) - responder.SetExtra("scope", strings.Join(requester.GetGrantedScopes(), " ")) + response.SetAccessToken(token) + response.SetTokenType("bearer") + response.SetExtra("expires_in", strconv.Itoa(int(c.AccessTokenLifespan/time.Second))) + response.SetExtra("scope", strings.Join(request.GetGrantedScopes(), " ")) // "A refresh token SHOULD NOT be included." // -> we won't issue one diff --git a/handler/core/client/client_test.go b/handler/core/client/client_test.go index 888a79036..776dec1cd 100644 --- a/handler/core/client/client_test.go +++ b/handler/core/client/client_test.go @@ -4,8 +4,6 @@ import ( "github.com/go-errors/errors" "github.com/golang/mock/gomock" "github.com/ory-am/fosite" - "github.com/ory-am/fosite/client" - "github.com/ory-am/fosite/enigma" "github.com/ory-am/fosite/internal" "github.com/stretchr/testify/assert" "net/http" @@ -16,13 +14,13 @@ import ( func TestValidateTokenEndpointRequest(t *testing.T) { ctrl := gomock.NewController(t) store := internal.NewMockClientCredentialsGrantStorage(ctrl) - chgen := internal.NewMockEnigma(ctrl) + chgen := internal.NewMockAccessTokenStrategy(ctrl) areq := internal.NewMockAccessRequester(ctrl) defer ctrl.Finish() h := ClientCredentialsGrantHandler{ Store: store, - Enigma: chgen, + AccessTokenStrategy: chgen, AccessTokenLifespan: time.Hour, } for k, c := range []struct { @@ -43,7 +41,7 @@ func TestValidateTokenEndpointRequest(t *testing.T) { }, } { c.mock() - err := h.ValidateTokenEndpointRequest(nil, c.req, areq, nil) + err := h.ValidateTokenEndpointRequest(nil, c.req, areq) assert.True(t, errors.Is(c.expectErr, err), "%d\n%s\n%s", k, err, c.expectErr) t.Logf("Passed test case %d", k) } @@ -52,15 +50,14 @@ func TestValidateTokenEndpointRequest(t *testing.T) { func TestHandleTokenEndpointRequest(t *testing.T) { ctrl := gomock.NewController(t) store := internal.NewMockClientCredentialsGrantStorage(ctrl) - chgen := internal.NewMockEnigma(ctrl) + chgen := internal.NewMockAccessTokenStrategy(ctrl) areq := internal.NewMockAccessRequester(ctrl) aresp := internal.NewMockAccessResponder(ctrl) - //mockcl := internal.NewMockClient(ctrl) defer ctrl.Finish() h := ClientCredentialsGrantHandler{ Store: store, - Enigma: chgen, + AccessTokenStrategy: chgen, AccessTokenLifespan: time.Hour, } for k, c := range []struct { @@ -76,29 +73,26 @@ func TestHandleTokenEndpointRequest(t *testing.T) { { mock: func() { areq.EXPECT().GetGrantType().Return("client_credentials") - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(nil, errors.New("")) + chgen.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "", errors.New("")) }, expectErr: fosite.ErrServerError, }, { mock: func() { areq.EXPECT().GetGrantType().Return("client_credentials") - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) - store.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("")) + chgen.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "", nil) + store.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any()).Return(errors.New("")) }, expectErr: fosite.ErrServerError, }, { mock: func() { areq.EXPECT().GetGrantType().Return("client_credentials") - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) - store.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + chgen.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("tokenfoo.bar", "", nil) + store.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any()).Return(nil) areq.EXPECT().GetGrantedScopes() - aresp.EXPECT().SetAccessToken(".") + aresp.EXPECT().SetAccessToken("tokenfoo.bar") aresp.EXPECT().SetTokenType("bearer") aresp.EXPECT().SetExtra("expires_in", gomock.Any()) aresp.EXPECT().SetExtra("scope", gomock.Any()) @@ -106,7 +100,7 @@ func TestHandleTokenEndpointRequest(t *testing.T) { }, } { c.mock() - err := h.HandleTokenEndpointRequest(nil, c.req, areq, aresp, nil) + err := h.HandleTokenEndpointRequest(nil, c.req, areq, aresp) assert.True(t, errors.Is(c.expectErr, err), "%d\n%s\n%s", k, err, c.expectErr) t.Logf("Passed test case %d", k) } diff --git a/handler/core/explicit/explicit_auth.go b/handler/core/explicit/explicit_auth.go index d9a2edc9b..1ca1312c2 100644 --- a/handler/core/explicit/explicit_auth.go +++ b/handler/core/explicit/explicit_auth.go @@ -3,8 +3,7 @@ package explicit import ( "github.com/go-errors/errors" . "github.com/ory-am/fosite" - "github.com/ory-am/fosite/enigma" - . "github.com/ory-am/fosite/handler/core" + "github.com/ory-am/fosite/handler/core" "golang.org/x/net/context" "net/http" "strings" @@ -16,8 +15,9 @@ const authCodeDefaultLifespan = time.Hour / 2 // CodeAuthorizeEndpointHandler is a response handler for the Authorize Code grant using the explicit grant type // as defined in https://tools.ietf.org/html/rfc6749#section-4.1 type AuthorizeExplicitGrantTypeHandler struct { - // Enigma is the algorithm responsible for creating a validatable, opaque string. - Enigma enigma.Enigma + AccessTokenStrategy core.AccessTokenStrategy + RefreshTokenStrategy core.RefreshTokenStrategy + AuthorizeCodeStrategy core.AuthorizeCodeStrategy // Store is used to persist session data across requests. Store AuthorizeCodeGrantStorage @@ -29,23 +29,20 @@ type AuthorizeExplicitGrantTypeHandler struct { AccessTokenLifespan time.Duration } -func (c *AuthorizeExplicitGrantTypeHandler) HandleAuthorizeEndpointRequest(_ context.Context, req *http.Request, ar AuthorizeRequester, resp AuthorizeResponder, session interface{}) error { +func (c *AuthorizeExplicitGrantTypeHandler) HandleAuthorizeEndpointRequest(ctx context.Context, req *http.Request, ar AuthorizeRequester, resp AuthorizeResponder) error { // This let's us define multiple response types, for example open id connect's id_token if ar.GetResponseTypes().Has("code") { // Generate the code - code, err := c.Enigma.GenerateChallenge(ar.GetClient().GetHashedSecret()) + code, signature, err := c.AuthorizeCodeStrategy.GenerateAuthorizeCode(ctx, req, ar) if err != nil { return errors.New(ErrServerError) } - if err := c.Store.CreateAuthorizeCodeSession(code.Signature, ar, &AuthorizeSession{ - Extra: session, - RequestRedirectURI: req.Form.Get("redirect_uri"), - }); err != nil { + if err := c.Store.CreateAuthorizeCodeSession(signature, ar); err != nil { return errors.New(ErrServerError) } - resp.AddQuery("code", code.String()) + resp.AddQuery("code", code) resp.AddQuery("state", ar.GetState()) resp.AddQuery("scope", strings.Join(ar.GetGrantedScopes(), " ")) ar.SetResponseTypeHandled("code") diff --git a/handler/core/explicit/explicit_auth_test.go b/handler/core/explicit/explicit_auth_test.go index 7e471bc73..59f1967c8 100644 --- a/handler/core/explicit/explicit_auth_test.go +++ b/handler/core/explicit/explicit_auth_test.go @@ -4,8 +4,6 @@ import ( "github.com/go-errors/errors" "github.com/golang/mock/gomock" "github.com/ory-am/fosite" - "github.com/ory-am/fosite/client" - "github.com/ory-am/fosite/enigma" "github.com/ory-am/fosite/internal" "github.com/stretchr/testify/assert" "net/http" @@ -16,14 +14,14 @@ import ( func TestHandleAuthorizeEndpointRequest(t *testing.T) { ctrl := gomock.NewController(t) store := internal.NewMockAuthorizeCodeGrantStorage(ctrl) - chgen := internal.NewMockEnigma(ctrl) + chgen := internal.NewMockAuthorizeCodeStrategy(ctrl) areq := internal.NewMockAuthorizeRequester(ctrl) aresp := internal.NewMockAuthorizeResponder(ctrl) defer ctrl.Finish() h := AuthorizeExplicitGrantTypeHandler{ - Store: store, - Enigma: chgen, + Store: store, + AuthorizeCodeStrategy: chgen, } for k, c := range []struct { mock func() @@ -43,8 +41,7 @@ func TestHandleAuthorizeEndpointRequest(t *testing.T) { { mock: func() { areq.EXPECT().GetResponseTypes().Return(fosite.Arguments{"code"}) - areq.EXPECT().GetClient().Return(&client.SecureClient{Secret: []byte("foosecret")}) - chgen.EXPECT().GenerateChallenge(gomock.Eq([]byte("foosecret"))).Return(nil, fosite.ErrServerError) + chgen.EXPECT().GenerateAuthorizeCode(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "", fosite.ErrServerError) }, expectErr: fosite.ErrServerError, }, @@ -52,9 +49,8 @@ func TestHandleAuthorizeEndpointRequest(t *testing.T) { req: &http.Request{Form: url.Values{"redirect_uri": {"foobar"}}}, mock: func() { areq.EXPECT().GetResponseTypes().Return(fosite.Arguments{"code"}) - areq.EXPECT().GetClient().Return(&client.SecureClient{Secret: []byte("foosecret")}) - chgen.EXPECT().GenerateChallenge(gomock.Eq([]byte("foosecret"))).Return(&enigma.Challenge{}, nil) - store.EXPECT().CreateAuthorizeCodeSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(fosite.ErrTemporarilyUnavailable) + chgen.EXPECT().GenerateAuthorizeCode(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "", nil) + store.EXPECT().CreateAuthorizeCodeSession(gomock.Any(), gomock.Any()).Return(fosite.ErrTemporarilyUnavailable) }, expectErr: fosite.ErrServerError, }, @@ -62,20 +58,20 @@ func TestHandleAuthorizeEndpointRequest(t *testing.T) { req: &http.Request{Form: url.Values{"redirect_uri": {"foobar"}}}, mock: func() { areq.EXPECT().GetResponseTypes().Return(fosite.Arguments{"code"}) - areq.EXPECT().GetClient().Return(&client.SecureClient{Secret: []byte("foosecret")}) - chgen.EXPECT().GenerateChallenge(gomock.Eq([]byte("foosecret"))).Return(&enigma.Challenge{Key: "foo", Signature: "bar"}, nil) - store.EXPECT().CreateAuthorizeCodeSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - aresp.EXPECT().AddQuery(gomock.Eq("code"), gomock.Eq("foo.bar")) - aresp.EXPECT().AddQuery(gomock.Eq("scope"), gomock.Any()) - aresp.EXPECT().AddQuery(gomock.Eq("state"), gomock.Any()) - areq.EXPECT().SetResponseTypeHandled(gomock.Eq("code")) + chgen.EXPECT().GenerateAuthorizeCode(gomock.Any(), gomock.Any(), gomock.Any()).Return("foo.bar", "bar", nil) + store.EXPECT().CreateAuthorizeCodeSession("bar", gomock.Any()).Return(nil) + + aresp.EXPECT().AddQuery("code", "foo.bar") + aresp.EXPECT().AddQuery("scope", gomock.Any()) + aresp.EXPECT().AddQuery("state", gomock.Any()) + areq.EXPECT().SetResponseTypeHandled("code") areq.EXPECT().GetGrantedScopes() areq.EXPECT().GetState() }, }, } { c.mock() - err := h.HandleAuthorizeEndpointRequest(nil, c.req, areq, aresp, nil) + err := h.HandleAuthorizeEndpointRequest(nil, c.req, areq, aresp) assert.True(t, errors.Is(c.expectErr, err), "%d\n%s\n%s", k, err, c.expectErr) t.Logf("Passed test case %d", k) } diff --git a/handler/core/explicit/explicit_token.go b/handler/core/explicit/explicit_token.go index a3d65d008..e3c88b6c2 100644 --- a/handler/core/explicit/explicit_token.go +++ b/handler/core/explicit/explicit_token.go @@ -4,9 +4,6 @@ import ( "github.com/go-errors/errors" "github.com/ory-am/common/pkg" . "github.com/ory-am/fosite" - "github.com/ory-am/fosite/enigma" - "github.com/ory-am/fosite/handler/core" - . "github.com/ory-am/fosite/handler/core" "golang.org/x/net/context" "net/http" "strconv" @@ -16,29 +13,20 @@ import ( // implements // * https://tools.ietf.org/html/rfc6749#section-4.1.3 (everything) -func (c *AuthorizeExplicitGrantTypeHandler) ValidateTokenEndpointRequest(_ context.Context, req *http.Request, request AccessRequester, session interface{}) error { +func (c *AuthorizeExplicitGrantTypeHandler) ValidateTokenEndpointRequest(ctx context.Context, r *http.Request, request AccessRequester) error { // grant_type REQUIRED. // Value MUST be set to "authorization_code". if request.GetGrantType() != "authorization_code" { return nil } - authSess := AuthorizeSession{Extra: session} - challenge := &enigma.Challenge{} - challenge.FromString(req.PostForm.Get("code")) - - // code REQUIRED. - // The authorization code received from the authorization server. - if challenge.Key == "" || challenge.Signature == "" { - return errors.New(ErrInvalidRequest) - } - // The authorization server MUST verify that the authorization code is valid - if err := c.Enigma.ValidateChallenge(request.GetClient().GetHashedSecret(), challenge); err != nil { + signature, err := c.AuthorizeCodeStrategy.ValidateAuthorizeCode(r.PostForm.Get("code"), ctx, r, request) + if err != nil { return errors.New(ErrInvalidRequest) } - ar, err := c.Store.GetAuthorizeCodeSession(challenge.Signature, &authSess) + authorizeRequest, err := c.Store.GetAuthorizeCodeSession(signature, request.GetSession()) if err == pkg.ErrNotFound { return errors.New(ErrInvalidRequest) } else if err != nil { @@ -46,12 +34,12 @@ func (c *AuthorizeExplicitGrantTypeHandler) ValidateTokenEndpointRequest(_ conte } // Override scopes - request.SetScopes(ar.GetScopes()) + request.SetScopes(authorizeRequest.GetScopes()) // The authorization server MUST ensure that the authorization code was issued to the authenticated // confidential client, or if the client is public, ensure that the // code was issued to "client_id" in the request, - if ar.GetClient().GetID() != request.GetClient().GetID() { + if authorizeRequest.GetClient().GetID() != request.GetClient().GetID() { return errors.New(ErrInvalidRequest) } @@ -59,7 +47,8 @@ func (c *AuthorizeExplicitGrantTypeHandler) ValidateTokenEndpointRequest(_ conte // "redirect_uri" parameter was included in the initial authorization // request as described in Section 4.1.1, and if included ensure that // their values are identical. - if authSess.RequestRedirectURI != "" && (req.PostForm.Get("redirect_uri") != authSess.RequestRedirectURI) { + forcedRedirectURI := authorizeRequest.GetRequestForm().Get("redirect_uri") + if forcedRedirectURI != "" && forcedRedirectURI != r.PostForm.Get("redirect_uri") { return errors.New(ErrInvalidRequest) } @@ -71,7 +60,7 @@ func (c *AuthorizeExplicitGrantTypeHandler) ValidateTokenEndpointRequest(_ conte // https://tools.ietf.org/html/rfc6819#section-5.1.5.3] // A short expiration time for tokens is a means of protection against // the following threats: replay, token leak, online guessing - if ar.GetRequestedAt().Add(c.AuthCodeLifespan).Before(time.Now()) { + if authorizeRequest.GetRequestedAt().Add(c.AuthCodeLifespan).Before(time.Now()) { return errors.New(ErrInvalidRequest) } @@ -81,49 +70,52 @@ func (c *AuthorizeExplicitGrantTypeHandler) ValidateTokenEndpointRequest(_ conte // client MUST authenticate with the authorization server as described // in Section 3.2.1. request.SetGrantTypeHandled("authorization_code") - session = authSess.Extra + request.SetSession(authorizeRequest.GetSession()) return nil } -func (c *AuthorizeExplicitGrantTypeHandler) HandleTokenEndpointRequest(ctx context.Context, req *http.Request, requester AccessRequester, responder AccessResponder, session interface{}) error { +func (c *AuthorizeExplicitGrantTypeHandler) HandleTokenEndpointRequest(ctx context.Context, req *http.Request, requester AccessRequester, responder AccessResponder) error { // grant_type REQUIRED. // Value MUST be set to "authorization_code". if requester.GetGrantType() != "authorization_code" { return nil } - access, err := c.Enigma.GenerateChallenge(requester.GetClient().GetHashedSecret()) + access, accessSignature, err := c.AccessTokenStrategy.GenerateAccessToken(ctx, req, requester) if err != nil { return errors.New(ErrServerError) } - refresh, err := c.Enigma.GenerateChallenge(requester.GetClient().GetHashedSecret()) + refresh, refreshSignature, err := c.RefreshTokenStrategy.GenerateRefreshToken(ctx, req, requester) if err != nil { return errors.New(ErrServerError) } - challenge := &enigma.Challenge{} - challenge.FromString(req.PostForm.Get("code")) - ar, err := c.Store.GetAuthorizeCodeSession(challenge.Signature, &AuthorizeSession{Extra: session}) + signature, err := c.AuthorizeCodeStrategy.ValidateAuthorizeCode(req.PostForm.Get("code"), ctx, req, requester) + if err != nil { + return errors.New(ErrInvalidRequest) + } + + accessRequest, err := c.Store.GetAuthorizeCodeSession(signature, nil) if err != nil { // The signature has already been verified both cryptographically and with lookup. If lookup fails here // it is due to some internal error. return errors.New(ErrServerError) } - if err := c.Store.DeleteAuthorizeCodeSession(req.PostForm.Get("code")); err != nil { + if err := c.Store.CreateAccessTokenSession(accessSignature, requester); err != nil { return errors.New(ErrServerError) - } else if err := c.Store.CreateAccessTokenSession(access.Signature, requester, &core.TokenSession{Extra: session}); err != nil { + } else if err := c.Store.CreateRefreshTokenSession(refreshSignature, requester); err != nil { return errors.New(ErrServerError) - } else if err := c.Store.CreateRefreshTokenSession(refresh.Signature, requester, &core.TokenSession{Extra: session}); err != nil { + } else if err := c.Store.DeleteAuthorizeCodeSession(signature); err != nil { return errors.New(ErrServerError) } - responder.SetAccessToken(access.String()) + responder.SetAccessToken(access) responder.SetTokenType("bearer") responder.SetExtra("expires_in", strconv.Itoa(int(c.AccessTokenLifespan/time.Second))) - responder.SetExtra("refresh_token", refresh.String()) - responder.SetExtra("state", ar.GetState()) + responder.SetExtra("refresh_token", refresh) + responder.SetExtra("state", accessRequest.GetRequestForm().Get("state")) responder.SetExtra("scope", strings.Join(requester.GetGrantedScopes(), " ")) return nil } diff --git a/handler/core/explicit/explicit_token_test.go b/handler/core/explicit/explicit_token_test.go index b88c5a75f..76779cf54 100644 --- a/handler/core/explicit/explicit_token_test.go +++ b/handler/core/explicit/explicit_token_test.go @@ -6,8 +6,6 @@ import ( "github.com/ory-am/common/pkg" "github.com/ory-am/fosite" "github.com/ory-am/fosite/client" - "github.com/ory-am/fosite/enigma" - authorize "github.com/ory-am/fosite/handler/core" "github.com/ory-am/fosite/internal" "github.com/stretchr/testify/assert" "net/http" @@ -19,15 +17,19 @@ import ( func TestHandleTokenEndpointRequest(t *testing.T) { ctrl := gomock.NewController(t) store := internal.NewMockAuthorizeCodeGrantStorage(ctrl) - chgen := internal.NewMockEnigma(ctrl) + ach := internal.NewMockAccessTokenStrategy(ctrl) + rch := internal.NewMockRefreshTokenStrategy(ctrl) + auch := internal.NewMockAuthorizeCodeStrategy(ctrl) areq := internal.NewMockAccessRequester(ctrl) aresp := internal.NewMockAccessResponder(ctrl) //mockcl := internal.NewMockClient(ctrl) defer ctrl.Finish() h := AuthorizeExplicitGrantTypeHandler{ - Store: store, - Enigma: chgen, + Store: store, + AuthorizeCodeStrategy: auch, + AccessTokenStrategy: ach, + RefreshTokenStrategy: rch, } for k, c := range []struct { mock func() @@ -42,8 +44,7 @@ func TestHandleTokenEndpointRequest(t *testing.T) { { mock: func() { areq.EXPECT().GetGrantType().Return("authorization_code") - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, errors.New("foo")) + ach.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "", fosite.ErrServerError) }, expectErr: fosite.ErrServerError, }, @@ -51,11 +52,10 @@ func TestHandleTokenEndpointRequest(t *testing.T) { req: &http.Request{PostForm: url.Values{}}, mock: func() { areq.EXPECT().GetGrantType().Return("authorization_code") - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - areq.EXPECT().GetClient().Return(&client.SecureClient{}) + ach.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "", nil) + rch.EXPECT().GenerateRefreshToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "", nil) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) + auch.EXPECT().ValidateAuthorizeCode(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("signature", nil) store.EXPECT().GetAuthorizeCodeSession(gomock.Any(), gomock.Any()).Return(nil, errors.New("")) }, @@ -65,15 +65,14 @@ func TestHandleTokenEndpointRequest(t *testing.T) { req: &http.Request{PostForm: url.Values{}}, mock: func() { areq.EXPECT().GetGrantType().Return("authorization_code") - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) + ach.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "", nil) + rch.EXPECT().GenerateRefreshToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "", nil) + + auch.EXPECT().ValidateAuthorizeCode(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("signature", nil) store.EXPECT().GetAuthorizeCodeSession(gomock.Any(), gomock.Any()).Return(&fosite.AuthorizeRequest{}, nil) - store.EXPECT().DeleteAuthorizeCodeSession(gomock.Any()).Return(nil) - store.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("")) + store.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any()).Return(errors.New("")) }, expectErr: fosite.ErrServerError, }, @@ -81,15 +80,15 @@ func TestHandleTokenEndpointRequest(t *testing.T) { req: &http.Request{PostForm: url.Values{}}, mock: func() { areq.EXPECT().GetGrantType().Return("authorization_code") - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) - store.EXPECT().DeleteAuthorizeCodeSession(gomock.Any()).Return(nil) + ach.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "", nil) + rch.EXPECT().GenerateRefreshToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "", nil) + + auch.EXPECT().ValidateAuthorizeCode(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("signature", nil) + store.EXPECT().GetAuthorizeCodeSession(gomock.Any(), gomock.Any()).Return(&fosite.AuthorizeRequest{}, nil) - store.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - store.EXPECT().CreateRefreshTokenSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("")) + store.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any()).Return(nil) + store.EXPECT().CreateRefreshTokenSession(gomock.Any(), gomock.Any()).Return(errors.New("")) }, expectErr: fosite.ErrServerError, }, @@ -98,27 +97,28 @@ func TestHandleTokenEndpointRequest(t *testing.T) { mock: func() { areq.EXPECT().GetGrantType().Return("authorization_code") areq.EXPECT().GetGrantedScopes() - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) - aresp.EXPECT().SetAccessToken(gomock.Eq(".")) - aresp.EXPECT().SetTokenType(gomock.Eq("bearer")) - aresp.EXPECT().SetExtra(gomock.Eq("refresh_token"), gomock.Any()) - aresp.EXPECT().SetExtra(gomock.Eq("expires_in"), gomock.Any()) - aresp.EXPECT().SetExtra(gomock.Eq("state"), gomock.Any()) - aresp.EXPECT().SetExtra(gomock.Eq("scope"), gomock.Any()) + ach.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("access.at", "at", nil) + rch.EXPECT().GenerateRefreshToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("refresh.rt", "rt", nil) + + auch.EXPECT().ValidateAuthorizeCode(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("signature", nil) + + aresp.EXPECT().SetAccessToken("access.at") + aresp.EXPECT().SetTokenType("bearer") + aresp.EXPECT().SetExtra("refresh_token", "refresh.rt") + aresp.EXPECT().SetExtra("expires_in", gomock.Any()) + aresp.EXPECT().SetExtra("state", gomock.Any()) + aresp.EXPECT().SetExtra("scope", gomock.Any()) store.EXPECT().DeleteAuthorizeCodeSession(gomock.Any()).Return(nil) store.EXPECT().GetAuthorizeCodeSession(gomock.Any(), gomock.Any()).Return(&fosite.AuthorizeRequest{}, nil) - store.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - store.EXPECT().CreateRefreshTokenSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + store.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any()).Return(nil) + store.EXPECT().CreateRefreshTokenSession(gomock.Any(), gomock.Any()).Return(nil) }, }, } { c.mock() - err := h.HandleTokenEndpointRequest(nil, c.req, areq, aresp, nil) + err := h.HandleTokenEndpointRequest(nil, c.req, areq, aresp) assert.True(t, errors.Is(c.expectErr, err), "%d\n%s\n%s", k, err, c.expectErr) t.Logf("Passed test case %d", k) } @@ -127,14 +127,14 @@ func TestHandleTokenEndpointRequest(t *testing.T) { func TestValidateTokenEndpointRequest(t *testing.T) { ctrl := gomock.NewController(t) store := internal.NewMockAuthorizeCodeGrantStorage(ctrl) - chgen := internal.NewMockEnigma(ctrl) areq := internal.NewMockAccessRequester(ctrl) + ach := internal.NewMockAuthorizeCodeStrategy(ctrl) authreq := internal.NewMockAuthorizeRequester(ctrl) defer ctrl.Finish() h := AuthorizeExplicitGrantTypeHandler{ - Store: store, - Enigma: chgen, + Store: store, + AuthorizeCodeStrategy: ach, } for k, c := range []struct { mock func() @@ -146,41 +146,13 @@ func TestValidateTokenEndpointRequest(t *testing.T) { areq.EXPECT().GetGrantType().Return("13245678") // grant_type REQUIRED. Value MUST be set to "authorization_code". }, }, - { - req: &http.Request{ - PostForm: url.Values{"foo": {"bar"}}, // code REQUIRED. The authorization code received from the authorization server. - }, - mock: func() { - areq.EXPECT().GetGrantType().Return("authorization_code") // grant_type REQUIRED. Value MUST be set to "authorization_code". - }, - expectErr: fosite.ErrInvalidRequest, - }, - { - req: &http.Request{ - PostForm: url.Values{"code": {".bar"}}, // code REQUIRED. The authorization code received from the authorization server. - }, - mock: func() { - areq.EXPECT().GetGrantType().Return("authorization_code") // grant_type REQUIRED. Value MUST be set to "authorization_code". - }, - expectErr: fosite.ErrInvalidRequest, - }, - { - req: &http.Request{ - PostForm: url.Values{"code": {"."}}, // code REQUIRED. The authorization code received from the authorization server. - }, - mock: func() { - areq.EXPECT().GetGrantType().Return("authorization_code") // grant_type REQUIRED. Value MUST be set to "authorization_code". - }, - expectErr: fosite.ErrInvalidRequest, - }, { req: &http.Request{ PostForm: url.Values{"code": {"foo.bar"}}, // code REQUIRED. The authorization code received from the authorization server. }, mock: func() { areq.EXPECT().GetGrantType().Return("authorization_code") // grant_type REQUIRED. Value MUST be set to "authorization_code". - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().ValidateChallenge(gomock.Any(), gomock.Any()).Return(errors.New("foo")) + ach.EXPECT().ValidateAuthorizeCode("foo.bar", gomock.Any(), gomock.Any(), gomock.Any()).Return("", errors.New("foo")) }, expectErr: fosite.ErrInvalidRequest, }, @@ -190,9 +162,9 @@ func TestValidateTokenEndpointRequest(t *testing.T) { }, mock: func() { areq.EXPECT().GetGrantType().Return("authorization_code") // grant_type REQUIRED. Value MUST be set to "authorization_code". - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().ValidateChallenge(gomock.Any(), gomock.Any()).Return(nil) - store.EXPECT().GetAuthorizeCodeSession(gomock.Any(), gomock.Any()).Return(nil, pkg.ErrNotFound) + areq.EXPECT().GetSession().Return("asdf") + ach.EXPECT().ValidateAuthorizeCode("foo.bar", gomock.Any(), gomock.Any(), gomock.Any()).Return("", nil) + store.EXPECT().GetAuthorizeCodeSession(gomock.Any(), "asdf").Return(nil, pkg.ErrNotFound) }, expectErr: fosite.ErrInvalidRequest, }, @@ -202,8 +174,8 @@ func TestValidateTokenEndpointRequest(t *testing.T) { }, mock: func() { areq.EXPECT().GetGrantType().Return("authorization_code") // grant_type REQUIRED. Value MUST be set to "authorization_code". - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().ValidateChallenge(gomock.Any(), gomock.Any()).Return(nil) + areq.EXPECT().GetSession().Return(nil) + ach.EXPECT().ValidateAuthorizeCode("foo.bar", gomock.Any(), gomock.Any(), gomock.Any()).Return("", nil) store.EXPECT().GetAuthorizeCodeSession(gomock.Any(), gomock.Any()).Return(nil, errors.New("foo")) }, expectErr: fosite.ErrServerError, @@ -216,8 +188,9 @@ func TestValidateTokenEndpointRequest(t *testing.T) { mock: func() { areq.EXPECT().GetGrantType().Return("authorization_code") // grant_type REQUIRED. Value MUST be set to "authorization_code". areq.EXPECT().GetClient().AnyTimes().Return(&client.SecureClient{ID: "foo"}) - chgen.EXPECT().ValidateChallenge(gomock.Any(), gomock.Any()).Return(nil) - store.EXPECT().GetAuthorizeCodeSession(gomock.Any(), gomock.Any()).Return(authreq, nil) + areq.EXPECT().GetSession().Return(nil) + ach.EXPECT().ValidateAuthorizeCode("foo.bar", gomock.Any(), gomock.Any(), gomock.Any()).Return("bar", nil) + store.EXPECT().GetAuthorizeCodeSession("bar", gomock.Any()).Return(authreq, nil) authreq.EXPECT().GetScopes().Return([]string{}) areq.EXPECT().SetScopes(gomock.Any()) @@ -234,12 +207,12 @@ func TestValidateTokenEndpointRequest(t *testing.T) { mock: func() { areq.EXPECT().GetGrantType().Return("authorization_code") // grant_type REQUIRED. Value MUST be set to "authorization_code". areq.EXPECT().GetClient().AnyTimes().Return(&client.SecureClient{ID: "foo"}) - chgen.EXPECT().ValidateChallenge(gomock.Any(), gomock.Any()).Return(nil) - store.EXPECT().GetAuthorizeCodeSession(gomock.Any(), gomock.Any()).Do(func(_ interface{}, sess *authorize.AuthorizeSession) { - sess.RequestRedirectURI = "request-redir" - }).Return(authreq, nil) + areq.EXPECT().GetSession().Return(nil) + ach.EXPECT().ValidateAuthorizeCode("foo.bar", gomock.Any(), gomock.Any(), gomock.Any()).Return("signature", nil) + store.EXPECT().GetAuthorizeCodeSession("signature", gomock.Any()).Return(authreq, nil) authreq.EXPECT().GetScopes().Return([]string{}) + authreq.EXPECT().GetRequestForm().Return(url.Values{"redirect_uri": {"request-redir"}}) areq.EXPECT().SetScopes(gomock.Any()) authreq.EXPECT().GetClient().Return(&client.SecureClient{ID: "foo"}) }, @@ -255,11 +228,11 @@ func TestValidateTokenEndpointRequest(t *testing.T) { mock: func() { areq.EXPECT().GetGrantType().Return("authorization_code") // grant_type REQUIRED. Value MUST be set to "authorization_code". areq.EXPECT().GetClient().AnyTimes().Return(&client.SecureClient{ID: "foo"}) - chgen.EXPECT().ValidateChallenge(gomock.Any(), gomock.Any()).Return(nil) - store.EXPECT().GetAuthorizeCodeSession(gomock.Any(), gomock.Any()).Do(func(_ interface{}, sess *authorize.AuthorizeSession) { - sess.RequestRedirectURI = "request-redir" - }).Return(authreq, nil) + areq.EXPECT().GetSession().Return(nil) + ach.EXPECT().ValidateAuthorizeCode("foo.bar", gomock.Any(), gomock.Any(), gomock.Any()).Return("", nil) + store.EXPECT().GetAuthorizeCodeSession(gomock.Any(), gomock.Any()).Return(authreq, nil) + authreq.EXPECT().GetRequestForm().Return(url.Values{"redirect_uri": {"request-redir"}}) authreq.EXPECT().GetScopes().Return([]string{}) areq.EXPECT().SetScopes(gomock.Any()) authreq.EXPECT().GetClient().Return(&client.SecureClient{ID: "foo"}) @@ -277,10 +250,12 @@ func TestValidateTokenEndpointRequest(t *testing.T) { mock: func() { areq.EXPECT().GetGrantType().Return("authorization_code") // grant_type REQUIRED. Value MUST be set to "authorization_code". areq.EXPECT().GetClient().AnyTimes().Return(&client.SecureClient{ID: "foo"}) - chgen.EXPECT().ValidateChallenge(gomock.Any(), gomock.Any()).Return(nil) + areq.EXPECT().GetSession().Return(nil) + ach.EXPECT().ValidateAuthorizeCode("foo.bar", gomock.Any(), gomock.Any(), gomock.Any()).Return("", nil) store.EXPECT().GetAuthorizeCodeSession(gomock.Any(), gomock.Any()).Return(authreq, nil) authreq.EXPECT().GetScopes().Return([]string{}) + authreq.EXPECT().GetRequestForm().Return(url.Values{"redirect_uri": {"request-redir"}}) areq.EXPECT().SetScopes(gomock.Any()) authreq.EXPECT().GetClient().Return(&client.SecureClient{ID: "foo"}) authreq.EXPECT().GetRequestedAt().Return(time.Now().Add(-time.Hour)) @@ -296,19 +271,23 @@ func TestValidateTokenEndpointRequest(t *testing.T) { mock: func() { areq.EXPECT().GetGrantType().Return("authorization_code") // grant_type REQUIRED. Value MUST be set to "authorization_code". areq.EXPECT().GetClient().AnyTimes().Return(&client.SecureClient{ID: "foo"}) - chgen.EXPECT().ValidateChallenge(gomock.Any(), gomock.Any()).Return(nil) + areq.EXPECT().GetSession().Return(nil) + ach.EXPECT().ValidateAuthorizeCode("foo.bar", gomock.Any(), gomock.Any(), gomock.Any()).Return("", nil) store.EXPECT().GetAuthorizeCodeSession(gomock.Any(), gomock.Any()).Return(authreq, nil) authreq.EXPECT().GetScopes().Return([]string{}) + authreq.EXPECT().GetRequestForm().Return(url.Values{}) areq.EXPECT().SetScopes(gomock.Any()) authreq.EXPECT().GetClient().Return(&client.SecureClient{ID: "foo"}) authreq.EXPECT().GetRequestedAt().Return(time.Now().Add(time.Hour)) + authreq.EXPECT().GetSession().Return("sess") + areq.EXPECT().SetSession("sess") areq.EXPECT().SetGrantTypeHandled("authorization_code") }, }, } { c.mock() - err := h.ValidateTokenEndpointRequest(nil, c.req, areq, nil) + err := h.ValidateTokenEndpointRequest(nil, c.req, areq) assert.True(t, errors.Is(c.expectErr, err), "%d\n%s\n%s", k, err, c.expectErr) t.Logf("Passed test case %d", k) } diff --git a/handler/core/implicit/implicit.go b/handler/core/implicit/implicit.go index f6cb23c84..a57380195 100644 --- a/handler/core/implicit/implicit.go +++ b/handler/core/implicit/implicit.go @@ -3,7 +3,6 @@ package implicit import ( "github.com/go-errors/errors" . "github.com/ory-am/fosite" - "github.com/ory-am/fosite/enigma" "github.com/ory-am/fosite/handler/core" "golang.org/x/net/context" "net/http" @@ -15,8 +14,7 @@ import ( // CodeAuthorizeEndpointHandler is a response handler for the Authorize Code grant using the explicit grant type // as defined in https://tools.ietf.org/html/rfc6749#section-4.1 type AuthorizeImplicitGrantTypeHandler struct { - // Enigma is the algorithm responsible for creating a validatable, opaque string. - Enigma enigma.Enigma + AccessTokenStrategy core.AccessTokenStrategy // Store is used to persist session data across requests. Store ImplicitGrantStorage @@ -25,21 +23,18 @@ type AuthorizeImplicitGrantTypeHandler struct { AccessTokenLifespan time.Duration } -func (c *AuthorizeImplicitGrantTypeHandler) HandleAuthorizeEndpointRequest(_ context.Context, req *http.Request, ar AuthorizeRequester, resp AuthorizeResponder, session interface{}) error { +func (c *AuthorizeImplicitGrantTypeHandler) HandleAuthorizeEndpointRequest(ctx context.Context, req *http.Request, ar AuthorizeRequester, resp AuthorizeResponder) error { // This let's us define multiple response types, for example open id connect's id_token if ar.GetResponseTypes().Has("token") { // Generate the code - access, err := c.Enigma.GenerateChallenge(ar.GetClient().GetHashedSecret()) + token, signature, err := c.AccessTokenStrategy.GenerateAccessToken(ctx, req, ar) if err != nil { return errors.New(ErrServerError) - } else if err := c.Store.CreateImplicitAccessTokenSession(access.Signature, ar, &core.AuthorizeSession{ - Extra: session, - RequestRedirectURI: req.Form.Get("redirect_uri"), - }); err != nil { + } else if err := c.Store.CreateImplicitAccessTokenSession(signature, ar); err != nil { return errors.New(ErrServerError) } - resp.AddFragment("access_token", access.String()) + resp.AddFragment("access_token", token) resp.AddFragment("expires_in", strconv.Itoa(int(c.AccessTokenLifespan/time.Second))) resp.AddFragment("token_type", "bearer") resp.AddFragment("state", ar.GetState()) diff --git a/handler/core/implicit/implicit_test.go b/handler/core/implicit/implicit_test.go index 82c2e8df6..668250cac 100644 --- a/handler/core/implicit/implicit_test.go +++ b/handler/core/implicit/implicit_test.go @@ -4,8 +4,6 @@ import ( "github.com/go-errors/errors" "github.com/golang/mock/gomock" "github.com/ory-am/fosite" - "github.com/ory-am/fosite/client" - "github.com/ory-am/fosite/enigma" . "github.com/ory-am/fosite/handler/core/implicit" "github.com/ory-am/fosite/internal" "github.com/stretchr/testify/assert" @@ -19,14 +17,14 @@ import ( func TestAuthorizeImplicitEndpointHandler(t *testing.T) { ctrl := gomock.NewController(t) store := internal.NewMockImplicitGrantStorage(ctrl) - chgen := internal.NewMockEnigma(ctrl) + chgen := internal.NewMockAccessTokenStrategy(ctrl) areq := internal.NewMockAuthorizeRequester(ctrl) aresp := internal.NewMockAuthorizeResponder(ctrl) defer ctrl.Finish() h := AuthorizeImplicitGrantTypeHandler{ Store: store, - Enigma: chgen, + AccessTokenStrategy: chgen, AccessTokenLifespan: time.Hour, } for k, c := range []struct { @@ -38,8 +36,7 @@ func TestAuthorizeImplicitEndpointHandler(t *testing.T) { { mock: func() { areq.EXPECT().GetResponseTypes().Return(fosite.Arguments{"token"}) - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(nil, errors.New("")) + chgen.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "", errors.New("")) }, expectErr: fosite.ErrServerError, }, @@ -47,9 +44,8 @@ func TestAuthorizeImplicitEndpointHandler(t *testing.T) { req: &http.Request{Form: url.Values{}}, mock: func() { areq.EXPECT().GetResponseTypes().Return(fosite.Arguments{"token"}) - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{Signature: "foo"}, nil) - store.EXPECT().CreateImplicitAccessTokenSession("foo", gomock.Any(), gomock.Any()).Return(errors.New("")) + chgen.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "foo", nil) + store.EXPECT().CreateImplicitAccessTokenSession("foo", gomock.Any()).Return(errors.New("")) }, expectErr: fosite.ErrServerError, }, @@ -57,24 +53,23 @@ func TestAuthorizeImplicitEndpointHandler(t *testing.T) { req: &http.Request{Form: url.Values{}}, mock: func() { areq.EXPECT().GetResponseTypes().Return(fosite.Arguments{"token"}) - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{Signature: "foo"}, nil) - store.EXPECT().CreateImplicitAccessTokenSession("foo", gomock.Any(), gomock.Any()).Return(nil) + chgen.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("foo.bar", "foo", nil) + store.EXPECT().CreateImplicitAccessTokenSession("foo", gomock.Any()).Return(nil) - aresp.EXPECT().AddFragment("access_token", gomock.Any()) + aresp.EXPECT().AddFragment("access_token", "foo.bar") aresp.EXPECT().AddFragment("expires_in", strconv.Itoa(int(h.AccessTokenLifespan/time.Second))) aresp.EXPECT().AddFragment("token_type", "bearer") - aresp.EXPECT().AddFragment("state", gomock.Any()) - aresp.EXPECT().AddFragment("scope", gomock.Any()) + aresp.EXPECT().AddFragment("state", "state") + aresp.EXPECT().AddFragment("scope", "scope") areq.EXPECT().SetResponseTypeHandled("token") - areq.EXPECT().GetState() - areq.EXPECT().GetGrantedScopes() + areq.EXPECT().GetState().Return("state") + areq.EXPECT().GetGrantedScopes().Return(fosite.Arguments{"scope"}) }, expectErr: nil, }, } { c.mock() - err := h.HandleAuthorizeEndpointRequest(nil, c.req, areq, aresp, nil) + err := h.HandleAuthorizeEndpointRequest(nil, c.req, areq, aresp) assert.True(t, errors.Is(c.expectErr, err), "%d\n%s\n%s", k, err, c.expectErr) t.Logf("Passed test case %d", k) } diff --git a/handler/core/implicit/storage.go b/handler/core/implicit/storage.go index 8b5b8d87c..549ddbc4a 100644 --- a/handler/core/implicit/storage.go +++ b/handler/core/implicit/storage.go @@ -2,9 +2,8 @@ package implicit import ( "github.com/ory-am/fosite" - "github.com/ory-am/fosite/handler/core" ) type ImplicitGrantStorage interface { - CreateImplicitAccessTokenSession(string, fosite.AuthorizeRequester, *core.AuthorizeSession) error + CreateImplicitAccessTokenSession(token string, request fosite.Requester) (err error) } diff --git a/handler/core/owner/owner.go b/handler/core/owner/owner.go index 585a55311..7b4094a51 100644 --- a/handler/core/owner/owner.go +++ b/handler/core/owner/owner.go @@ -4,7 +4,6 @@ import ( "github.com/go-errors/errors" "github.com/ory-am/common/pkg" "github.com/ory-am/fosite" - "github.com/ory-am/fosite/enigma" "github.com/ory-am/fosite/handler/core" "golang.org/x/net/context" "net/http" @@ -14,8 +13,7 @@ import ( ) type ResourceOwnerPasswordCredentialsGrantHandler struct { - // Enigma is the algorithm responsible for creating a validatable, opaque string. - Enigma enigma.Enigma + AccessTokenStrategy core.AccessTokenStrategy // Store is used to persist session data across requests. Store ResourceOwnerPasswordCredentialsGrantStorage @@ -25,7 +23,7 @@ type ResourceOwnerPasswordCredentialsGrantHandler struct { } // ValidateTokenEndpointRequest implements https://tools.ietf.org/html/rfc6749#section-4.3.2 -func (c *ResourceOwnerPasswordCredentialsGrantHandler) ValidateTokenEndpointRequest(_ context.Context, req *http.Request, request fosite.AccessRequester, session interface{}) error { +func (c *ResourceOwnerPasswordCredentialsGrantHandler) ValidateTokenEndpointRequest(_ context.Context, req *http.Request, request fosite.AccessRequester) error { // grant_type REQUIRED. // Value MUST be set to "password". if request.GetGrantType() != "password" { @@ -42,24 +40,27 @@ func (c *ResourceOwnerPasswordCredentialsGrantHandler) ValidateTokenEndpointRequ return errors.New(fosite.ErrServerError) } + // Credentials must not be passed around, potentially leaking to the database! + delete(request.GetRequestForm(), "password") + request.SetGrantTypeHandled("password") return nil } // HandleTokenEndpointRequest implements https://tools.ietf.org/html/rfc6749#section-4.3.3 -func (c *ResourceOwnerPasswordCredentialsGrantHandler) HandleTokenEndpointRequest(ctx context.Context, req *http.Request, requester fosite.AccessRequester, responder fosite.AccessResponder, session interface{}) error { +func (c *ResourceOwnerPasswordCredentialsGrantHandler) HandleTokenEndpointRequest(ctx context.Context, req *http.Request, requester fosite.AccessRequester, responder fosite.AccessResponder) error { if requester.GetGrantType() != "password" { return nil } - access, err := c.Enigma.GenerateChallenge(requester.GetClient().GetHashedSecret()) + token, signature, err := c.AccessTokenStrategy.GenerateAccessToken(ctx, req, requester) if err != nil { return errors.New(fosite.ErrServerError) - } else if err := c.Store.CreateAccessTokenSession(access.Signature, requester, &core.TokenSession{Extra: session}); err != nil { + } else if err := c.Store.CreateAccessTokenSession(signature, requester); err != nil { return errors.New(fosite.ErrServerError) } - responder.SetAccessToken(access.String()) + responder.SetAccessToken(token) responder.SetTokenType("bearer") responder.SetExtra("expires_in", strconv.Itoa(int(c.AccessTokenLifespan/time.Second))) responder.SetExtra("scope", strings.Join(requester.GetGrantedScopes(), " ")) diff --git a/handler/core/owner/owner_test.go b/handler/core/owner/owner_test.go index a0201b38d..09377e76e 100644 --- a/handler/core/owner/owner_test.go +++ b/handler/core/owner/owner_test.go @@ -5,8 +5,6 @@ import ( "github.com/golang/mock/gomock" "github.com/ory-am/common/pkg" "github.com/ory-am/fosite" - "github.com/ory-am/fosite/client" - "github.com/ory-am/fosite/enigma" "github.com/ory-am/fosite/internal" "github.com/stretchr/testify/assert" "net/http" @@ -18,13 +16,11 @@ import ( func TestValidateTokenEndpointRequest(t *testing.T) { ctrl := gomock.NewController(t) store := internal.NewMockResourceOwnerPasswordCredentialsGrantStorage(ctrl) - chgen := internal.NewMockEnigma(ctrl) areq := internal.NewMockAccessRequester(ctrl) defer ctrl.Finish() h := ResourceOwnerPasswordCredentialsGrantHandler{ Store: store, - Enigma: chgen, AccessTokenLifespan: time.Hour, } for k, c := range []struct { @@ -72,12 +68,13 @@ func TestValidateTokenEndpointRequest(t *testing.T) { mock: func() { areq.EXPECT().GetGrantType().Return("password") store.EXPECT().DoCredentialsAuthenticate("peter", "pan").Return(nil) + areq.EXPECT().GetRequestForm().Return(url.Values{}) areq.EXPECT().SetGrantTypeHandled("password") }, }, } { c.mock() - err := h.ValidateTokenEndpointRequest(nil, c.req, areq, nil) + err := h.ValidateTokenEndpointRequest(nil, c.req, areq) assert.True(t, errors.Is(c.expectErr, err), "%d\n%s\n%s", k, err, c.expectErr) t.Logf("Passed test case %d", k) } @@ -86,7 +83,7 @@ func TestValidateTokenEndpointRequest(t *testing.T) { func TestHandleTokenEndpointRequest(t *testing.T) { ctrl := gomock.NewController(t) store := internal.NewMockResourceOwnerPasswordCredentialsGrantStorage(ctrl) - chgen := internal.NewMockEnigma(ctrl) + chgen := internal.NewMockAccessTokenStrategy(ctrl) areq := internal.NewMockAccessRequester(ctrl) aresp := internal.NewMockAccessResponder(ctrl) //mockcl := internal.NewMockClient(ctrl) @@ -94,7 +91,7 @@ func TestHandleTokenEndpointRequest(t *testing.T) { h := ResourceOwnerPasswordCredentialsGrantHandler{ Store: store, - Enigma: chgen, + AccessTokenStrategy: chgen, AccessTokenLifespan: time.Hour, } for k, c := range []struct { @@ -110,28 +107,25 @@ func TestHandleTokenEndpointRequest(t *testing.T) { { mock: func() { areq.EXPECT().GetGrantType().Return("password") - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(nil, errors.New("")) + chgen.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "", errors.New("")) }, expectErr: fosite.ErrServerError, }, { mock: func() { areq.EXPECT().GetGrantType().Return("password") - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) - store.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("")) + chgen.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "foo", nil) + store.EXPECT().CreateAccessTokenSession("foo", gomock.Any()).Return(errors.New("")) }, expectErr: fosite.ErrServerError, }, { mock: func() { areq.EXPECT().GetGrantType().Return("password") - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) - store.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + chgen.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("foo.bar", "", nil) + store.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any()).Return(nil) - aresp.EXPECT().SetAccessToken(".") + aresp.EXPECT().SetAccessToken("foo.bar") aresp.EXPECT().SetTokenType("bearer") aresp.EXPECT().SetExtra("expires_in", gomock.Any()) aresp.EXPECT().SetExtra("scope", gomock.Any()) @@ -140,7 +134,7 @@ func TestHandleTokenEndpointRequest(t *testing.T) { }, } { c.mock() - err := h.HandleTokenEndpointRequest(nil, c.req, areq, aresp, nil) + err := h.HandleTokenEndpointRequest(nil, c.req, areq, aresp) assert.True(t, errors.Is(c.expectErr, err), "%d\n%s\n%s", k, err, c.expectErr) t.Logf("Passed test case %d", k) } diff --git a/handler/core/refresh/refresh.go b/handler/core/refresh/refresh.go index 4c2d16456..4d989572d 100644 --- a/handler/core/refresh/refresh.go +++ b/handler/core/refresh/refresh.go @@ -4,7 +4,6 @@ import ( "github.com/go-errors/errors" "github.com/ory-am/common/pkg" "github.com/ory-am/fosite" - "github.com/ory-am/fosite/enigma" "github.com/ory-am/fosite/handler/core" "golang.org/x/net/context" "net/http" @@ -14,8 +13,9 @@ import ( ) type RefreshTokenGrantHandler struct { - // Enigma is the algorithm responsible for creating a validatable, opaque string. - Enigma enigma.Enigma + AccessTokenStrategy core.AccessTokenStrategy + + RefreshTokenStrategy core.RefreshTokenStrategy // Store is used to persist session data across requests. Store RefreshTokenGrantStorage @@ -25,7 +25,7 @@ type RefreshTokenGrantHandler struct { } // ValidateTokenEndpointRequest implements https://tools.ietf.org/html/rfc6749#section-6 -func (c *RefreshTokenGrantHandler) ValidateTokenEndpointRequest(_ context.Context, req *http.Request, request fosite.AccessRequester, session interface{}) error { +func (c *RefreshTokenGrantHandler) ValidateTokenEndpointRequest(ctx context.Context, req *http.Request, request fosite.AccessRequester) error { // grant_type REQUIRED. // Value MUST be set to "client_credentials". if request.GetGrantType() != "refresh_token" { @@ -33,13 +33,12 @@ func (c *RefreshTokenGrantHandler) ValidateTokenEndpointRequest(_ context.Contex } // The authorization server MUST ... validate the refresh token. - challenge := new(enigma.Challenge) - challenge.FromString(req.Form.Get("refresh_token")) - if err := c.Enigma.ValidateChallenge(request.GetClient().GetHashedSecret(), challenge); err != nil { + signature, err := c.RefreshTokenStrategy.ValidateRefreshToken(req.Form.Get("refresh_token"), ctx, req, request) + if err != nil { return errors.New(fosite.ErrInvalidRequest) } - ar, err := c.Store.GetRefreshTokenSession(challenge.Signature, &core.TokenSession{Extra: session}) + accessRequest, err := c.Store.GetRefreshTokenSession(signature, nil) if err == pkg.ErrNotFound { return errors.New(fosite.ErrInvalidRequest) } else if err != nil { @@ -47,7 +46,7 @@ func (c *RefreshTokenGrantHandler) ValidateTokenEndpointRequest(_ context.Contex } // The authorization server MUST ... and ensure that the refresh token was issued to the authenticated client - if ar.GetClient().GetID() != request.GetClient().GetID() { + if accessRequest.GetClient().GetID() != request.GetClient().GetID() { return errors.New(fosite.ErrInvalidRequest) } @@ -56,35 +55,38 @@ func (c *RefreshTokenGrantHandler) ValidateTokenEndpointRequest(_ context.Contex } // HandleTokenEndpointRequest implements https://tools.ietf.org/html/rfc6749#section-6 -func (c *RefreshTokenGrantHandler) HandleTokenEndpointRequest(ctx context.Context, req *http.Request, requester fosite.AccessRequester, responder fosite.AccessResponder, session interface{}) error { +func (c *RefreshTokenGrantHandler) HandleTokenEndpointRequest(ctx context.Context, req *http.Request, requester fosite.AccessRequester, responder fosite.AccessResponder) error { if requester.GetGrantType() != "refresh_token" { return nil } - access, err := c.Enigma.GenerateChallenge(requester.GetClient().GetHashedSecret()) + accessToken, accessSignature, err := c.AccessTokenStrategy.GenerateAccessToken(ctx, req, requester) if err != nil { return errors.New(fosite.ErrServerError) - } else if err := c.Store.CreateAccessTokenSession(access.Signature, requester, &core.TokenSession{Extra: session}); err != nil { + } else if err := c.Store.CreateAccessTokenSession(accessSignature, requester); err != nil { return errors.New(fosite.ErrServerError) } - refresh, err := c.Enigma.GenerateChallenge(requester.GetClient().GetHashedSecret()) + refreshToken, refreshSignature, err := c.RefreshTokenStrategy.GenerateRefreshToken(ctx, req, requester) if err != nil { return errors.New(fosite.ErrServerError) - } else if err := c.Store.CreateRefreshTokenSession(refresh.Signature, requester, &core.TokenSession{Extra: session}); err != nil { + } else if err := c.Store.CreateRefreshTokenSession(refreshSignature, requester); err != nil { return errors.New(fosite.ErrServerError) } - challenge := new(enigma.Challenge) - challenge.FromString(req.Form.Get("refresh_token")) - if err := c.Store.DeleteRefreshTokenSession(challenge.Signature); err != nil { + signature, err := c.RefreshTokenStrategy.ValidateRefreshToken(req.PostForm.Get("refresh_token"), ctx, req, requester) + if err != nil { + return errors.New(fosite.ErrInvalidRequest) + } + + if err := c.Store.DeleteRefreshTokenSession(signature); err != nil { return errors.New(fosite.ErrServerError) } - responder.SetAccessToken(access.String()) + responder.SetAccessToken(accessToken) responder.SetTokenType("bearer") responder.SetExtra("expires_in", strconv.Itoa(int(c.AccessTokenLifespan/time.Second))) responder.SetExtra("scope", strings.Join(requester.GetGrantedScopes(), " ")) - responder.SetExtra("refresh_token", refresh.String()) + responder.SetExtra("refresh_token", refreshToken) return nil } diff --git a/handler/core/refresh/refresh_test.go b/handler/core/refresh/refresh_test.go index 36e3cd8c5..8cf0842cf 100644 --- a/handler/core/refresh/refresh_test.go +++ b/handler/core/refresh/refresh_test.go @@ -6,7 +6,6 @@ import ( "github.com/ory-am/common/pkg" "github.com/ory-am/fosite" "github.com/ory-am/fosite/client" - "github.com/ory-am/fosite/enigma" "github.com/ory-am/fosite/internal" "github.com/stretchr/testify/assert" "net/http" @@ -18,14 +17,14 @@ import ( func TestValidateTokenEndpointRequest(t *testing.T) { ctrl := gomock.NewController(t) store := internal.NewMockRefreshTokenGrantStorage(ctrl) - chgen := internal.NewMockEnigma(ctrl) + chgen := internal.NewMockRefreshTokenStrategy(ctrl) areq := internal.NewMockAccessRequester(ctrl) defer ctrl.Finish() h := RefreshTokenGrantHandler{ - Store: store, - Enigma: chgen, - AccessTokenLifespan: time.Hour, + Store: store, + RefreshTokenStrategy: chgen, + AccessTokenLifespan: time.Hour, } for k, c := range []struct { mock func() @@ -41,8 +40,7 @@ func TestValidateTokenEndpointRequest(t *testing.T) { req: &http.Request{PostForm: url.Values{}}, mock: func() { areq.EXPECT().GetGrantType().Return("refresh_token") - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().ValidateChallenge(gomock.Any(), gomock.Any()).Return(errors.New("")) + chgen.EXPECT().ValidateRefreshToken("", gomock.Any(), gomock.Any(), gomock.Any()).Return("", errors.New("")) }, expectErr: fosite.ErrInvalidRequest, }, @@ -50,9 +48,8 @@ func TestValidateTokenEndpointRequest(t *testing.T) { req: &http.Request{PostForm: url.Values{}}, mock: func() { areq.EXPECT().GetGrantType().Return("refresh_token") - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().ValidateChallenge(gomock.Any(), gomock.Any()).Return(nil) - store.EXPECT().GetRefreshTokenSession(gomock.Any(), gomock.Any()).Return(nil, pkg.ErrNotFound) + chgen.EXPECT().ValidateRefreshToken("", gomock.Any(), gomock.Any(), gomock.Any()).Return("signature", nil) + store.EXPECT().GetRefreshTokenSession("signature", gomock.Any()).Return(nil, pkg.ErrNotFound) }, expectErr: fosite.ErrInvalidRequest, }, @@ -60,8 +57,7 @@ func TestValidateTokenEndpointRequest(t *testing.T) { req: &http.Request{PostForm: url.Values{}}, mock: func() { areq.EXPECT().GetGrantType().Return("refresh_token") - areq.EXPECT().GetClient().Return(&client.SecureClient{}) - chgen.EXPECT().ValidateChallenge(gomock.Any(), gomock.Any()).Return(nil) + chgen.EXPECT().ValidateRefreshToken("", gomock.Any(), gomock.Any(), gomock.Any()).Return("signature", nil) store.EXPECT().GetRefreshTokenSession(gomock.Any(), gomock.Any()).Return(nil, errors.New("")) }, expectErr: fosite.ErrServerError, @@ -71,9 +67,8 @@ func TestValidateTokenEndpointRequest(t *testing.T) { mock: func() { areq.EXPECT().GetGrantType().Return("refresh_token") areq.EXPECT().GetClient().Return(&client.SecureClient{ID: "foo"}) - areq.EXPECT().GetClient().Return(&client.SecureClient{ID: "foo"}) - chgen.EXPECT().ValidateChallenge(gomock.Any(), gomock.Any()).Return(nil) - store.EXPECT().GetRefreshTokenSession(gomock.Any(), gomock.Any()).Return(&fosite.AccessRequest{Client: &client.SecureClient{ID: ""}}, nil) + chgen.EXPECT().ValidateRefreshToken("", gomock.Any(), gomock.Any(), gomock.Any()).Return("signature", nil) + store.EXPECT().GetRefreshTokenSession(gomock.Any(), gomock.Any()).Return(&fosite.Request{Client: &client.SecureClient{ID: ""}}, nil) }, expectErr: fosite.ErrInvalidRequest, }, @@ -82,15 +77,14 @@ func TestValidateTokenEndpointRequest(t *testing.T) { mock: func() { areq.EXPECT().GetGrantType().Return("refresh_token") areq.EXPECT().GetClient().Return(&client.SecureClient{ID: "foo"}) - areq.EXPECT().GetClient().Return(&client.SecureClient{ID: "foo"}) - chgen.EXPECT().ValidateChallenge(gomock.Any(), gomock.Any()).Return(nil) - store.EXPECT().GetRefreshTokenSession(gomock.Any(), gomock.Any()).Return(&fosite.AccessRequest{Client: &client.SecureClient{ID: "foo"}}, nil) + chgen.EXPECT().ValidateRefreshToken("", gomock.Any(), gomock.Any(), gomock.Any()).Return("signature", nil) + store.EXPECT().GetRefreshTokenSession(gomock.Any(), gomock.Any()).Return(&fosite.Request{Client: &client.SecureClient{ID: "foo"}}, nil) areq.EXPECT().SetGrantTypeHandled("refresh_token") }, }, } { c.mock() - err := h.ValidateTokenEndpointRequest(nil, c.req, areq, nil) + err := h.ValidateTokenEndpointRequest(nil, c.req, areq) assert.True(t, errors.Is(c.expectErr, err), "%d\n%s\n%s", k, err, c.expectErr) t.Logf("Passed test case %d", k) } @@ -99,7 +93,8 @@ func TestValidateTokenEndpointRequest(t *testing.T) { func TestHandleTokenEndpointRequest(t *testing.T) { ctrl := gomock.NewController(t) store := internal.NewMockRefreshTokenGrantStorage(ctrl) - chgen := internal.NewMockEnigma(ctrl) + rcts := internal.NewMockRefreshTokenStrategy(ctrl) + acts := internal.NewMockAccessTokenStrategy(ctrl) areq := internal.NewMockAccessRequester(ctrl) aresp := internal.NewMockAccessResponder(ctrl) //mockcl := internal.NewMockClient(ctrl) @@ -108,9 +103,10 @@ func TestHandleTokenEndpointRequest(t *testing.T) { areq.EXPECT().GetClient().AnyTimes().Return(&client.SecureClient{}) h := RefreshTokenGrantHandler{ - Store: store, - Enigma: chgen, - AccessTokenLifespan: time.Hour, + Store: store, + RefreshTokenStrategy: rcts, + AccessTokenStrategy: acts, + AccessTokenLifespan: time.Hour, } for k, c := range []struct { mock func() @@ -125,7 +121,7 @@ func TestHandleTokenEndpointRequest(t *testing.T) { { mock: func() { areq.EXPECT().GetGrantType().Return("refresh_token") - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(nil, errors.New("")) + acts.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "", errors.New("")) }, expectErr: fosite.ErrServerError, }, @@ -133,10 +129,10 @@ func TestHandleTokenEndpointRequest(t *testing.T) { req: &http.Request{PostForm: url.Values{}}, mock: func() { areq.EXPECT().GetGrantType().Return("refresh_token") - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) - store.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - store.EXPECT().CreateRefreshTokenSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("")) + acts.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "access", nil) + rcts.EXPECT().GenerateRefreshToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "refresh", nil) + store.EXPECT().CreateAccessTokenSession("access", gomock.Any()).Return(nil) + store.EXPECT().CreateRefreshTokenSession("refresh", gomock.Any()).Return(errors.New("")) }, expectErr: fosite.ErrServerError, }, @@ -144,10 +140,11 @@ func TestHandleTokenEndpointRequest(t *testing.T) { req: &http.Request{PostForm: url.Values{}}, mock: func() { areq.EXPECT().GetGrantType().Return("refresh_token") - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) - store.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - store.EXPECT().CreateRefreshTokenSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + acts.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "access", nil) + rcts.EXPECT().GenerateRefreshToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("", "refresh", nil) + rcts.EXPECT().ValidateRefreshToken(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("signature", nil) + store.EXPECT().CreateAccessTokenSession("access", gomock.Any()).Return(nil) + store.EXPECT().CreateRefreshTokenSession("refresh", gomock.Any()).Return(nil) store.EXPECT().DeleteRefreshTokenSession(gomock.Any()).Return(errors.New("")) }, expectErr: fosite.ErrServerError, @@ -156,23 +153,24 @@ func TestHandleTokenEndpointRequest(t *testing.T) { req: &http.Request{PostForm: url.Values{}}, mock: func() { areq.EXPECT().GetGrantType().Return("refresh_token") - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) - chgen.EXPECT().GenerateChallenge(gomock.Any()).Return(&enigma.Challenge{}, nil) - store.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - store.EXPECT().CreateRefreshTokenSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + acts.EXPECT().GenerateAccessToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("access.token", "access", nil) + rcts.EXPECT().GenerateRefreshToken(gomock.Any(), gomock.Any(), gomock.Any()).Return("refresh.token", "refresh", nil) + rcts.EXPECT().ValidateRefreshToken(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("signature", nil) + store.EXPECT().CreateAccessTokenSession("access", gomock.Any()).Return(nil) + store.EXPECT().CreateRefreshTokenSession("refresh", gomock.Any()).Return(nil) store.EXPECT().DeleteRefreshTokenSession(gomock.Any()).Return(nil) - aresp.EXPECT().SetAccessToken(".") + aresp.EXPECT().SetAccessToken("access.token") aresp.EXPECT().SetTokenType("bearer") aresp.EXPECT().SetExtra("expires_in", gomock.Any()) aresp.EXPECT().SetExtra("scope", gomock.Any()) - aresp.EXPECT().SetExtra("refresh_token", ".") + aresp.EXPECT().SetExtra("refresh_token", "refresh.token") areq.EXPECT().GetGrantedScopes() }, }, } { c.mock() - err := h.HandleTokenEndpointRequest(nil, c.req, areq, aresp, nil) + err := h.HandleTokenEndpointRequest(nil, c.req, areq, aresp) assert.True(t, errors.Is(c.expectErr, err), "%d\n%s\n%s", k, err, c.expectErr) t.Logf("Passed test case %d", k) } diff --git a/handler/core/storage.go b/handler/core/storage.go index 994025827..7189bd8bb 100644 --- a/handler/core/storage.go +++ b/handler/core/storage.go @@ -4,35 +4,26 @@ import ( "github.com/ory-am/fosite" ) -type TokenSession struct { - Extra interface{} -} - -type AuthorizeSession struct { - RequestRedirectURI string - Extra interface{} -} - type AuthorizeCodeStorage interface { - CreateAuthorizeCodeSession(string, fosite.AuthorizeRequester, *AuthorizeSession) error + CreateAuthorizeCodeSession(code string, request fosite.Requester) (err error) - GetAuthorizeCodeSession(string, *AuthorizeSession) (fosite.AuthorizeRequester, error) + GetAuthorizeCodeSession(code string, session interface{}) (request fosite.Requester, err error) - DeleteAuthorizeCodeSession(code string) error + DeleteAuthorizeCodeSession(code string) (err error) } type AccessTokenStorage interface { - CreateAccessTokenSession(signature string, access fosite.AccessRequester, session *TokenSession) error + CreateAccessTokenSession(signature string, request fosite.Requester) (err error) - GetAccessTokenSession(signature string, session *TokenSession) (fosite.AccessRequester, error) + GetAccessTokenSession(signature string, session interface{}) (request fosite.Requester, err error) - DeleteAccessTokenSession(signature string) error + DeleteAccessTokenSession(signature string) (err error) } type RefreshTokenStorage interface { - CreateRefreshTokenSession(signature string, access fosite.AccessRequester, session *TokenSession) error + CreateRefreshTokenSession(signature string, request fosite.Requester) (err error) - GetRefreshTokenSession(signature string, session *TokenSession) (fosite.AccessRequester, error) + GetRefreshTokenSession(signature string, session interface{}) (request fosite.Requester, err error) - DeleteRefreshTokenSession(signature string) error + DeleteRefreshTokenSession(signature string) (err error) } diff --git a/handler/core/strategy.go b/handler/core/strategy.go new file mode 100644 index 000000000..5e8eaccd5 --- /dev/null +++ b/handler/core/strategy.go @@ -0,0 +1,22 @@ +package core + +import ( + "github.com/ory-am/fosite" + "golang.org/x/net/context" + "net/http" +) + +type AccessTokenStrategy interface { + GenerateAccessToken(ctx context.Context, req *http.Request, requester fosite.Requester) (token string, signature string, err error) + ValidateAccessToken(token string, ctx context.Context, req *http.Request, requester fosite.Requester) (signature string, err error) +} + +type RefreshTokenStrategy interface { + GenerateRefreshToken(ctx context.Context, req *http.Request, requester fosite.Requester) (token string, signature string, err error) + ValidateRefreshToken(token string, ctx context.Context, req *http.Request, requester fosite.Requester) (signature string, err error) +} + +type AuthorizeCodeStrategy interface { + GenerateAuthorizeCode(ctx context.Context, req *http.Request, requester fosite.Requester) (token string, signature string, err error) + ValidateAuthorizeCode(token string, ctx context.Context, req *http.Request, requester fosite.Requester) (signature string, err error) +} diff --git a/handler/core/strategy/hmacsha.go b/handler/core/strategy/hmacsha.go new file mode 100644 index 000000000..8c45b8db9 --- /dev/null +++ b/handler/core/strategy/hmacsha.go @@ -0,0 +1,36 @@ +package strategy + +import ( + "github.com/ory-am/fosite" + "github.com/ory-am/fosite/enigma" + "golang.org/x/net/context" + "net/http" +) + +type HMACSHAStrategy struct { + Enigma *enigma.HMACSHAEnigma +} + +func (h HMACSHAStrategy) GenerateAccessToken(_ context.Context, _ *http.Request, requester fosite.Requester) (token string, signature string, err error) { + return h.Enigma.Generate(requester.GetClient().GetHashedSecret()) +} + +func (h HMACSHAStrategy) ValidateAccessToken(token string, _ context.Context, _ *http.Request, requester fosite.Requester) (signature string, err error) { + return h.Enigma.Validate(requester.GetClient().GetHashedSecret(), token) +} + +func (h HMACSHAStrategy) GenerateRefreshToken(_ context.Context, _ *http.Request, requester fosite.Requester) (token string, signature string, err error) { + return h.Enigma.Generate(requester.GetClient().GetHashedSecret()) +} + +func (h HMACSHAStrategy) ValidateRefreshToken(token string, _ context.Context, _ *http.Request, requester fosite.Requester) (signature string, err error) { + return h.Enigma.Validate(requester.GetClient().GetHashedSecret(), token) +} + +func (h HMACSHAStrategy) GenerateAuthorizeCode(_ context.Context, _ *http.Request, requester fosite.Requester) (token string, signature string, err error) { + return h.Enigma.Generate(requester.GetClient().GetHashedSecret()) +} + +func (h HMACSHAStrategy) ValidateAuthorizeCode(token string, _ context.Context, _ *http.Request, requester fosite.Requester) (signature string, err error) { + return h.Enigma.Validate(requester.GetClient().GetHashedSecret(), token) +} diff --git a/handler/core/strategy/strategy_test.go b/handler/core/strategy/strategy_test.go new file mode 100644 index 000000000..e8d61bbad --- /dev/null +++ b/handler/core/strategy/strategy_test.go @@ -0,0 +1,50 @@ +package strategy + +import ( + "github.com/ory-am/fosite" + "github.com/ory-am/fosite/client" + "github.com/ory-am/fosite/enigma" + "github.com/stretchr/testify/assert" + "strings" + "testing" +) + +var s = HMACSHAStrategy{ + Enigma: &enigma.HMACSHAEnigma{GlobalSecret: []byte("foobarfoobarfoobarfoobar")}, +} + +var r = &fosite.Request{ + Client: &client.SecureClient{ + Secret: []byte("foobarfoobarfoobarfoobar"), + }, +} + +func TestAccessToken(t *testing.T) { + token, signature, err := s.GenerateAccessToken(nil, nil, r) + assert.Nil(t, err, "%s", err) + assert.Equal(t, strings.Split(token, ".")[1], signature) + + validate, err := s.ValidateAccessToken(token, nil, nil, r) + assert.Nil(t, err, "%s", err) + assert.Equal(t, signature, validate) +} + +func TestRefreshToken(t *testing.T) { + token, signature, err := s.GenerateRefreshToken(nil, nil, r) + assert.Nil(t, err, "%s", err) + assert.Equal(t, strings.Split(token, ".")[1], signature) + + validate, err := s.ValidateRefreshToken(token, nil, nil, r) + assert.Nil(t, err, "%s", err) + assert.Equal(t, signature, validate) +} + +func TestGenerateAuthorizeCode(t *testing.T) { + token, signature, err := s.GenerateAuthorizeCode(nil, nil, r) + assert.Nil(t, err, "%s", err) + assert.Equal(t, strings.Split(token, ".")[1], signature) + + validate, err := s.ValidateAuthorizeCode(token, nil, nil, r) + assert.Nil(t, err, "%s", err) + assert.Equal(t, signature, validate) +} diff --git a/handler/oidc/oidc_auth.go b/handler/oidc/oidc_auth.go new file mode 100644 index 000000000..099a8dc3b --- /dev/null +++ b/handler/oidc/oidc_auth.go @@ -0,0 +1,58 @@ +package oidc + +/* +import ( + "github.com/go-errors/errors" + . "github.com/ory-am/fosite" + "github.com/ory-am/fosite/enigma" + . "github.com/ory-am/fosite/handler/core" + "golang.org/x/net/context" + "net/http" + "strings" + "time" +) + +const authCodeDefaultLifespan = time.Hour / 2 + +// CodeAuthorizeEndpointHandler is a response handler for the Authorize Code grant using the explicit grant type +// as defined in https://tools.ietf.org/html/rfc6749#section-4.1 +type AuthorizeExplicitGrantTypeHandler struct { + // Enigma is the algorithm responsible for creating a validatable, opaque string. + Enigma enigma.Enigma + + // Store is used to persist session data across requests. + Store AuthorizeCodeGrantStorage + + // AuthCodeLifespan defines the lifetime of an authorize code. + AuthCodeLifespan time.Duration + + // AccessTokenLifespan defines the lifetime of an access token. + AccessTokenLifespan time.Duration +} + +func (c *AuthorizeExplicitGrantTypeHandler) HandleAuthorizeEndpointRequest(_ context.Context, req *http.Request, ar AuthorizeRequester, resp AuthorizeResponder, session interface{}) error { + // This let's us define multiple response types, for example open id connect's id_token + if ar.GetResponseTypes().Has("code") { + // Generate the code + code, err := c.Enigma.GenerateChallenge(ar.GetClient().GetHashedSecret()) + if err != nil { + return errors.New(ErrServerError) + } + + if err := c.Store.CreateAuthorizeCodeSession(code.Signature, ar, &AuthorizeSession{ + Extra: session, + RequestRedirectURI: req.Form.Get("redirect_uri"), + }); err != nil { + return errors.New(ErrServerError) + } + + resp.AddQuery("code", code.String()) + resp.AddQuery("state", ar.GetState()) + resp.AddQuery("scope", strings.Join(ar.GetGrantedScopes(), " ")) + ar.SetResponseTypeHandled("code") + return nil + } + + return nil +} +*/ diff --git a/internal/access_request.go b/internal/access_request.go index 4b0649801..d236730c2 100644 --- a/internal/access_request.go +++ b/internal/access_request.go @@ -7,6 +7,7 @@ import ( gomock "github.com/golang/mock/gomock" fosite "github.com/ory-am/fosite" client "github.com/ory-am/fosite/client" + url "net/url" time "time" ) @@ -71,6 +72,16 @@ func (_mr *_MockAccessRequesterRecorder) GetGrantedScopes() *gomock.Call { return _mr.mock.ctrl.RecordCall(_mr.mock, "GetGrantedScopes") } +func (_m *MockAccessRequester) GetRequestForm() url.Values { + ret := _m.ctrl.Call(_m, "GetRequestForm") + ret0, _ := ret[0].(url.Values) + return ret0 +} + +func (_mr *_MockAccessRequesterRecorder) GetRequestForm() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "GetRequestForm") +} + func (_m *MockAccessRequester) GetRequestedAt() time.Time { ret := _m.ctrl.Call(_m, "GetRequestedAt") ret0, _ := ret[0].(time.Time) @@ -91,6 +102,16 @@ func (_mr *_MockAccessRequesterRecorder) GetScopes() *gomock.Call { return _mr.mock.ctrl.RecordCall(_mr.mock, "GetScopes") } +func (_m *MockAccessRequester) GetSession() interface{} { + ret := _m.ctrl.Call(_m, "GetSession") + ret0, _ := ret[0].(interface{}) + return ret0 +} + +func (_mr *_MockAccessRequesterRecorder) GetSession() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "GetSession") +} + func (_m *MockAccessRequester) GrantScope(_param0 string) { _m.ctrl.Call(_m, "GrantScope", _param0) } @@ -114,3 +135,11 @@ func (_m *MockAccessRequester) SetScopes(_param0 fosite.Arguments) { func (_mr *_MockAccessRequesterRecorder) SetScopes(arg0 interface{}) *gomock.Call { return _mr.mock.ctrl.RecordCall(_mr.mock, "SetScopes", arg0) } + +func (_m *MockAccessRequester) SetSession(_param0 interface{}) { + _m.ctrl.Call(_m, "SetSession", _param0) +} + +func (_mr *_MockAccessRequesterRecorder) SetSession(arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "SetSession", arg0) +} diff --git a/internal/access_token_strategy.go b/internal/access_token_strategy.go new file mode 100644 index 000000000..e1b469281 --- /dev/null +++ b/internal/access_token_strategy.go @@ -0,0 +1,55 @@ +// Automatically generated by MockGen. DO NOT EDIT! +// Source: github.com/ory-am/fosite/handler/core (interfaces: AccessTokenStrategy) + +package internal + +import ( + gomock "github.com/golang/mock/gomock" + fosite "github.com/ory-am/fosite" + context "golang.org/x/net/context" + http "net/http" +) + +// Mock of AccessTokenStrategy interface +type MockAccessTokenStrategy struct { + ctrl *gomock.Controller + recorder *_MockAccessTokenStrategyRecorder +} + +// Recorder for MockAccessTokenStrategy (not exported) +type _MockAccessTokenStrategyRecorder struct { + mock *MockAccessTokenStrategy +} + +func NewMockAccessTokenStrategy(ctrl *gomock.Controller) *MockAccessTokenStrategy { + mock := &MockAccessTokenStrategy{ctrl: ctrl} + mock.recorder = &_MockAccessTokenStrategyRecorder{mock} + return mock +} + +func (_m *MockAccessTokenStrategy) EXPECT() *_MockAccessTokenStrategyRecorder { + return _m.recorder +} + +func (_m *MockAccessTokenStrategy) GenerateAccessToken(_param0 context.Context, _param1 *http.Request, _param2 fosite.Requester) (string, string, error) { + ret := _m.ctrl.Call(_m, "GenerateAccessToken", _param0, _param1, _param2) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(string) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +func (_mr *_MockAccessTokenStrategyRecorder) GenerateAccessToken(arg0, arg1, arg2 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "GenerateAccessToken", arg0, arg1, arg2) +} + +func (_m *MockAccessTokenStrategy) ValidateAccessToken(_param0 string, _param1 context.Context, _param2 *http.Request, _param3 fosite.Requester) (string, error) { + ret := _m.ctrl.Call(_m, "ValidateAccessToken", _param0, _param1, _param2, _param3) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +func (_mr *_MockAccessTokenStrategyRecorder) ValidateAccessToken(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ValidateAccessToken", arg0, arg1, arg2, arg3) +} diff --git a/internal/authorize_code_strategy.go b/internal/authorize_code_strategy.go new file mode 100644 index 000000000..be3574157 --- /dev/null +++ b/internal/authorize_code_strategy.go @@ -0,0 +1,55 @@ +// Automatically generated by MockGen. DO NOT EDIT! +// Source: github.com/ory-am/fosite/handler/core (interfaces: AuthorizeCodeStrategy) + +package internal + +import ( + gomock "github.com/golang/mock/gomock" + fosite "github.com/ory-am/fosite" + context "golang.org/x/net/context" + http "net/http" +) + +// Mock of AuthorizeCodeStrategy interface +type MockAuthorizeCodeStrategy struct { + ctrl *gomock.Controller + recorder *_MockAuthorizeCodeStrategyRecorder +} + +// Recorder for MockAuthorizeCodeStrategy (not exported) +type _MockAuthorizeCodeStrategyRecorder struct { + mock *MockAuthorizeCodeStrategy +} + +func NewMockAuthorizeCodeStrategy(ctrl *gomock.Controller) *MockAuthorizeCodeStrategy { + mock := &MockAuthorizeCodeStrategy{ctrl: ctrl} + mock.recorder = &_MockAuthorizeCodeStrategyRecorder{mock} + return mock +} + +func (_m *MockAuthorizeCodeStrategy) EXPECT() *_MockAuthorizeCodeStrategyRecorder { + return _m.recorder +} + +func (_m *MockAuthorizeCodeStrategy) GenerateAuthorizeCode(_param0 context.Context, _param1 *http.Request, _param2 fosite.Requester) (string, string, error) { + ret := _m.ctrl.Call(_m, "GenerateAuthorizeCode", _param0, _param1, _param2) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(string) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +func (_mr *_MockAuthorizeCodeStrategyRecorder) GenerateAuthorizeCode(arg0, arg1, arg2 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "GenerateAuthorizeCode", arg0, arg1, arg2) +} + +func (_m *MockAuthorizeCodeStrategy) ValidateAuthorizeCode(_param0 string, _param1 context.Context, _param2 *http.Request, _param3 fosite.Requester) (string, error) { + ret := _m.ctrl.Call(_m, "ValidateAuthorizeCode", _param0, _param1, _param2, _param3) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +func (_mr *_MockAuthorizeCodeStrategyRecorder) ValidateAuthorizeCode(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ValidateAuthorizeCode", arg0, arg1, arg2, arg3) +} diff --git a/internal/authorize_handler.go b/internal/authorize_handler.go index 68e9b27ee..4bb2302a3 100644 --- a/internal/authorize_handler.go +++ b/internal/authorize_handler.go @@ -31,12 +31,12 @@ func (_m *MockAuthorizeEndpointHandler) EXPECT() *_MockAuthorizeEndpointHandlerR return _m.recorder } -func (_m *MockAuthorizeEndpointHandler) HandleAuthorizeEndpointRequest(_param0 context.Context, _param1 *http.Request, _param2 fosite.AuthorizeRequester, _param3 fosite.AuthorizeResponder, _param4 interface{}) error { - ret := _m.ctrl.Call(_m, "HandleAuthorizeEndpointRequest", _param0, _param1, _param2, _param3, _param4) +func (_m *MockAuthorizeEndpointHandler) HandleAuthorizeEndpointRequest(_param0 context.Context, _param1 *http.Request, _param2 fosite.AuthorizeRequester, _param3 fosite.AuthorizeResponder) error { + ret := _m.ctrl.Call(_m, "HandleAuthorizeEndpointRequest", _param0, _param1, _param2, _param3) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockAuthorizeEndpointHandlerRecorder) HandleAuthorizeEndpointRequest(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "HandleAuthorizeEndpointRequest", arg0, arg1, arg2, arg3, arg4) +func (_mr *_MockAuthorizeEndpointHandlerRecorder) HandleAuthorizeEndpointRequest(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "HandleAuthorizeEndpointRequest", arg0, arg1, arg2, arg3) } diff --git a/internal/authorize_request.go b/internal/authorize_request.go index 9cac17fcf..30c60eeff 100644 --- a/internal/authorize_request.go +++ b/internal/authorize_request.go @@ -72,6 +72,16 @@ func (_mr *_MockAuthorizeRequesterRecorder) GetRedirectURI() *gomock.Call { return _mr.mock.ctrl.RecordCall(_mr.mock, "GetRedirectURI") } +func (_m *MockAuthorizeRequester) GetRequestForm() url.Values { + ret := _m.ctrl.Call(_m, "GetRequestForm") + ret0, _ := ret[0].(url.Values) + return ret0 +} + +func (_mr *_MockAuthorizeRequesterRecorder) GetRequestForm() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "GetRequestForm") +} + func (_m *MockAuthorizeRequester) GetRequestedAt() time.Time { ret := _m.ctrl.Call(_m, "GetRequestedAt") ret0, _ := ret[0].(time.Time) @@ -102,6 +112,16 @@ func (_mr *_MockAuthorizeRequesterRecorder) GetScopes() *gomock.Call { return _mr.mock.ctrl.RecordCall(_mr.mock, "GetScopes") } +func (_m *MockAuthorizeRequester) GetSession() interface{} { + ret := _m.ctrl.Call(_m, "GetSession") + ret0, _ := ret[0].(interface{}) + return ret0 +} + +func (_mr *_MockAuthorizeRequesterRecorder) GetSession() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "GetSession") +} + func (_m *MockAuthorizeRequester) GetState() string { ret := _m.ctrl.Call(_m, "GetState") ret0, _ := ret[0].(string) @@ -112,6 +132,14 @@ func (_mr *_MockAuthorizeRequesterRecorder) GetState() *gomock.Call { return _mr.mock.ctrl.RecordCall(_mr.mock, "GetState") } +func (_m *MockAuthorizeRequester) GrantScope(_param0 string) { + _m.ctrl.Call(_m, "GrantScope", _param0) +} + +func (_mr *_MockAuthorizeRequesterRecorder) GrantScope(arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "GrantScope", arg0) +} + func (_m *MockAuthorizeRequester) IsRedirectURIValid() bool { ret := _m.ctrl.Call(_m, "IsRedirectURIValid") ret0, _ := ret[0].(bool) @@ -137,3 +165,11 @@ func (_m *MockAuthorizeRequester) SetScopes(_param0 fosite.Arguments) { func (_mr *_MockAuthorizeRequesterRecorder) SetScopes(arg0 interface{}) *gomock.Call { return _mr.mock.ctrl.RecordCall(_mr.mock, "SetScopes", arg0) } + +func (_m *MockAuthorizeRequester) SetSession(_param0 interface{}) { + _m.ctrl.Call(_m, "SetSession", _param0) +} + +func (_mr *_MockAuthorizeRequesterRecorder) SetSession(arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "SetSession", arg0) +} diff --git a/internal/core_client_storage.go b/internal/core_client_storage.go index 3698eef74..fa8d83a95 100644 --- a/internal/core_client_storage.go +++ b/internal/core_client_storage.go @@ -6,7 +6,6 @@ package internal import ( gomock "github.com/golang/mock/gomock" fosite "github.com/ory-am/fosite" - core "github.com/ory-am/fosite/handler/core" ) // Mock of ClientCredentialsGrantStorage interface @@ -30,14 +29,14 @@ func (_m *MockClientCredentialsGrantStorage) EXPECT() *_MockClientCredentialsGra return _m.recorder } -func (_m *MockClientCredentialsGrantStorage) CreateAccessTokenSession(_param0 string, _param1 fosite.AccessRequester, _param2 *core.TokenSession) error { - ret := _m.ctrl.Call(_m, "CreateAccessTokenSession", _param0, _param1, _param2) +func (_m *MockClientCredentialsGrantStorage) CreateAccessTokenSession(_param0 string, _param1 fosite.Requester) error { + ret := _m.ctrl.Call(_m, "CreateAccessTokenSession", _param0, _param1) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockClientCredentialsGrantStorageRecorder) CreateAccessTokenSession(arg0, arg1, arg2 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateAccessTokenSession", arg0, arg1, arg2) +func (_mr *_MockClientCredentialsGrantStorageRecorder) CreateAccessTokenSession(arg0, arg1 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateAccessTokenSession", arg0, arg1) } func (_m *MockClientCredentialsGrantStorage) DeleteAccessTokenSession(_param0 string) error { @@ -50,9 +49,9 @@ func (_mr *_MockClientCredentialsGrantStorageRecorder) DeleteAccessTokenSession( return _mr.mock.ctrl.RecordCall(_mr.mock, "DeleteAccessTokenSession", arg0) } -func (_m *MockClientCredentialsGrantStorage) GetAccessTokenSession(_param0 string, _param1 *core.TokenSession) (fosite.AccessRequester, error) { +func (_m *MockClientCredentialsGrantStorage) GetAccessTokenSession(_param0 string, _param1 interface{}) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetAccessTokenSession", _param0, _param1) - ret0, _ := ret[0].(fosite.AccessRequester) + ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/internal/core_explicit_storage.go b/internal/core_explicit_storage.go index 47bb5409a..9b1927403 100644 --- a/internal/core_explicit_storage.go +++ b/internal/core_explicit_storage.go @@ -6,7 +6,6 @@ package internal import ( gomock "github.com/golang/mock/gomock" fosite "github.com/ory-am/fosite" - core "github.com/ory-am/fosite/handler/core" ) // Mock of AuthorizeCodeGrantStorage interface @@ -30,34 +29,34 @@ func (_m *MockAuthorizeCodeGrantStorage) EXPECT() *_MockAuthorizeCodeGrantStorag return _m.recorder } -func (_m *MockAuthorizeCodeGrantStorage) CreateAccessTokenSession(_param0 string, _param1 fosite.AccessRequester, _param2 *core.TokenSession) error { - ret := _m.ctrl.Call(_m, "CreateAccessTokenSession", _param0, _param1, _param2) +func (_m *MockAuthorizeCodeGrantStorage) CreateAccessTokenSession(_param0 string, _param1 fosite.Requester) error { + ret := _m.ctrl.Call(_m, "CreateAccessTokenSession", _param0, _param1) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockAuthorizeCodeGrantStorageRecorder) CreateAccessTokenSession(arg0, arg1, arg2 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateAccessTokenSession", arg0, arg1, arg2) +func (_mr *_MockAuthorizeCodeGrantStorageRecorder) CreateAccessTokenSession(arg0, arg1 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateAccessTokenSession", arg0, arg1) } -func (_m *MockAuthorizeCodeGrantStorage) CreateAuthorizeCodeSession(_param0 string, _param1 fosite.AuthorizeRequester, _param2 *core.AuthorizeSession) error { - ret := _m.ctrl.Call(_m, "CreateAuthorizeCodeSession", _param0, _param1, _param2) +func (_m *MockAuthorizeCodeGrantStorage) CreateAuthorizeCodeSession(_param0 string, _param1 fosite.Requester) error { + ret := _m.ctrl.Call(_m, "CreateAuthorizeCodeSession", _param0, _param1) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockAuthorizeCodeGrantStorageRecorder) CreateAuthorizeCodeSession(arg0, arg1, arg2 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateAuthorizeCodeSession", arg0, arg1, arg2) +func (_mr *_MockAuthorizeCodeGrantStorageRecorder) CreateAuthorizeCodeSession(arg0, arg1 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateAuthorizeCodeSession", arg0, arg1) } -func (_m *MockAuthorizeCodeGrantStorage) CreateRefreshTokenSession(_param0 string, _param1 fosite.AccessRequester, _param2 *core.TokenSession) error { - ret := _m.ctrl.Call(_m, "CreateRefreshTokenSession", _param0, _param1, _param2) +func (_m *MockAuthorizeCodeGrantStorage) CreateRefreshTokenSession(_param0 string, _param1 fosite.Requester) error { + ret := _m.ctrl.Call(_m, "CreateRefreshTokenSession", _param0, _param1) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockAuthorizeCodeGrantStorageRecorder) CreateRefreshTokenSession(arg0, arg1, arg2 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateRefreshTokenSession", arg0, arg1, arg2) +func (_mr *_MockAuthorizeCodeGrantStorageRecorder) CreateRefreshTokenSession(arg0, arg1 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateRefreshTokenSession", arg0, arg1) } func (_m *MockAuthorizeCodeGrantStorage) DeleteAccessTokenSession(_param0 string) error { @@ -90,9 +89,9 @@ func (_mr *_MockAuthorizeCodeGrantStorageRecorder) DeleteRefreshTokenSession(arg return _mr.mock.ctrl.RecordCall(_mr.mock, "DeleteRefreshTokenSession", arg0) } -func (_m *MockAuthorizeCodeGrantStorage) GetAccessTokenSession(_param0 string, _param1 *core.TokenSession) (fosite.AccessRequester, error) { +func (_m *MockAuthorizeCodeGrantStorage) GetAccessTokenSession(_param0 string, _param1 interface{}) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetAccessTokenSession", _param0, _param1) - ret0, _ := ret[0].(fosite.AccessRequester) + ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -101,9 +100,9 @@ func (_mr *_MockAuthorizeCodeGrantStorageRecorder) GetAccessTokenSession(arg0, a return _mr.mock.ctrl.RecordCall(_mr.mock, "GetAccessTokenSession", arg0, arg1) } -func (_m *MockAuthorizeCodeGrantStorage) GetAuthorizeCodeSession(_param0 string, _param1 *core.AuthorizeSession) (fosite.AuthorizeRequester, error) { +func (_m *MockAuthorizeCodeGrantStorage) GetAuthorizeCodeSession(_param0 string, _param1 interface{}) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetAuthorizeCodeSession", _param0, _param1) - ret0, _ := ret[0].(fosite.AuthorizeRequester) + ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -112,9 +111,9 @@ func (_mr *_MockAuthorizeCodeGrantStorageRecorder) GetAuthorizeCodeSession(arg0, return _mr.mock.ctrl.RecordCall(_mr.mock, "GetAuthorizeCodeSession", arg0, arg1) } -func (_m *MockAuthorizeCodeGrantStorage) GetRefreshTokenSession(_param0 string, _param1 *core.TokenSession) (fosite.AccessRequester, error) { +func (_m *MockAuthorizeCodeGrantStorage) GetRefreshTokenSession(_param0 string, _param1 interface{}) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetRefreshTokenSession", _param0, _param1) - ret0, _ := ret[0].(fosite.AccessRequester) + ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/internal/core_implicit_storage.go b/internal/core_implicit_storage.go index fac0aa918..57b34a781 100644 --- a/internal/core_implicit_storage.go +++ b/internal/core_implicit_storage.go @@ -6,7 +6,6 @@ package internal import ( gomock "github.com/golang/mock/gomock" fosite "github.com/ory-am/fosite" - core "github.com/ory-am/fosite/handler/core" ) // Mock of ImplicitGrantStorage interface @@ -30,12 +29,12 @@ func (_m *MockImplicitGrantStorage) EXPECT() *_MockImplicitGrantStorageRecorder return _m.recorder } -func (_m *MockImplicitGrantStorage) CreateImplicitAccessTokenSession(_param0 string, _param1 fosite.AuthorizeRequester, _param2 *core.AuthorizeSession) error { - ret := _m.ctrl.Call(_m, "CreateImplicitAccessTokenSession", _param0, _param1, _param2) +func (_m *MockImplicitGrantStorage) CreateImplicitAccessTokenSession(_param0 string, _param1 fosite.Requester) error { + ret := _m.ctrl.Call(_m, "CreateImplicitAccessTokenSession", _param0, _param1) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockImplicitGrantStorageRecorder) CreateImplicitAccessTokenSession(arg0, arg1, arg2 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateImplicitAccessTokenSession", arg0, arg1, arg2) +func (_mr *_MockImplicitGrantStorageRecorder) CreateImplicitAccessTokenSession(arg0, arg1 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateImplicitAccessTokenSession", arg0, arg1) } diff --git a/internal/core_owner_storage.go b/internal/core_owner_storage.go index aec2b0f14..7a8db92f1 100644 --- a/internal/core_owner_storage.go +++ b/internal/core_owner_storage.go @@ -6,7 +6,6 @@ package internal import ( gomock "github.com/golang/mock/gomock" fosite "github.com/ory-am/fosite" - core "github.com/ory-am/fosite/handler/core" ) // Mock of ResourceOwnerPasswordCredentialsGrantStorage interface @@ -30,14 +29,14 @@ func (_m *MockResourceOwnerPasswordCredentialsGrantStorage) EXPECT() *_MockResou return _m.recorder } -func (_m *MockResourceOwnerPasswordCredentialsGrantStorage) CreateAccessTokenSession(_param0 string, _param1 fosite.AccessRequester, _param2 *core.TokenSession) error { - ret := _m.ctrl.Call(_m, "CreateAccessTokenSession", _param0, _param1, _param2) +func (_m *MockResourceOwnerPasswordCredentialsGrantStorage) CreateAccessTokenSession(_param0 string, _param1 fosite.Requester) error { + ret := _m.ctrl.Call(_m, "CreateAccessTokenSession", _param0, _param1) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockResourceOwnerPasswordCredentialsGrantStorageRecorder) CreateAccessTokenSession(arg0, arg1, arg2 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateAccessTokenSession", arg0, arg1, arg2) +func (_mr *_MockResourceOwnerPasswordCredentialsGrantStorageRecorder) CreateAccessTokenSession(arg0, arg1 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateAccessTokenSession", arg0, arg1) } func (_m *MockResourceOwnerPasswordCredentialsGrantStorage) DeleteAccessTokenSession(_param0 string) error { @@ -60,9 +59,9 @@ func (_mr *_MockResourceOwnerPasswordCredentialsGrantStorageRecorder) DoCredenti return _mr.mock.ctrl.RecordCall(_mr.mock, "DoCredentialsAuthenticate", arg0, arg1) } -func (_m *MockResourceOwnerPasswordCredentialsGrantStorage) GetAccessTokenSession(_param0 string, _param1 *core.TokenSession) (fosite.AccessRequester, error) { +func (_m *MockResourceOwnerPasswordCredentialsGrantStorage) GetAccessTokenSession(_param0 string, _param1 interface{}) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetAccessTokenSession", _param0, _param1) - ret0, _ := ret[0].(fosite.AccessRequester) + ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/internal/core_refresh_storage.go b/internal/core_refresh_storage.go index eac6e8d21..4db64fcae 100644 --- a/internal/core_refresh_storage.go +++ b/internal/core_refresh_storage.go @@ -6,7 +6,6 @@ package internal import ( gomock "github.com/golang/mock/gomock" fosite "github.com/ory-am/fosite" - core "github.com/ory-am/fosite/handler/core" ) // Mock of RefreshTokenGrantStorage interface @@ -30,24 +29,24 @@ func (_m *MockRefreshTokenGrantStorage) EXPECT() *_MockRefreshTokenGrantStorageR return _m.recorder } -func (_m *MockRefreshTokenGrantStorage) CreateAccessTokenSession(_param0 string, _param1 fosite.AccessRequester, _param2 *core.TokenSession) error { - ret := _m.ctrl.Call(_m, "CreateAccessTokenSession", _param0, _param1, _param2) +func (_m *MockRefreshTokenGrantStorage) CreateAccessTokenSession(_param0 string, _param1 fosite.Requester) error { + ret := _m.ctrl.Call(_m, "CreateAccessTokenSession", _param0, _param1) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockRefreshTokenGrantStorageRecorder) CreateAccessTokenSession(arg0, arg1, arg2 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateAccessTokenSession", arg0, arg1, arg2) +func (_mr *_MockRefreshTokenGrantStorageRecorder) CreateAccessTokenSession(arg0, arg1 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateAccessTokenSession", arg0, arg1) } -func (_m *MockRefreshTokenGrantStorage) CreateRefreshTokenSession(_param0 string, _param1 fosite.AccessRequester, _param2 *core.TokenSession) error { - ret := _m.ctrl.Call(_m, "CreateRefreshTokenSession", _param0, _param1, _param2) +func (_m *MockRefreshTokenGrantStorage) CreateRefreshTokenSession(_param0 string, _param1 fosite.Requester) error { + ret := _m.ctrl.Call(_m, "CreateRefreshTokenSession", _param0, _param1) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockRefreshTokenGrantStorageRecorder) CreateRefreshTokenSession(arg0, arg1, arg2 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateRefreshTokenSession", arg0, arg1, arg2) +func (_mr *_MockRefreshTokenGrantStorageRecorder) CreateRefreshTokenSession(arg0, arg1 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateRefreshTokenSession", arg0, arg1) } func (_m *MockRefreshTokenGrantStorage) DeleteAccessTokenSession(_param0 string) error { @@ -70,9 +69,9 @@ func (_mr *_MockRefreshTokenGrantStorageRecorder) DeleteRefreshTokenSession(arg0 return _mr.mock.ctrl.RecordCall(_mr.mock, "DeleteRefreshTokenSession", arg0) } -func (_m *MockRefreshTokenGrantStorage) GetAccessTokenSession(_param0 string, _param1 *core.TokenSession) (fosite.AccessRequester, error) { +func (_m *MockRefreshTokenGrantStorage) GetAccessTokenSession(_param0 string, _param1 interface{}) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetAccessTokenSession", _param0, _param1) - ret0, _ := ret[0].(fosite.AccessRequester) + ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -81,9 +80,9 @@ func (_mr *_MockRefreshTokenGrantStorageRecorder) GetAccessTokenSession(arg0, ar return _mr.mock.ctrl.RecordCall(_mr.mock, "GetAccessTokenSession", arg0, arg1) } -func (_m *MockRefreshTokenGrantStorage) GetRefreshTokenSession(_param0 string, _param1 *core.TokenSession) (fosite.AccessRequester, error) { +func (_m *MockRefreshTokenGrantStorage) GetRefreshTokenSession(_param0 string, _param1 interface{}) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetRefreshTokenSession", _param0, _param1) - ret0, _ := ret[0].(fosite.AccessRequester) + ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/internal/enigma.go b/internal/enigma.go deleted file mode 100644 index 7aa19d5c4..000000000 --- a/internal/enigma.go +++ /dev/null @@ -1,51 +0,0 @@ -// Automatically generated by MockGen. DO NOT EDIT! -// Source: enigma.go - -package internal - -import ( - gomock "github.com/golang/mock/gomock" - . "github.com/ory-am/fosite/enigma" -) - -// Mock of Enigma interface -type MockEnigma struct { - ctrl *gomock.Controller - recorder *_MockEnigmaRecorder -} - -// Recorder for MockEnigma (not exported) -type _MockEnigmaRecorder struct { - mock *MockEnigma -} - -func NewMockEnigma(ctrl *gomock.Controller) *MockEnigma { - mock := &MockEnigma{ctrl: ctrl} - mock.recorder = &_MockEnigmaRecorder{mock} - return mock -} - -func (_m *MockEnigma) EXPECT() *_MockEnigmaRecorder { - return _m.recorder -} - -func (_m *MockEnigma) GenerateChallenge(secret []byte) (*Challenge, error) { - ret := _m.ctrl.Call(_m, "GenerateChallenge", secret) - ret0, _ := ret[0].(*Challenge) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -func (_mr *_MockEnigmaRecorder) GenerateChallenge(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "GenerateChallenge", arg0) -} - -func (_m *MockEnigma) ValidateChallenge(secret []byte, challenge *Challenge) error { - ret := _m.ctrl.Call(_m, "ValidateChallenge", secret, challenge) - ret0, _ := ret[0].(error) - return ret0 -} - -func (_mr *_MockEnigmaRecorder) ValidateChallenge(arg0, arg1 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ValidateChallenge", arg0, arg1) -} diff --git a/internal/refresh_token_strategy.go b/internal/refresh_token_strategy.go new file mode 100644 index 000000000..fc6194e89 --- /dev/null +++ b/internal/refresh_token_strategy.go @@ -0,0 +1,55 @@ +// Automatically generated by MockGen. DO NOT EDIT! +// Source: github.com/ory-am/fosite/handler/core (interfaces: RefreshTokenStrategy) + +package internal + +import ( + gomock "github.com/golang/mock/gomock" + fosite "github.com/ory-am/fosite" + context "golang.org/x/net/context" + http "net/http" +) + +// Mock of RefreshTokenStrategy interface +type MockRefreshTokenStrategy struct { + ctrl *gomock.Controller + recorder *_MockRefreshTokenStrategyRecorder +} + +// Recorder for MockRefreshTokenStrategy (not exported) +type _MockRefreshTokenStrategyRecorder struct { + mock *MockRefreshTokenStrategy +} + +func NewMockRefreshTokenStrategy(ctrl *gomock.Controller) *MockRefreshTokenStrategy { + mock := &MockRefreshTokenStrategy{ctrl: ctrl} + mock.recorder = &_MockRefreshTokenStrategyRecorder{mock} + return mock +} + +func (_m *MockRefreshTokenStrategy) EXPECT() *_MockRefreshTokenStrategyRecorder { + return _m.recorder +} + +func (_m *MockRefreshTokenStrategy) GenerateRefreshToken(_param0 context.Context, _param1 *http.Request, _param2 fosite.Requester) (string, string, error) { + ret := _m.ctrl.Call(_m, "GenerateRefreshToken", _param0, _param1, _param2) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(string) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +func (_mr *_MockRefreshTokenStrategyRecorder) GenerateRefreshToken(arg0, arg1, arg2 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "GenerateRefreshToken", arg0, arg1, arg2) +} + +func (_m *MockRefreshTokenStrategy) ValidateRefreshToken(_param0 string, _param1 context.Context, _param2 *http.Request, _param3 fosite.Requester) (string, error) { + ret := _m.ctrl.Call(_m, "ValidateRefreshToken", _param0, _param1, _param2, _param3) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +func (_mr *_MockRefreshTokenStrategyRecorder) ValidateRefreshToken(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ValidateRefreshToken", arg0, arg1, arg2, arg3) +} diff --git a/internal/request.go b/internal/request.go new file mode 100644 index 000000000..4fa8aadda --- /dev/null +++ b/internal/request.go @@ -0,0 +1,117 @@ +// Automatically generated by MockGen. DO NOT EDIT! +// Source: github.com/ory-am/fosite (interfaces: Requester) + +package internal + +import ( + gomock "github.com/golang/mock/gomock" + fosite "github.com/ory-am/fosite" + client "github.com/ory-am/fosite/client" + url "net/url" + time "time" +) + +// Mock of Requester interface +type MockRequester struct { + ctrl *gomock.Controller + recorder *_MockRequesterRecorder +} + +// Recorder for MockRequester (not exported) +type _MockRequesterRecorder struct { + mock *MockRequester +} + +func NewMockRequester(ctrl *gomock.Controller) *MockRequester { + mock := &MockRequester{ctrl: ctrl} + mock.recorder = &_MockRequesterRecorder{mock} + return mock +} + +func (_m *MockRequester) EXPECT() *_MockRequesterRecorder { + return _m.recorder +} + +func (_m *MockRequester) GetClient() client.Client { + ret := _m.ctrl.Call(_m, "GetClient") + ret0, _ := ret[0].(client.Client) + return ret0 +} + +func (_mr *_MockRequesterRecorder) GetClient() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "GetClient") +} + +func (_m *MockRequester) GetGrantedScopes() fosite.Arguments { + ret := _m.ctrl.Call(_m, "GetGrantedScopes") + ret0, _ := ret[0].(fosite.Arguments) + return ret0 +} + +func (_mr *_MockRequesterRecorder) GetGrantedScopes() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "GetGrantedScopes") +} + +func (_m *MockRequester) GetRequestForm() url.Values { + ret := _m.ctrl.Call(_m, "GetRequestForm") + ret0, _ := ret[0].(url.Values) + return ret0 +} + +func (_mr *_MockRequesterRecorder) GetRequestForm() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "GetRequestForm") +} + +func (_m *MockRequester) GetRequestedAt() time.Time { + ret := _m.ctrl.Call(_m, "GetRequestedAt") + ret0, _ := ret[0].(time.Time) + return ret0 +} + +func (_mr *_MockRequesterRecorder) GetRequestedAt() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "GetRequestedAt") +} + +func (_m *MockRequester) GetScopes() fosite.Arguments { + ret := _m.ctrl.Call(_m, "GetScopes") + ret0, _ := ret[0].(fosite.Arguments) + return ret0 +} + +func (_mr *_MockRequesterRecorder) GetScopes() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "GetScopes") +} + +func (_m *MockRequester) GetSession() interface{} { + ret := _m.ctrl.Call(_m, "GetSession") + ret0, _ := ret[0].(interface{}) + return ret0 +} + +func (_mr *_MockRequesterRecorder) GetSession() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "GetSession") +} + +func (_m *MockRequester) GrantScope(_param0 string) { + _m.ctrl.Call(_m, "GrantScope", _param0) +} + +func (_mr *_MockRequesterRecorder) GrantScope(arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "GrantScope", arg0) +} + +func (_m *MockRequester) SetScopes(_param0 fosite.Arguments) { + _m.ctrl.Call(_m, "SetScopes", _param0) +} + +func (_mr *_MockRequesterRecorder) SetScopes(arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "SetScopes", arg0) +} + +func (_m *MockRequester) SetSession(_param0 interface{}) { + _m.ctrl.Call(_m, "SetSession", _param0) +} + +func (_mr *_MockRequesterRecorder) SetSession(arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "SetSession", arg0) +} diff --git a/internal/token_handler.go b/internal/token_handler.go index f71cbf4f7..34e9c1f14 100644 --- a/internal/token_handler.go +++ b/internal/token_handler.go @@ -31,22 +31,22 @@ func (_m *MockTokenEndpointHandler) EXPECT() *_MockTokenEndpointHandlerRecorder return _m.recorder } -func (_m *MockTokenEndpointHandler) ValidateTokenEndpointRequest(_param0 context.Context, _param1 *http.Request, _param2 fosite.AccessRequester, _param3 interface{}) error { - ret := _m.ctrl.Call(_m, "ValidateTokenEndpointRequest", _param0, _param1, _param2, _param3) +func (_m *MockTokenEndpointHandler) HandleTokenEndpointRequest(_param0 context.Context, _param1 *http.Request, _param2 fosite.AccessRequester, _param3 fosite.AccessResponder) error { + ret := _m.ctrl.Call(_m, "HandleTokenEndpointRequest", _param0, _param1, _param2, _param3) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTokenEndpointHandlerRecorder) ValidateTokenEndpointRequest(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ValidateTokenEndpointRequest", arg0, arg1, arg2, arg3) +func (_mr *_MockTokenEndpointHandlerRecorder) HandleTokenEndpointRequest(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "HandleTokenEndpointRequest", arg0, arg1, arg2, arg3) } -func (_m *MockTokenEndpointHandler) HandleTokenEndpointRequest(_param0 context.Context, _param1 *http.Request, _param2 fosite.AccessRequester, _param3 fosite.AccessResponder, _param4 interface{}) error { - ret := _m.ctrl.Call(_m, "HandleTokenEndpointRequest", _param0, _param1, _param2, _param3, _param4) +func (_m *MockTokenEndpointHandler) ValidateTokenEndpointRequest(_param0 context.Context, _param1 *http.Request, _param2 fosite.AccessRequester) error { + ret := _m.ctrl.Call(_m, "ValidateTokenEndpointRequest", _param0, _param1, _param2) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTokenEndpointHandlerRecorder) HandleTokenEndpointRequest(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "HandleTokenEndpointRequest", arg0, arg1, arg2, arg3, arg4) +func (_mr *_MockTokenEndpointHandlerRecorder) ValidateTokenEndpointRequest(arg0, arg1, arg2 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ValidateTokenEndpointRequest", arg0, arg1, arg2) } diff --git a/internal/token_storage.go b/internal/token_storage.go deleted file mode 100644 index 6bee1b667..000000000 --- a/internal/token_storage.go +++ /dev/null @@ -1,93 +0,0 @@ -// Automatically generated by MockGen. DO NOT EDIT! -// Source: github.com/ory-am/fosite/handler/token (interfaces: TokenStorage) - -package internal - -import ( - gomock "github.com/golang/mock/gomock" - fosite "github.com/ory-am/fosite" - core "github.com/ory-am/fosite/handler/core" -) - -// Mock of TokenStorage interface -type MockTokenStorage struct { - ctrl *gomock.Controller - recorder *_MockTokenStorageRecorder -} - -// Recorder for MockTokenStorage (not exported) -type _MockTokenStorageRecorder struct { - mock *MockTokenStorage -} - -func NewMockTokenStorage(ctrl *gomock.Controller) *MockTokenStorage { - mock := &MockTokenStorage{ctrl: ctrl} - mock.recorder = &_MockTokenStorageRecorder{mock} - return mock -} - -func (_m *MockTokenStorage) EXPECT() *_MockTokenStorageRecorder { - return _m.recorder -} - -func (_m *MockTokenStorage) CreateAccessTokenSession(_param0 string, _param1 fosite.AccessRequester, _param2 *core.TokenSession) error { - ret := _m.ctrl.Call(_m, "CreateAccessTokenSession", _param0, _param1, _param2) - ret0, _ := ret[0].(error) - return ret0 -} - -func (_mr *_MockTokenStorageRecorder) CreateAccessTokenSession(arg0, arg1, arg2 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateAccessTokenSession", arg0, arg1, arg2) -} - -func (_m *MockTokenStorage) CreateRefreshTokenSession(_param0 string, _param1 fosite.AccessRequester, _param2 *core.TokenSession) error { - ret := _m.ctrl.Call(_m, "CreateRefreshTokenSession", _param0, _param1, _param2) - ret0, _ := ret[0].(error) - return ret0 -} - -func (_mr *_MockTokenStorageRecorder) CreateRefreshTokenSession(arg0, arg1, arg2 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateRefreshTokenSession", arg0, arg1, arg2) -} - -func (_m *MockTokenStorage) DeleteAccessTokenSession(_param0 string) error { - ret := _m.ctrl.Call(_m, "DeleteAccessTokenSession", _param0) - ret0, _ := ret[0].(error) - return ret0 -} - -func (_mr *_MockTokenStorageRecorder) DeleteAccessTokenSession(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "DeleteAccessTokenSession", arg0) -} - -func (_m *MockTokenStorage) DeleteRefreshTokenSession(_param0 string) error { - ret := _m.ctrl.Call(_m, "DeleteRefreshTokenSession", _param0) - ret0, _ := ret[0].(error) - return ret0 -} - -func (_mr *_MockTokenStorageRecorder) DeleteRefreshTokenSession(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "DeleteRefreshTokenSession", arg0) -} - -func (_m *MockTokenStorage) GetAccessTokenSession(_param0 string, _param1 *core.TokenSession) (fosite.AccessRequester, error) { - ret := _m.ctrl.Call(_m, "GetAccessTokenSession", _param0, _param1) - ret0, _ := ret[0].(fosite.AccessRequester) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -func (_mr *_MockTokenStorageRecorder) GetAccessTokenSession(arg0, arg1 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "GetAccessTokenSession", arg0, arg1) -} - -func (_m *MockTokenStorage) GetRefreshTokenSession(_param0 string, _param1 *core.TokenSession) (fosite.AccessRequester, error) { - ret := _m.ctrl.Call(_m, "GetRefreshTokenSession", _param0, _param1) - ret0, _ := ret[0].(fosite.AccessRequester) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -func (_mr *_MockTokenStorageRecorder) GetRefreshTokenSession(arg0, arg1 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "GetRefreshTokenSession", arg0, arg1) -} diff --git a/oauth2.go b/oauth2.go index 1efdbd4d9..c09d60620 100644 --- a/oauth2.go +++ b/oauth2.go @@ -1,8 +1,11 @@ package fosite import ( + "github.com/ory-am/fosite/client" "golang.org/x/net/context" "net/http" + "net/url" + "time" ) var DefaultRequiredScopeName = "fosite" @@ -85,7 +88,7 @@ type OAuth2Provider interface { // // The following specs must be considered in any implementation of this method: // https://tools.ietf.org/html/rfc6749#section-5.1 - NewAccessResponse(_ context.Context, req *http.Request, requester AccessRequester, session interface{}) (AccessResponder, error) + NewAccessResponse(ctx context.Context, req *http.Request, requester AccessRequester) (AccessResponder, error) // WriteAccessError writes an access request error response. // @@ -99,3 +102,117 @@ type OAuth2Provider interface { // https://tools.ietf.org/html/rfc6749#section-5.1 WriteAccessResponse(rw http.ResponseWriter, requester AccessRequester, responder AccessResponder) } + +// Requester is an abstract interface for handling requests in Fosite. +type Requester interface { + // GetRequestedAt returns the time the request was created. + GetRequestedAt() (requestedAt time.Time) + + // GetClient returns the requests client. + GetClient() (client client.Client) + + // GetScopes returns the request's scopes. + GetScopes() (scopes Arguments) + + // SetScopes sets the request's scopes. + SetScopes(scopes Arguments) + + // GetGrantScopes returns all granted scopes. + GetGrantedScopes() (grantedScopes Arguments) + + // GrantScope marks a request's scope as granted. + GrantScope(scope string) + + // GetSession returns a pointer to the request's session or nil if none is set. + GetSession() (session interface{}) + + // GetSession sets the request's session pointer. + SetSession(session interface{}) + + // GetRequestForm returns the request's form input. + GetRequestForm() url.Values +} + +// AccessRequester is a token endpoint's request context. +type AccessRequester interface { + // GetGrantType returns the requests grant type. + GetGrantType() (grantType string) + + // SetGrantTypeHandled marks a grant type as handled indicating that the response type is supported. + SetGrantTypeHandled(grantType string) + + // DidHandleGrantType returns if the requested grant type has been handled correctly. + DidHandleGrantType() (didHandle bool) + + Requester +} + +// AuthorizeRequester is an authorize endpoint's request context. +type AuthorizeRequester interface { + // GetResponseTypes returns the requested response types + GetResponseTypes() (responseTypes Arguments) + + // SetResponseTypeHandled marks a response_type (e.g. token or code) as handled indicating that the response type + // is supported. + SetResponseTypeHandled(responseType string) + + // DidHandleAllResponseTypes returns if all requested response types have been handled correctly + DidHandleAllResponseTypes() (didHandle bool) + + // GetRedirectURI returns the requested redirect URI + GetRedirectURI() (redirectURL *url.URL) + + // IsRedirectURIValid returns false if the redirect is not rfc-conform (i.e. missing client, not on white list, + // or malformed) + IsRedirectURIValid() (isValid bool) + + // GetState returns the request's state. + GetState() (state string) + + Requester +} + +// AccessResponder is a token endpoint's response. +type AccessResponder interface { + // SetExtra sets a key value pair for the access response. + SetExtra(key string, value interface{}) + + // GetExtra returns a key's value. + GetExtra(key string) interface{} + + // SetAccessToken sets the responses mandatory access token. + SetAccessToken(token string) + + // SetTokenType set's the responses mandatory token type + SetTokenType(tokenType string) + + // SetAccessToken returns the responses access token. + GetAccessToken() (token string) + + // GetTokenType returns the responses token type. + GetTokenType() (token string) + + // ToMap converts the response to a map. + ToMap() map[string]interface{} +} + +// AuthorizeResponder is an authorization endpoint's response. +type AuthorizeResponder interface { + // GetHeader returns the response's header + GetHeader() (header http.Header) + + // AddHeader adds an header key value pair to the response + AddHeader(key, value string) + + // GetQuery returns the response's query + GetQuery() (query url.Values) + + // AddQuery adds an url query key value pair to the response + AddQuery(key, value string) + + // GetHeader returns the response's url fragments + GetFragment() (fragment url.Values) + + // AddHeader adds a key value pair to the response's url fragment + AddFragment(key, value string) +} diff --git a/oauth2_integration_helper_test.go b/oauth2_integration_helper_test.go index eb7d3cc43..3829570dd 100644 --- a/oauth2_integration_helper_test.go +++ b/oauth2_integration_helper_test.go @@ -65,7 +65,7 @@ func tokenEndpoint(t *testing.T, oauth2 OAuth2Provider) func(rw http.ResponseWri return } - response, err := oauth2.NewAccessResponse(ctx, req, accessRequest, &mySessionData) + response, err := oauth2.NewAccessResponse(ctx, req, accessRequest) if err != nil { t.Logf("Access resonse %s failed because %s\n", accessRequest, err.Error()) oauth2.WriteAccessError(rw, accessRequest, err) diff --git a/oauth2_integration_test.go b/oauth2_integration_test.go index 46377ad3e..7ef80cdcd 100644 --- a/oauth2_integration_test.go +++ b/oauth2_integration_test.go @@ -7,6 +7,7 @@ import ( . "github.com/ory-am/fosite" "github.com/ory-am/fosite/enigma" "github.com/ory-am/fosite/handler/core/explicit" + "github.com/ory-am/fosite/handler/core/strategy" . "github.com/ory-am/fosite/internal" "github.com/parnurzeal/gorequest" "github.com/stretchr/testify/assert" @@ -15,14 +16,13 @@ import ( goauth2 "golang.org/x/oauth2" "net/http" "net/http/httptest" + "net/url" "testing" "time" ) var clientID = "foo" var clientSecret = "barbarbarbarbar" -var clientSecretByte = []byte("barbarbarbarbar") -var state = "random-state" var ts *httptest.Server var mockStore *MockStorage @@ -31,6 +31,12 @@ var mockAuthStore *MockAuthorizeCodeGrantStorage var mockAuthReq *MockAuthorizeRequester var mockHasher *MockHasher +var defaultStrategy = &strategy.HMACSHAStrategy{ + Enigma: &enigma.HMACSHAEnigma{ + GlobalSecret: []byte("super-global-secret"), + }, +} + func TestFosite(t *testing.T) { ctrl := gomock.NewController(t) mockStore = NewMockStorage(ctrl) @@ -41,8 +47,10 @@ func TestFosite(t *testing.T) { defer ctrl.Finish() authExplicitHandler := &explicit.AuthorizeExplicitGrantTypeHandler{ - Enigma: &enigma.HMACSHAEnigma{GlobalSecret: []byte("super-global-secret")}, - Store: mockAuthStore, + AccessTokenStrategy: defaultStrategy, + RefreshTokenStrategy: defaultStrategy, + AuthorizeCodeStrategy: defaultStrategy, + Store: mockAuthStore, } oauth2 := NewFosite(mockStore) @@ -159,7 +167,7 @@ func oauth2TestAuthorizeCodeWorkFlow(oauth2 OAuth2Provider, t *testing.T, refres mockClient.EXPECT().GetHashedSecret().AnyTimes().Return(workingClientHashedSecret) mockClient.EXPECT().GetRedirectURIs().AnyTimes().Return([]string{ts.URL + "/cb"}) - mockAuthStore.EXPECT().CreateAuthorizeCodeSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + mockAuthStore.EXPECT().CreateAuthorizeCodeSession(gomock.Any(), gomock.Any()).Return(nil) mockAuthStore.EXPECT().GetAuthorizeCodeSession(gomock.Any(), gomock.Any()).AnyTimes().Return(nil, errors.New("foo")) }, expectStatusCode: http.StatusOK, @@ -188,16 +196,17 @@ func oauth2TestAuthorizeCodeWorkFlow(oauth2 OAuth2Provider, t *testing.T, refres mockClient.EXPECT().GetHashedSecret().AnyTimes().Return(workingClientHashedSecret) mockClient.EXPECT().GetRedirectURIs().AnyTimes().Return([]string{ts.URL + "/cb"}) - mockAuthStore.EXPECT().CreateAuthorizeCodeSession(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil) - mockAuthStore.EXPECT().GetAuthorizeCodeSession(gomock.Any(), gomock.Any()).AnyTimes().AnyTimes().Return(mockAuthReq, nil) - mockAuthStore.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil) - mockAuthStore.EXPECT().CreateRefreshTokenSession(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil) + mockAuthStore.EXPECT().CreateAuthorizeCodeSession(gomock.Any(), gomock.Any()).AnyTimes().Return(nil) + mockAuthStore.EXPECT().GetAuthorizeCodeSession(gomock.Any(), gomock.Any()).AnyTimes().Return(mockAuthReq, nil) + mockAuthStore.EXPECT().CreateAccessTokenSession(gomock.Any(), gomock.Any()).AnyTimes().Return(nil) + mockAuthStore.EXPECT().CreateRefreshTokenSession(gomock.Any(), gomock.Any()).AnyTimes().Return(nil) mockAuthStore.EXPECT().DeleteAuthorizeCodeSession(gomock.Any()).AnyTimes().Return(nil) mockAuthReq.EXPECT().GetClient().AnyTimes().Return(mockClient) mockAuthReq.EXPECT().GetRequestedAt().AnyTimes().Return(time.Now()) + mockAuthReq.EXPECT().GetRequestForm().AnyTimes().Return(url.Values{}) + mockAuthReq.EXPECT().GetSession().AnyTimes().Return(nil) mockAuthReq.EXPECT().GetScopes().Return([]string{DefaultRequiredScopeName}) - mockAuthReq.EXPECT().GetState() }, expectStatusCode: http.StatusOK, expectPath: "/cb", diff --git a/request.go b/request.go new file mode 100644 index 000000000..9e924cca5 --- /dev/null +++ b/request.go @@ -0,0 +1,53 @@ +package fosite + +import ( + "github.com/ory-am/fosite/client" + "net/url" + "time" +) + +// Request is an implementation of Requester +type Request struct { + RequestedAt time.Time + Client client.Client + Scopes Arguments + GrantedScopes []string + Form url.Values + Session interface{} +} + +func (a *Request) GetRequestForm() url.Values { + return a.Form +} + +func (a *Request) GetRequestedAt() time.Time { + return a.RequestedAt +} + +func (a *Request) GetClient() client.Client { + return a.Client +} + +func (a *Request) GetScopes() Arguments { + return a.Scopes +} + +func (a *Request) SetScopes(s Arguments) { + a.Scopes = s +} + +func (a *Request) GetGrantedScopes() Arguments { + return Arguments(a.GrantedScopes) +} + +func (a *Request) GrantScope(scope string) { + a.GrantedScopes = append(a.GrantedScopes, scope) +} + +func (a *Request) SetSession(session interface{}) { + a.Session = session +} + +func (a *Request) GetSession() interface{} { + return a.Session +} diff --git a/access.go b/request_test.go similarity index 100% rename from access.go rename to request_test.go