From eb9077f6608d776ae50eb2ad4205705bad6ee0eb Mon Sep 17 00:00:00 2001 From: Aeneas Date: Mon, 17 Oct 2016 14:12:25 +0200 Subject: [PATCH] 0.5.0 (#119) * all: resolve regression issues introduced by 0.4.0 - closes #118 * oauth2: introspection handler excess calls - closes #117 * oauth2: inaccurate expires_in time - closes #72 --- HISTORY.md | 14 ++ README.md | 26 +--- access_request.go | 2 +- access_request_handler.go | 2 +- access_request_handler_test.go | 2 +- access_response.go | 2 +- authorize_request_test.go | 4 +- authorize_response_writer.go | 2 +- authorize_response_writer_test.go | 4 +- compose/compose.go | 8 +- compose/compose_oauth2.go | 142 ++++++------------ compose/compose_openid.go | 84 +++++------ handler/oauth2/flow_authorize_code_auth.go | 1 + .../oauth2/flow_authorize_code_auth_test.go | 1 + handler/oauth2/flow_authorize_code_token.go | 8 +- .../oauth2/flow_authorize_code_token_test.go | 13 +- handler/oauth2/flow_authorize_implicit.go | 5 +- .../oauth2/flow_authorize_implicit_test.go | 4 +- handler/oauth2/flow_client_credentials.go | 2 + .../oauth2/flow_client_credentials_test.go | 5 +- handler/oauth2/flow_refresh.go | 3 +- handler/oauth2/flow_refresh_test.go | 3 +- handler/oauth2/flow_resource_owner_test.go | 1 + handler/oauth2/helper.go | 9 +- handler/oauth2/helper_test.go | 11 ++ handler/oauth2/storage.go | 6 +- handler/oauth2/strategy_hmacsha.go | 21 +-- handler/oauth2/strategy_hmacsha_session.go | 37 ----- handler/oauth2/strategy_hmacsha_test.go | 22 +-- handler/oauth2/strategy_jwt_session.go | 40 +++++ handler/openid/flow_hybrid.go | 6 +- handler/openid/flow_hybrid_test.go | 6 +- handler/openid/flow_implicit.go | 6 +- handler/openid/flow_implicit_test.go | 3 +- handler/openid/strategy_jwt.go | 49 +++++- integration/authorize_code_grant_test.go | 7 +- integration/authorize_implicit_grant_test.go | 7 +- integration/client_credentials_grant_test.go | 7 +- integration/helper_endpoints_test.go | 18 +-- integration/helper_setup_test.go | 3 +- integration/introspect_token_test.go | 15 +- integration/oidc_explicit_test.go | 2 +- integration/oidc_implicit_hybrid_test.go | 4 +- integration/refresh_token_grant_test.go | 11 +- ...e_owner_password_credentials_grant_test.go | 5 +- integration/revoke_token_test.go | 15 +- internal/access_request.go | 6 +- internal/access_token_storage.go | 2 +- internal/authorize_code_storage.go | 2 +- internal/authorize_request.go | 6 +- internal/oauth2_client_storage.go | 2 +- internal/oauth2_explicit_storage.go | 2 +- internal/oauth2_owner_storage.go | 4 +- internal/oauth2_refresh_storage.go | 2 +- internal/oauth2_revoke_storage.go | 4 +- internal/oauth2_storage.go | 6 +- internal/request.go | 6 +- introspect.go | 2 +- introspection_request_handler.go | 12 +- introspection_request_handler_test.go | 10 +- introspection_response_writer.go | 20 ++- oauth2.go | 15 +- request.go | 18 +-- request_test.go | 6 +- session.go | 63 ++++++++ storage/memory.go | 6 +- 66 files changed, 453 insertions(+), 389 deletions(-) delete mode 100644 handler/oauth2/strategy_hmacsha_session.go create mode 100644 session.go diff --git a/HISTORY.md b/HISTORY.md index 33dc71be1..8738a8aaf 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,20 @@ This is a list of breaking changes. As long as `1.0.0` is not released, breaking changes will be addressed as minor version bumps (`0.1.0` -> `0.2.0`). +## 0.5.0 + +Breaking changes: + +* `compose.OpenIDConnectExplicit` is now `compose.OpenIDConnectExplicitFactory` +* `compose.OpenIDConnectImplicit` is now `compose.OpenIDConnectImplicitFactory` +* `compose.OpenIDConnectHybrid` is now `compose.OpenIDConnectHybridFactory` +* The token introspection handler is no longer added automatically by `compose.OAuth2*`. Add `compose.OAuth2TokenIntrospectionFactory` +to your composer if you need token introspection. +* Session refactor: + * The HMACSessionContainer was removed and replaced by `fosite.Session` / `fosite.DefaultSession`. All sessions + must now implement this signature. The new session interface allows for better expiration time handling. + * The OpenID `DefaultSession` signature changed as well, it is now implementing the `fosite.Session` interface + ## 0.4.0 Breaking changes: diff --git a/README.md b/README.md index cb61549fe..e7b39317d 100644 --- a/README.md +++ b/README.md @@ -202,20 +202,6 @@ var config = compose.Config { var oauth2Provider = compose.ComposeAllEnabled(config *Config, storage, secret, privateKey) -// The session will be persisted by the store and made available when e.g. validating tokens or handling token endpoint requests. -// The default OAuth2 and OpenID Connect handlers require the session to implement a few methods. Apart from that, the -// session struct can be anything you want it to be. -type session struct { - UserID string - Foobar int - - // this is used in the OAuth2 handlers. You can set per-session expiry times here, for example. - *oauth2Strat.HMACSession - - // this is used by the OpenID connect handlers. You can set custom id token headers and claims. - *oidcStrat.DefaultSession -} - // The authorize endpoint is usually at "https://mydomain.com/oauth2/auth". func authorizeHandlerFunc(rw http.ResponseWriter, req *http.Request) { // This context will be passed to all methods. It doesn't fulfill a real purpose in the standard library but could be used @@ -247,8 +233,12 @@ func authorizeHandlerFunc(rw http.ResponseWriter, req *http.Request) { // Now that the user is authorized, we set up a session. When validating / looking up tokens, we additionally get // the session. You can store anything you want in it. - mySessionData := &session{ - UserID: req.Form.Get("username") + + // The session will be persisted by the store and made available when e.g. validating tokens or handling token endpoint requests. + // The default OAuth2 and OpenID Connect handlers require the session to implement a few methods. Apart from that, the + // session struct can be anything you want it to be. + mySessionData := &fosite.DefaultSession{ + Username: req.Form.Get("username") } // It's also wise to check the requested scopes, e.g.: @@ -273,7 +263,7 @@ func authorizeHandlerFunc(rw http.ResponseWriter, req *http.Request) { // The token endpoint is usually at "https://mydomain.com/oauth2/token" func tokenHandlerFunc(rw http.ResponseWriter, req *http.Request) { ctx := NewContext() - mySessionData := &session{} + mySessionData := new(fosite.DefaultSession) // This will create an access request object and iterate through the registered TokenEndpointHandlers to validate the request. accessRequest, err := oauth2.NewAccessRequest(ctx, req, mySessionData) @@ -302,7 +292,7 @@ func tokenHandlerFunc(rw http.ResponseWriter, req *http.Request) { func someResourceProviderHandlerFunc(rw http.ResponseWriter, req *http.Request) { ctx := NewContext() - mySessionData := &session{} + mySessionData := new(fosite.DefaultSession) requiredScope := "blogposts.create" ar, err := oauth2.IntrospectToken(ctx, fosite.AccessTokenFromRequest(req), mySessionData, requiredScope) diff --git a/access_request.go b/access_request.go index 00c56b8b4..810b7de3e 100644 --- a/access_request.go +++ b/access_request.go @@ -7,7 +7,7 @@ type AccessRequest struct { Request } -func NewAccessRequest(session interface{}) *AccessRequest { +func NewAccessRequest(session Session) *AccessRequest { r := &AccessRequest{ GrantTypes: Arguments{}, HandledGrantType: Arguments{}, diff --git a/access_request_handler.go b/access_request_handler.go index 13910cecb..76c80331b 100644 --- a/access_request_handler.go +++ b/access_request_handler.go @@ -33,7 +33,7 @@ import ( // credentials (or assigned other authentication requirements), the // 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) { +func (f *Fosite) NewAccessRequest(ctx context.Context, r *http.Request, session Session) (AccessRequester, error) { accessRequest := NewAccessRequest(session) if r.Method != "POST" { diff --git a/access_request_handler_test.go b/access_request_handler_test.go index 4bed190f8..05cae91d3 100644 --- a/access_request_handler_test.go +++ b/access_request_handler_test.go @@ -183,7 +183,7 @@ func TestNewAccessRequest(t *testing.T) { c.mock() ctx := NewContext() fosite.TokenEndpointHandlers = c.handlers - ar, err := fosite.NewAccessRequest(ctx, r, &struct{}{}) + ar, err := fosite.NewAccessRequest(ctx, r, new(DefaultSession)) assert.True(t, errors.Cause(err) == c.expectErr, "%d\nwant: %s \ngot: %s", k, c.expectErr, err) if err != nil { t.Logf("Error occured: %v", err) diff --git a/access_response.go b/access_response.go index 0455f73bc..75b15f732 100644 --- a/access_response.go +++ b/access_response.go @@ -23,7 +23,7 @@ func (a *AccessResponse) SetScopes(scopes Arguments) { } func (a *AccessResponse) SetExpiresIn(expiresIn time.Duration) { - a.SetExtra("expires_in", strconv.Itoa(int(expiresIn))) + a.SetExtra("expires_in", strconv.FormatInt(int64(expiresIn/time.Second), 10)) } func (a *AccessResponse) SetExtra(key string, value interface{}) { diff --git a/authorize_request_test.go b/authorize_request_test.go index 9c18dcf91..b6704bcb7 100644 --- a/authorize_request_test.go +++ b/authorize_request_test.go @@ -96,10 +96,10 @@ func TestAuthorizeRequest(t *testing.T) { assert.Equal(t, c.isRedirValid, c.ar.IsRedirectURIValid(), "%d", k) c.ar.GrantScope("foo") - c.ar.SetSession(&struct{}{}) + c.ar.SetSession(&DefaultSession{}) c.ar.SetRequestedScopes([]string{"foo"}) assert.True(t, c.ar.GetGrantedScopes().Has("foo")) assert.True(t, c.ar.GetRequestedScopes().Has("foo")) - assert.Equal(t, &struct{}{}, c.ar.GetSession()) + assert.Equal(t, &DefaultSession{}, c.ar.GetSession()) } } diff --git a/authorize_response_writer.go b/authorize_response_writer.go index b729bc591..b7182057f 100644 --- a/authorize_response_writer.go +++ b/authorize_response_writer.go @@ -8,7 +8,7 @@ import ( "golang.org/x/net/context" ) -func (o *Fosite) NewAuthorizeResponse(ctx context.Context, r *http.Request, ar AuthorizeRequester, session interface{}) (AuthorizeResponder, error) { +func (o *Fosite) NewAuthorizeResponse(ctx context.Context, r *http.Request, ar AuthorizeRequester, session Session) (AuthorizeResponder, error) { var resp = &AuthorizeResponse{ Header: http.Header{}, Query: url.Values{}, diff --git a/authorize_response_writer_test.go b/authorize_response_writer_test.go index 62b101a9c..e5024e419 100644 --- a/authorize_response_writer_test.go +++ b/authorize_response_writer_test.go @@ -25,7 +25,7 @@ func TestNewAuthorizeResponse(t *testing.T) { duo := &Fosite{ AuthorizeEndpointHandlers: AuthorizeEndpointHandlers{handlers[0], handlers[0]}, } - ar.EXPECT().SetSession(gomock.Eq(struct{}{})).AnyTimes() + ar.EXPECT().SetSession(gomock.Eq(new(DefaultSession))).AnyTimes() fooErr := errors.New("foo") for k, c := range []struct { isErr bool @@ -66,7 +66,7 @@ func TestNewAuthorizeResponse(t *testing.T) { }, } { c.mock() - responder, err := oauth2.NewAuthorizeResponse(ctx, &http.Request{}, ar, struct{}{}) + responder, err := oauth2.NewAuthorizeResponse(ctx, &http.Request{}, ar, new(DefaultSession)) 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/compose/compose.go b/compose/compose.go index 0dc650bd6..4ab8b3ee2 100644 --- a/compose/compose.go +++ b/compose/compose.go @@ -75,8 +75,10 @@ func ComposeAllEnabled(config *Config, storage interface{}, secret []byte, key * OAuth2RefreshTokenGrantFactory, OAuth2ResourceOwnerPasswordCredentialsFactory, - OpenIDConnectExplicit, - OpenIDConnectImplicit, - OpenIDConnectHybrid, + OpenIDConnectExplicitFactory, + OpenIDConnectImplicitFactory, + OpenIDConnectHybridFactory, + + OAuth2TokenIntrospectionFactory, ) } diff --git a/compose/compose_oauth2.go b/compose/compose_oauth2.go index 3a45b572a..eed09d598 100644 --- a/compose/compose_oauth2.go +++ b/compose/compose_oauth2.go @@ -8,134 +8,68 @@ import ( // OAuth2AuthorizeExplicitFactory creates an OAuth2 authorize code grant ("authorize explicit flow") handler and registers // an access token, refresh token and authorize code validator. func OAuth2AuthorizeExplicitFactory(config *Config, storage interface{}, strategy interface{}) interface{} { - return &struct { - // register the handler - *oauth2.AuthorizeExplicitGrantHandler - - // also register the validator for access tokens - *oauth2.CoreValidator - }{ - AuthorizeExplicitGrantHandler: &oauth2.AuthorizeExplicitGrantHandler{ - AccessTokenStrategy: strategy.(oauth2.AccessTokenStrategy), - RefreshTokenStrategy: strategy.(oauth2.RefreshTokenStrategy), - AuthorizeCodeStrategy: strategy.(oauth2.AuthorizeCodeStrategy), - AuthorizeCodeGrantStorage: storage.(oauth2.AuthorizeCodeGrantStorage), - AuthCodeLifespan: config.GetAuthorizeCodeLifespan(), - AccessTokenLifespan: config.GetAccessTokenLifespan(), - ScopeStrategy: fosite.HierarchicScopeStrategy, - }, - CoreValidator: &oauth2.CoreValidator{ - CoreStrategy: strategy.(oauth2.CoreStrategy), - CoreStorage: storage.(oauth2.CoreStorage), - ScopeStrategy: fosite.HierarchicScopeStrategy, - }, + return &oauth2.AuthorizeExplicitGrantHandler{ + AccessTokenStrategy: strategy.(oauth2.AccessTokenStrategy), + RefreshTokenStrategy: strategy.(oauth2.RefreshTokenStrategy), + AuthorizeCodeStrategy: strategy.(oauth2.AuthorizeCodeStrategy), + AuthorizeCodeGrantStorage: storage.(oauth2.AuthorizeCodeGrantStorage), + AuthCodeLifespan: config.GetAuthorizeCodeLifespan(), + AccessTokenLifespan: config.GetAccessTokenLifespan(), + ScopeStrategy: fosite.HierarchicScopeStrategy, } } // OAuth2ClientCredentialsGrantFactory creates an OAuth2 client credentials grant handler and registers // an access token, refresh token and authorize code validator. func OAuth2ClientCredentialsGrantFactory(config *Config, storage interface{}, strategy interface{}) interface{} { - return &struct { - // register the handler - *oauth2.ClientCredentialsGrantHandler - - // also register the validator for access tokens - *oauth2.CoreValidator - }{ - ClientCredentialsGrantHandler: &oauth2.ClientCredentialsGrantHandler{ - HandleHelper: &oauth2.HandleHelper{ - AccessTokenStrategy: strategy.(oauth2.AccessTokenStrategy), - AccessTokenStorage: storage.(oauth2.AccessTokenStorage), - AccessTokenLifespan: config.GetAccessTokenLifespan(), - }, - ScopeStrategy: fosite.HierarchicScopeStrategy, - }, - CoreValidator: &oauth2.CoreValidator{ - CoreStrategy: strategy.(oauth2.CoreStrategy), - CoreStorage: storage.(oauth2.CoreStorage), - ScopeStrategy: fosite.HierarchicScopeStrategy, + return &oauth2.ClientCredentialsGrantHandler{ + HandleHelper: &oauth2.HandleHelper{ + AccessTokenStrategy: strategy.(oauth2.AccessTokenStrategy), + AccessTokenStorage: storage.(oauth2.AccessTokenStorage), + AccessTokenLifespan: config.GetAccessTokenLifespan(), }, + ScopeStrategy: fosite.HierarchicScopeStrategy, } } // OAuth2RefreshTokenGrantFactory creates an OAuth2 refresh grant handler and registers // an access token, refresh token and authorize code validator. func OAuth2RefreshTokenGrantFactory(config *Config, storage interface{}, strategy interface{}) interface{} { - return &struct { - // register the handler - *oauth2.RefreshTokenGrantHandler - - // also register the validator for access tokens - *oauth2.CoreValidator - }{ - RefreshTokenGrantHandler: &oauth2.RefreshTokenGrantHandler{ - AccessTokenStrategy: strategy.(oauth2.AccessTokenStrategy), - RefreshTokenStrategy: strategy.(oauth2.RefreshTokenStrategy), - RefreshTokenGrantStorage: storage.(oauth2.RefreshTokenGrantStorage), - AccessTokenLifespan: config.GetAccessTokenLifespan(), - }, - CoreValidator: &oauth2.CoreValidator{ - CoreStrategy: strategy.(oauth2.CoreStrategy), - CoreStorage: storage.(oauth2.CoreStorage), - ScopeStrategy: fosite.HierarchicScopeStrategy, - }, + return &oauth2.RefreshTokenGrantHandler{ + AccessTokenStrategy: strategy.(oauth2.AccessTokenStrategy), + RefreshTokenStrategy: strategy.(oauth2.RefreshTokenStrategy), + RefreshTokenGrantStorage: storage.(oauth2.RefreshTokenGrantStorage), + AccessTokenLifespan: config.GetAccessTokenLifespan(), } } // OAuth2AuthorizeImplicitFactory creates an OAuth2 implicit grant ("authorize implicit flow") handler and registers // an access token, refresh token and authorize code validator. func OAuth2AuthorizeImplicitFactory(config *Config, storage interface{}, strategy interface{}) interface{} { - return &struct { - // register the handler - *oauth2.AuthorizeImplicitGrantTypeHandler - - // also register the validator for access tokens - *oauth2.CoreValidator - }{ - AuthorizeImplicitGrantTypeHandler: &oauth2.AuthorizeImplicitGrantTypeHandler{ - AccessTokenStrategy: strategy.(oauth2.AccessTokenStrategy), - AccessTokenStorage: storage.(oauth2.AccessTokenStorage), - AccessTokenLifespan: config.GetAccessTokenLifespan(), - ScopeStrategy: fosite.HierarchicScopeStrategy, - }, - CoreValidator: &oauth2.CoreValidator{ - CoreStrategy: strategy.(oauth2.CoreStrategy), - CoreStorage: storage.(oauth2.CoreStorage), - ScopeStrategy: fosite.HierarchicScopeStrategy, - }, + return &oauth2.AuthorizeImplicitGrantTypeHandler{ + AccessTokenStrategy: strategy.(oauth2.AccessTokenStrategy), + AccessTokenStorage: storage.(oauth2.AccessTokenStorage), + AccessTokenLifespan: config.GetAccessTokenLifespan(), + ScopeStrategy: fosite.HierarchicScopeStrategy, } } // OAuth2ResourceOwnerPasswordCredentialsFactory creates an OAuth2 resource owner password credentials grant handler and registers // an access token, refresh token and authorize code validator. func OAuth2ResourceOwnerPasswordCredentialsFactory(config *Config, storage interface{}, strategy interface{}) interface{} { - return &struct { - // register the handler - *oauth2.ResourceOwnerPasswordCredentialsGrantHandler - - // also register the validator for access tokens - *oauth2.CoreValidator - }{ - ResourceOwnerPasswordCredentialsGrantHandler: &oauth2.ResourceOwnerPasswordCredentialsGrantHandler{ - ResourceOwnerPasswordCredentialsGrantStorage: storage.(oauth2.ResourceOwnerPasswordCredentialsGrantStorage), - HandleHelper: &oauth2.HandleHelper{ - AccessTokenStrategy: strategy.(oauth2.AccessTokenStrategy), - AccessTokenStorage: storage.(oauth2.AccessTokenStorage), - AccessTokenLifespan: config.GetAccessTokenLifespan(), - }, - RefreshTokenStrategy: strategy.(oauth2.RefreshTokenStrategy), - ScopeStrategy: fosite.HierarchicScopeStrategy, - }, - CoreValidator: &oauth2.CoreValidator{ - CoreStrategy: strategy.(oauth2.CoreStrategy), - CoreStorage: storage.(oauth2.CoreStorage), - ScopeStrategy: fosite.HierarchicScopeStrategy, + return &oauth2.ResourceOwnerPasswordCredentialsGrantHandler{ + ResourceOwnerPasswordCredentialsGrantStorage: storage.(oauth2.ResourceOwnerPasswordCredentialsGrantStorage), + HandleHelper: &oauth2.HandleHelper{ + AccessTokenStrategy: strategy.(oauth2.AccessTokenStrategy), + AccessTokenStorage: storage.(oauth2.AccessTokenStorage), + AccessTokenLifespan: config.GetAccessTokenLifespan(), }, + RefreshTokenStrategy: strategy.(oauth2.RefreshTokenStrategy), + ScopeStrategy: fosite.HierarchicScopeStrategy, } } -// OAuth2TokenRevocationFactory creates an OAuth2 token revocation handler and registers -// an access token, refresh token and authorize code validator. +// OAuth2TokenRevocationFactory creates an OAuth2 token revocation handler. func OAuth2TokenRevocationFactory(config *Config, storage interface{}, strategy interface{}) interface{} { return &oauth2.TokenRevocationHandler{ TokenRevocationStorage: storage.(oauth2.TokenRevocationStorage), @@ -143,3 +77,13 @@ func OAuth2TokenRevocationFactory(config *Config, storage interface{}, strategy RefreshTokenStrategy: strategy.(oauth2.RefreshTokenStrategy), } } + +// OAuth2TokenIntrospectionFactory creates an OAuth2 token introspection handler and registers +// an access token and refresh token validator. +func OAuth2TokenIntrospectionFactory(config *Config, storage interface{}, strategy interface{}) interface{} { + return &oauth2.CoreValidator{ + CoreStrategy: strategy.(oauth2.CoreStrategy), + CoreStorage: storage.(oauth2.CoreStorage), + ScopeStrategy: fosite.HierarchicScopeStrategy, + } +} diff --git a/compose/compose_openid.go b/compose/compose_openid.go index 3816d3960..f231f087b 100644 --- a/compose/compose_openid.go +++ b/compose/compose_openid.go @@ -6,65 +6,53 @@ import ( "github.com/ory-am/fosite/handler/openid" ) -// OpenIDConnectExplicit creates an OpenID Connect explicit ("authorize code flow") grant handler. You must add this handler +// OpenIDConnectExplicitFactory creates an OpenID Connect explicit ("authorize code flow") grant handler. You must add this handler // *after* you have added an OAuth2 authorize code handler! -func OpenIDConnectExplicit(config *Config, storage interface{}, strategy interface{}) interface{} { - return &struct { - *openid.OpenIDConnectExplicitHandler - }{ - OpenIDConnectExplicitHandler: &openid.OpenIDConnectExplicitHandler{ - OpenIDConnectRequestStorage: storage.(openid.OpenIDConnectRequestStorage), - IDTokenHandleHelper: &openid.IDTokenHandleHelper{ - IDTokenStrategy: strategy.(openid.OpenIDConnectTokenStrategy), - }, +func OpenIDConnectExplicitFactory(config *Config, storage interface{}, strategy interface{}) interface{} { + return &openid.OpenIDConnectExplicitHandler{ + OpenIDConnectRequestStorage: storage.(openid.OpenIDConnectRequestStorage), + IDTokenHandleHelper: &openid.IDTokenHandleHelper{ + IDTokenStrategy: strategy.(openid.OpenIDConnectTokenStrategy), }, } } -// OpenIDConnectImplicit creates an OpenID Connect implicit ("implicit flow") grant handler. You must add this handler +// OpenIDConnectImplicitFactory creates an OpenID Connect implicit ("implicit flow") grant handler. You must add this handler // *after* you have added an OAuth2 authorize implicit handler! -func OpenIDConnectImplicit(config *Config, storage interface{}, strategy interface{}) interface{} { - return &struct { - *openid.OpenIDConnectImplicitHandler - }{ - OpenIDConnectImplicitHandler: &openid.OpenIDConnectImplicitHandler{ - AuthorizeImplicitGrantTypeHandler: &oauth2.AuthorizeImplicitGrantTypeHandler{ - AccessTokenStrategy: strategy.(oauth2.AccessTokenStrategy), - AccessTokenStorage: storage.(oauth2.AccessTokenStorage), - AccessTokenLifespan: config.GetAccessTokenLifespan(), - }, - ScopeStrategy: fosite.HierarchicScopeStrategy, - IDTokenHandleHelper: &openid.IDTokenHandleHelper{ - IDTokenStrategy: strategy.(openid.OpenIDConnectTokenStrategy), - }, +func OpenIDConnectImplicitFactory(config *Config, storage interface{}, strategy interface{}) interface{} { + return &openid.OpenIDConnectImplicitHandler{ + AuthorizeImplicitGrantTypeHandler: &oauth2.AuthorizeImplicitGrantTypeHandler{ + AccessTokenStrategy: strategy.(oauth2.AccessTokenStrategy), + AccessTokenStorage: storage.(oauth2.AccessTokenStorage), + AccessTokenLifespan: config.GetAccessTokenLifespan(), + }, + ScopeStrategy: fosite.HierarchicScopeStrategy, + IDTokenHandleHelper: &openid.IDTokenHandleHelper{ + IDTokenStrategy: strategy.(openid.OpenIDConnectTokenStrategy), }, } } -// OpenIDConnectHybrid creates an OpenID Connect hybrid grant handler. You must add this handler +// OpenIDConnectHybridFactory creates an OpenID Connect hybrid grant handler. You must add this handler // *after* you have added an OAuth2 authorize code and implicit authorize handler! -func OpenIDConnectHybrid(config *Config, storage interface{}, strategy interface{}) interface{} { - return &struct { - *openid.OpenIDConnectHybridHandler - }{ - OpenIDConnectHybridHandler: &openid.OpenIDConnectHybridHandler{ - AuthorizeExplicitGrantHandler: &oauth2.AuthorizeExplicitGrantHandler{ - AccessTokenStrategy: strategy.(oauth2.AccessTokenStrategy), - RefreshTokenStrategy: strategy.(oauth2.RefreshTokenStrategy), - AuthorizeCodeStrategy: strategy.(oauth2.AuthorizeCodeStrategy), - AuthorizeCodeGrantStorage: storage.(oauth2.AuthorizeCodeGrantStorage), - AuthCodeLifespan: config.GetAuthorizeCodeLifespan(), - AccessTokenLifespan: config.GetAccessTokenLifespan(), - }, - ScopeStrategy: fosite.HierarchicScopeStrategy, - AuthorizeImplicitGrantTypeHandler: &oauth2.AuthorizeImplicitGrantTypeHandler{ - AccessTokenStrategy: strategy.(oauth2.AccessTokenStrategy), - AccessTokenStorage: storage.(oauth2.AccessTokenStorage), - AccessTokenLifespan: config.GetAccessTokenLifespan(), - }, - IDTokenHandleHelper: &openid.IDTokenHandleHelper{ - IDTokenStrategy: strategy.(openid.OpenIDConnectTokenStrategy), - }, +func OpenIDConnectHybridFactory(config *Config, storage interface{}, strategy interface{}) interface{} { + return &openid.OpenIDConnectHybridHandler{ + AuthorizeExplicitGrantHandler: &oauth2.AuthorizeExplicitGrantHandler{ + AccessTokenStrategy: strategy.(oauth2.AccessTokenStrategy), + RefreshTokenStrategy: strategy.(oauth2.RefreshTokenStrategy), + AuthorizeCodeStrategy: strategy.(oauth2.AuthorizeCodeStrategy), + AuthorizeCodeGrantStorage: storage.(oauth2.AuthorizeCodeGrantStorage), + AuthCodeLifespan: config.GetAuthorizeCodeLifespan(), + AccessTokenLifespan: config.GetAccessTokenLifespan(), + }, + ScopeStrategy: fosite.HierarchicScopeStrategy, + AuthorizeImplicitGrantTypeHandler: &oauth2.AuthorizeImplicitGrantTypeHandler{ + AccessTokenStrategy: strategy.(oauth2.AccessTokenStrategy), + AccessTokenStorage: storage.(oauth2.AccessTokenStorage), + AccessTokenLifespan: config.GetAccessTokenLifespan(), + }, + IDTokenHandleHelper: &openid.IDTokenHandleHelper{ + IDTokenStrategy: strategy.(openid.OpenIDConnectTokenStrategy), }, } } diff --git a/handler/oauth2/flow_authorize_code_auth.go b/handler/oauth2/flow_authorize_code_auth.go index 6618d157f..96b4149a3 100644 --- a/handler/oauth2/flow_authorize_code_auth.go +++ b/handler/oauth2/flow_authorize_code_auth.go @@ -65,6 +65,7 @@ func (c *AuthorizeExplicitGrantHandler) IssueAuthorizeCode(ctx context.Context, return errors.Wrap(fosite.ErrServerError, err.Error()) } + ar.GetSession().SetExpiresAt(fosite.AuthorizeCode, time.Now().Add(c.AuthCodeLifespan)) resp.AddQuery("code", code) resp.AddQuery("state", ar.GetState()) resp.AddQuery("scope", strings.Join(ar.GetGrantedScopes(), " ")) diff --git a/handler/oauth2/flow_authorize_code_auth_test.go b/handler/oauth2/flow_authorize_code_auth_test.go index 172036884..357ece4ab 100644 --- a/handler/oauth2/flow_authorize_code_auth_test.go +++ b/handler/oauth2/flow_authorize_code_auth_test.go @@ -24,6 +24,7 @@ func TestAuthorizeCode_HandleAuthorizeEndpointRequest(t *testing.T) { areq := fosite.NewAuthorizeRequest() httpreq := &http.Request{Form: url.Values{}} + areq.Session = new(fosite.DefaultSession) h := AuthorizeExplicitGrantHandler{ AuthorizeCodeGrantStorage: store, AuthorizeCodeStrategy: chgen, diff --git a/handler/oauth2/flow_authorize_code_token.go b/handler/oauth2/flow_authorize_code_token.go index 73e36ae18..55546b9b0 100644 --- a/handler/oauth2/flow_authorize_code_token.go +++ b/handler/oauth2/flow_authorize_code_token.go @@ -1,12 +1,11 @@ package oauth2 import ( - "net/http" - "time" - "github.com/ory-am/fosite" "github.com/pkg/errors" "golang.org/x/net/context" + "net/http" + "time" ) // HandleTokenEndpointRequest implements @@ -61,6 +60,7 @@ func (c *AuthorizeExplicitGrantHandler) HandleTokenEndpointRequest(ctx context.C // client MUST authenticate with the authorization server as described // in Section 3.2.1. request.SetSession(authorizeRequest.GetSession()) + request.GetSession().SetExpiresAt(fosite.AccessToken, time.Now().Add(c.AccessTokenLifespan)) return nil } @@ -103,7 +103,7 @@ func (c *AuthorizeExplicitGrantHandler) PopulateTokenEndpointResponse(ctx contex responder.SetAccessToken(access) responder.SetTokenType("bearer") - responder.SetExpiresIn(c.AccessTokenLifespan / time.Second) + responder.SetExpiresIn(getExpiresIn(requester, fosite.AccessToken, c.AccessTokenLifespan, time.Now())) responder.SetScopes(requester.GetGrantedScopes()) if refresh != "" { responder.SetExtra("refresh_token", refresh) diff --git a/handler/oauth2/flow_authorize_code_token_test.go b/handler/oauth2/flow_authorize_code_token_test.go index e81e02627..e75c339c5 100644 --- a/handler/oauth2/flow_authorize_code_token_test.go +++ b/handler/oauth2/flow_authorize_code_token_test.go @@ -23,9 +23,10 @@ func TestAuthorizeCode_PopulateTokenEndpointResponse(t *testing.T) { //mockcl := internal.NewMockClient(ctrl) defer ctrl.Finish() - areq := fosite.NewAccessRequest(nil) + areq := fosite.NewAccessRequest(new(fosite.DefaultSession)) httpreq := &http.Request{PostForm: url.Values{}} authreq := fosite.NewAuthorizeRequest() + areq.Session = new(fosite.DefaultSession) h := AuthorizeExplicitGrantHandler{ AuthorizeCodeGrantStorage: store, @@ -55,14 +56,14 @@ func TestAuthorizeCode_PopulateTokenEndpointResponse(t *testing.T) { } httpreq.PostForm.Add("code", "authcode") auch.EXPECT().AuthorizeCodeSignature("authcode").AnyTimes().Return("authsig") - store.EXPECT().GetAuthorizeCodeSession(nil, "authsig", nil).Return(nil, fosite.ErrNotFound) + store.EXPECT().GetAuthorizeCodeSession(nil, "authsig", gomock.Any()).Return(nil, fosite.ErrNotFound) }, expectErr: fosite.ErrServerError, }, { description: "should fail because validation failed", setup: func() { - store.EXPECT().GetAuthorizeCodeSession(nil, "authsig", nil).AnyTimes().Return(authreq, nil) + store.EXPECT().GetAuthorizeCodeSession(nil, "authsig", gomock.Any()).AnyTimes().Return(authreq, nil) auch.EXPECT().ValidateAuthorizeCode(nil, areq, "authcode").Return(errors.New("")) }, expectErr: fosite.ErrInvalidRequest, @@ -122,6 +123,8 @@ func TestAuthorizeCode_HandleTokenEndpointRequest(t *testing.T) { authreq := fosite.NewAuthorizeRequest() areq := fosite.NewAccessRequest(nil) httpreq := &http.Request{PostForm: url.Values{}} + areq.Session = new(fosite.DefaultSession) + authreq.Session = new(fosite.DefaultSession) h := AuthorizeExplicitGrantHandler{ AuthorizeCodeGrantStorage: store, @@ -146,14 +149,14 @@ func TestAuthorizeCode_HandleTokenEndpointRequest(t *testing.T) { areq.GrantTypes = fosite.Arguments{"authorization_code"} // grant_type REQUIRED. Value MUST be set to "authorization_code". httpreq.PostForm = url.Values{"code": {"foo.bar"}} ach.EXPECT().AuthorizeCodeSignature("foo.bar").AnyTimes().Return("bar") - store.EXPECT().GetAuthorizeCodeSession(nil, "bar", nil).Return(nil, fosite.ErrNotFound) + store.EXPECT().GetAuthorizeCodeSession(nil, "bar", gomock.Any()).Return(nil, fosite.ErrNotFound) }, expectErr: fosite.ErrInvalidRequest, }, { description: "should fail because authcode validation failed", setup: func() { - store.EXPECT().GetAuthorizeCodeSession(nil, "bar", nil).AnyTimes().Return(authreq, nil) + store.EXPECT().GetAuthorizeCodeSession(nil, "bar", gomock.Any()).AnyTimes().Return(authreq, nil) ach.EXPECT().ValidateAuthorizeCode(nil, areq, "foo.bar").Return(errors.New("")) }, expectErr: fosite.ErrInvalidRequest, diff --git a/handler/oauth2/flow_authorize_implicit.go b/handler/oauth2/flow_authorize_implicit.go index b3dfd4039..d602c8e08 100644 --- a/handler/oauth2/flow_authorize_implicit.go +++ b/handler/oauth2/flow_authorize_implicit.go @@ -2,7 +2,6 @@ package oauth2 import ( "net/http" - "strconv" "strings" "time" @@ -11,6 +10,7 @@ import ( "github.com/ory-am/fosite" "github.com/pkg/errors" "golang.org/x/net/context" + "strconv" ) // AuthorizeImplicitGrantTypeHandler is a response handler for the Authorize Code grant using the implicit grant type @@ -63,8 +63,9 @@ func (c *AuthorizeImplicitGrantTypeHandler) IssueImplicitAccessToken(ctx context return errors.Wrap(fosite.ErrServerError, err.Error()) } + ar.GetSession().SetExpiresAt(fosite.AccessToken, time.Now().Add(c.AccessTokenLifespan)) resp.AddFragment("access_token", token) - resp.AddFragment("expires_in", strconv.Itoa(int(c.AccessTokenLifespan/time.Second))) + resp.AddFragment("expires_in", strconv.FormatInt(int64(getExpiresIn(ar, fosite.AccessToken, c.AccessTokenLifespan, time.Now())/time.Second), 10)) resp.AddFragment("token_type", "bearer") resp.AddFragment("state", ar.GetState()) resp.AddFragment("scope", strings.Join(ar.GetGrantedScopes(), " ")) diff --git a/handler/oauth2/flow_authorize_implicit_test.go b/handler/oauth2/flow_authorize_implicit_test.go index 01db058cd..5b82a9287 100644 --- a/handler/oauth2/flow_authorize_implicit_test.go +++ b/handler/oauth2/flow_authorize_implicit_test.go @@ -3,7 +3,6 @@ package oauth2 import ( "net/http" "net/url" - "strconv" "testing" "time" @@ -23,6 +22,7 @@ func TestAuthorizeImplicit_EndpointHandler(t *testing.T) { areq := fosite.NewAuthorizeRequest() httpreq := &http.Request{Form: url.Values{}} + areq.Session = new(fosite.DefaultSession) h := AuthorizeImplicitGrantTypeHandler{ AccessTokenStorage: store, @@ -70,7 +70,7 @@ func TestAuthorizeImplicit_EndpointHandler(t *testing.T) { store.EXPECT().CreateAccessTokenSession(nil, "ats", areq).AnyTimes().Return(nil) aresp.EXPECT().AddFragment("access_token", "access.ats") - aresp.EXPECT().AddFragment("expires_in", strconv.Itoa(int(h.AccessTokenLifespan/time.Second))) + aresp.EXPECT().AddFragment("expires_in", gomock.Any()) aresp.EXPECT().AddFragment("token_type", "bearer") aresp.EXPECT().AddFragment("state", "state") aresp.EXPECT().AddFragment("scope", "scope") diff --git a/handler/oauth2/flow_client_credentials.go b/handler/oauth2/flow_client_credentials.go index 32821791c..ba2e08fa2 100644 --- a/handler/oauth2/flow_client_credentials.go +++ b/handler/oauth2/flow_client_credentials.go @@ -8,6 +8,7 @@ import ( "github.com/ory-am/fosite" "github.com/pkg/errors" "golang.org/x/net/context" + "time" ) type ClientCredentialsGrantHandler struct { @@ -38,6 +39,7 @@ func (c *ClientCredentialsGrantHandler) HandleTokenEndpointRequest(_ context.Con } // if the client is not public, he has already been authenticated by the access request handler. + request.GetSession().SetExpiresAt(fosite.AccessToken, time.Now().Add(c.AccessTokenLifespan)) return nil } diff --git a/handler/oauth2/flow_client_credentials_test.go b/handler/oauth2/flow_client_credentials_test.go index 2fc63189f..ffcafd47a 100644 --- a/handler/oauth2/flow_client_credentials_test.go +++ b/handler/oauth2/flow_client_credentials_test.go @@ -43,6 +43,7 @@ func TestClientCredentials_HandleTokenEndpointRequest(t *testing.T) { { description: "should pass", mock: func() { + areq.EXPECT().GetSession().Return(new(fosite.DefaultSession)) areq.EXPECT().GetGrantTypes().Return(fosite.Arguments{"client_credentials"}) areq.EXPECT().GetRequestedScopes().Return([]string{"foo", "bar", "baz.bar"}) areq.EXPECT().GetClient().Return(&fosite.DefaultClient{ @@ -63,7 +64,7 @@ func TestClientCredentials_PopulateTokenEndpointResponse(t *testing.T) { ctrl := gomock.NewController(t) store := internal.NewMockClientCredentialsGrantStorage(ctrl) chgen := internal.NewMockAccessTokenStrategy(ctrl) - areq := fosite.NewAccessRequest(nil) + areq := fosite.NewAccessRequest(new(fosite.DefaultSession)) aresp := fosite.NewAccessResponse() defer ctrl.Finish() @@ -100,7 +101,7 @@ func TestClientCredentials_PopulateTokenEndpointResponse(t *testing.T) { description: "should pass", mock: func() { areq.GrantTypes = fosite.Arguments{"client_credentials"} - + areq.Session = &fosite.DefaultSession{} areq.Client = &fosite.DefaultClient{GrantTypes: fosite.Arguments{"client_credentials"}} chgen.EXPECT().GenerateAccessToken(nil, areq).Return("tokenfoo.bar", "bar", nil) store.EXPECT().CreateAccessTokenSession(nil, "bar", areq).Return(nil) diff --git a/handler/oauth2/flow_refresh.go b/handler/oauth2/flow_refresh.go index 08b0ef836..d14376319 100644 --- a/handler/oauth2/flow_refresh.go +++ b/handler/oauth2/flow_refresh.go @@ -63,6 +63,7 @@ func (c *RefreshTokenGrantHandler) HandleTokenEndpointRequest(ctx context.Contex request.GrantScope(scope) } + request.GetSession().SetExpiresAt(fosite.AccessToken, time.Now().Add(c.AccessTokenLifespan)) return nil } @@ -89,7 +90,7 @@ func (c *RefreshTokenGrantHandler) PopulateTokenEndpointResponse(ctx context.Con responder.SetAccessToken(accessToken) responder.SetTokenType("bearer") - responder.SetExpiresIn(c.AccessTokenLifespan / time.Second) + responder.SetExpiresIn(getExpiresIn(requester, fosite.AccessToken, c.AccessTokenLifespan, time.Now())) responder.SetScopes(requester.GetGrantedScopes()) responder.SetExtra("refresh_token", refreshToken) return nil diff --git a/handler/oauth2/flow_refresh_test.go b/handler/oauth2/flow_refresh_test.go index d9276133d..5378f2722 100644 --- a/handler/oauth2/flow_refresh_test.go +++ b/handler/oauth2/flow_refresh_test.go @@ -20,7 +20,7 @@ func TestRefreshFlow_HandleTokenEndpointRequest(t *testing.T) { defer ctrl.Finish() areq := fosite.NewAccessRequest(nil) - sess := struct{ Subject string }{Subject: "othersub"} + sess := &fosite.DefaultSession{Subject: "othersub"} httpreq := &http.Request{PostForm: url.Values{}} h := RefreshTokenGrantHandler{ @@ -166,6 +166,7 @@ func TestRefreshFlow_PopulateTokenEndpointResponse(t *testing.T) { { description: "should pass", setup: func() { + areq.Session = &fosite.DefaultSession{} store.EXPECT().PersistRefreshTokenGrantSession(nil, "reftokensig", "atsig", "resig", areq).AnyTimes().Return(nil) aresp.EXPECT().SetAccessToken("access.atsig") diff --git a/handler/oauth2/flow_resource_owner_test.go b/handler/oauth2/flow_resource_owner_test.go index 350b65c21..083f041ac 100644 --- a/handler/oauth2/flow_resource_owner_test.go +++ b/handler/oauth2/flow_resource_owner_test.go @@ -111,6 +111,7 @@ func TestResourceOwnerFlow_PopulateTokenEndpointResponse(t *testing.T) { { description: "should pass", setup: func() { + areq.Session = &fosite.DefaultSession{} areq.GrantTypes = fosite.Arguments{"password"} chgen.EXPECT().GenerateAccessToken(nil, areq).Return(mockAT, "bar", nil) store.EXPECT().CreateAccessTokenSession(nil, "bar", areq).Return(nil) diff --git a/handler/oauth2/helper.go b/handler/oauth2/helper.go index 9c126e1f7..e94e23fb1 100644 --- a/handler/oauth2/helper.go +++ b/handler/oauth2/helper.go @@ -24,7 +24,14 @@ func (h *HandleHelper) IssueAccessToken(ctx context.Context, req *http.Request, responder.SetAccessToken(token) responder.SetTokenType("bearer") - responder.SetExpiresIn(h.AccessTokenLifespan / time.Second) + responder.SetExpiresIn(getExpiresIn(requester, fosite.AccessToken, h.AccessTokenLifespan, time.Now())) responder.SetScopes(requester.GetGrantedScopes()) return nil } + +func getExpiresIn(r fosite.Requester, key fosite.TokenType, defaultLifespan time.Duration, now time.Time) time.Duration { + if r.GetSession().GetExpiresAt(key).IsZero() { + return defaultLifespan + } + return time.Duration(r.GetSession().GetExpiresAt(key).UnixNano() - now.UnixNano()) +} diff --git a/handler/oauth2/helper_test.go b/handler/oauth2/helper_test.go index 20dcad215..67f5945b9 100644 --- a/handler/oauth2/helper_test.go +++ b/handler/oauth2/helper_test.go @@ -13,6 +13,16 @@ import ( "github.com/stretchr/testify/require" ) +func TestGetExpiresIn(t *testing.T) { + now := time.Now() + r := fosite.NewAccessRequest(&fosite.DefaultSession{ + ExpiresAt: map[fosite.TokenType]time.Time{ + fosite.AccessToken: now.Add(time.Hour), + }, + }) + assert.Equal(t, time.Hour, getExpiresIn(r, fosite.AccessToken, time.Millisecond, now)) +} + func TestIssueAccessToken(t *testing.T) { ctrl := gomock.NewController(t) areq := &fosite.AccessRequest{} @@ -28,6 +38,7 @@ func TestIssueAccessToken(t *testing.T) { AccessTokenLifespan: time.Hour, } + areq.Session = &fosite.DefaultSession{} for k, c := range []struct { mock func() err error diff --git a/handler/oauth2/storage.go b/handler/oauth2/storage.go index 49e2c677b..240272531 100644 --- a/handler/oauth2/storage.go +++ b/handler/oauth2/storage.go @@ -14,7 +14,7 @@ type CoreStorage interface { type AuthorizeCodeStorage interface { CreateAuthorizeCodeSession(ctx context.Context, code string, request fosite.Requester) (err error) - GetAuthorizeCodeSession(ctx context.Context, code string, session interface{}) (request fosite.Requester, err error) + GetAuthorizeCodeSession(ctx context.Context, code string, session fosite.Session) (request fosite.Requester, err error) DeleteAuthorizeCodeSession(ctx context.Context, code string) (err error) } @@ -22,7 +22,7 @@ type AuthorizeCodeStorage interface { type AccessTokenStorage interface { CreateAccessTokenSession(ctx context.Context, signature string, request fosite.Requester) (err error) - GetAccessTokenSession(ctx context.Context, signature string, session interface{}) (request fosite.Requester, err error) + GetAccessTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error) DeleteAccessTokenSession(ctx context.Context, signature string) (err error) } @@ -30,7 +30,7 @@ type AccessTokenStorage interface { type RefreshTokenStorage interface { CreateRefreshTokenSession(ctx context.Context, signature string, request fosite.Requester) (err error) - GetRefreshTokenSession(ctx context.Context, signature string, session interface{}) (request fosite.Requester, err error) + GetRefreshTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error) DeleteRefreshTokenSession(ctx context.Context, signature string) (err error) } diff --git a/handler/oauth2/strategy_hmacsha.go b/handler/oauth2/strategy_hmacsha.go index 3a3c0e0c9..c1f7ab10b 100644 --- a/handler/oauth2/strategy_hmacsha.go +++ b/handler/oauth2/strategy_hmacsha.go @@ -1,7 +1,6 @@ package oauth2 import ( - "reflect" "time" "fmt" @@ -32,10 +31,12 @@ func (h HMACSHAStrategy) GenerateAccessToken(_ context.Context, _ fosite.Request } func (h HMACSHAStrategy) ValidateAccessToken(_ context.Context, r fosite.Requester, token string) (err error) { - if session, ok := r.GetSession().(HMACSessionContainer); !ok { - return errors.Wrap(fosite.ErrMisconfiguration, fmt.Sprintf("Session must be of type HMACSessionContainer, got: %s", reflect.TypeOf(r.GetSession()))) - } else if session.AccessTokenExpiresAt(r.GetRequestedAt().Add(h.AccessTokenLifespan)).Before(time.Now()) { - return errors.Wrap(fosite.ErrTokenExpired, fmt.Sprintf("Access token expired at %s", session.AccessTokenExpiresAt(r.GetRequestedAt().Add(h.AccessTokenLifespan)))) + var exp = r.GetSession().GetExpiresAt(fosite.AccessToken) + if exp.IsZero() && r.GetRequestedAt().Add(h.AccessTokenLifespan).Before(time.Now()) { + return errors.Wrap(fosite.ErrTokenExpired, fmt.Sprintf("Access token expired at %s", r.GetRequestedAt().Add(h.AccessTokenLifespan))) + } + if !exp.IsZero() && exp.Before(time.Now()) { + return errors.Wrap(fosite.ErrTokenExpired, fmt.Sprintf("Access token expired at %s", exp)) } return h.Enigma.Validate(token) } @@ -53,10 +54,12 @@ func (h HMACSHAStrategy) GenerateAuthorizeCode(_ context.Context, _ fosite.Reque } func (h HMACSHAStrategy) ValidateAuthorizeCode(_ context.Context, r fosite.Requester, token string) (err error) { - if session, ok := r.GetSession().(HMACSessionContainer); !ok { - return errors.Wrap(fosite.ErrMisconfiguration, fmt.Sprintf("Session must be of type HMACSessionContainer, got: %s", reflect.TypeOf(r.GetSession()))) - } else if session.AuthorizeCodeExpiresAt(r.GetRequestedAt().Add(h.AuthorizeCodeLifespan)).Before(time.Now()) { - return errors.Wrap(fosite.ErrTokenExpired, fmt.Sprintf("Authorize code expired at %s", session.AuthorizeCodeExpiresAt(r.GetRequestedAt().Add(h.AccessTokenLifespan)))) + var exp = r.GetSession().GetExpiresAt(fosite.AuthorizeCode) + if exp.IsZero() && r.GetRequestedAt().Add(h.AuthorizeCodeLifespan).Before(time.Now()) { + return errors.Wrap(fosite.ErrTokenExpired, fmt.Sprintf("Authorize code expired at %s", r.GetRequestedAt().Add(h.AuthorizeCodeLifespan))) + } + if !exp.IsZero() && exp.Before(time.Now()) { + return errors.Wrap(fosite.ErrTokenExpired, fmt.Sprintf("Authorize code expired at %s", exp)) } return h.Enigma.Validate(token) diff --git a/handler/oauth2/strategy_hmacsha_session.go b/handler/oauth2/strategy_hmacsha_session.go deleted file mode 100644 index ded6ba219..000000000 --- a/handler/oauth2/strategy_hmacsha_session.go +++ /dev/null @@ -1,37 +0,0 @@ -package oauth2 - -import ( - "time" -) - -type HMACSessionContainer interface { - // AccessTokenExpiresAt returns the access token's expiry. - AccessTokenExpiresAt(fallback time.Time) time.Time - - // AccessTokenExpiresAt returns the authorize code's expiry. - AuthorizeCodeExpiresAt(fallback time.Time) time.Time -} - -// HMACSession Container for the HMAC session. -type HMACSession struct { - AccessTokenExpiry time.Time - AuthorizeCodeExpiry time.Time -} - -func (s *HMACSession) AccessTokenExpiresAt(fallback time.Time) time.Time { - if s == nil { - return fallback - } else if s.AccessTokenExpiry.IsZero() { - return fallback - } - return s.AccessTokenExpiry -} - -func (s *HMACSession) AuthorizeCodeExpiresAt(fallback time.Time) time.Time { - if s == nil { - return fallback - } else if s.AuthorizeCodeExpiry.IsZero() { - return fallback - } - return s.AuthorizeCodeExpiry -} diff --git a/handler/oauth2/strategy_hmacsha_test.go b/handler/oauth2/strategy_hmacsha_test.go index 64080a75a..7bcd1fffe 100644 --- a/handler/oauth2/strategy_hmacsha_test.go +++ b/handler/oauth2/strategy_hmacsha_test.go @@ -18,9 +18,11 @@ var hmacExpiredCase = fosite.Request{ Client: &fosite.DefaultClient{ Secret: []byte("foobarfoobarfoobarfoobar"), }, - Session: &HMACSession{ - AccessTokenExpiry: time.Now().Add(-time.Hour), - AuthorizeCodeExpiry: time.Now().Add(-time.Hour), + Session: &fosite.DefaultSession{ + ExpiresAt: map[fosite.TokenType]time.Time{ + fosite.AccessToken: time.Now().Add(-time.Hour), + fosite.AuthorizeCode: time.Now().Add(-time.Hour), + }, }, } @@ -28,9 +30,11 @@ var hmacValidCase = fosite.Request{ Client: &fosite.DefaultClient{ Secret: []byte("foobarfoobarfoobarfoobar"), }, - Session: &HMACSession{ - AccessTokenExpiry: time.Now().Add(time.Hour), - AuthorizeCodeExpiry: time.Now().Add(time.Hour), + Session: &fosite.DefaultSession{ + ExpiresAt: map[fosite.TokenType]time.Time{ + fosite.AccessToken: time.Now().Add(time.Hour), + fosite.AuthorizeCode: time.Now().Add(time.Hour), + }, }, } @@ -75,7 +79,7 @@ func TestHMACRefreshToken(t *testing.T) { } func TestHMACAuthorizeCode(t *testing.T) { - for _, c := range []struct { + for k, c := range []struct { r fosite.Request pass bool }{ @@ -94,11 +98,11 @@ func TestHMACAuthorizeCode(t *testing.T) { err = s.ValidateAuthorizeCode(nil, &c.r, token) if c.pass { - assert.Nil(t, err, "%s", err) + assert.Nil(t, err, "%d: %s", k, err) validate := s.Enigma.Signature(token) assert.Equal(t, signature, validate) } else { - assert.NotNil(t, err, "%s", err) + assert.NotNil(t, err, "%d: %s", k, err) } } } diff --git a/handler/oauth2/strategy_jwt_session.go b/handler/oauth2/strategy_jwt_session.go index c4c35aec8..ce485c268 100644 --- a/handler/oauth2/strategy_jwt_session.go +++ b/handler/oauth2/strategy_jwt_session.go @@ -1,7 +1,9 @@ package oauth2 import ( + "github.com/ory-am/fosite" "github.com/ory-am/fosite/token/jwt" + "time" ) type JWTSessionContainer interface { @@ -10,12 +12,17 @@ type JWTSessionContainer interface { // GetJWTHeader returns the header. GetJWTHeader() *jwt.Headers + + fosite.Session } // JWTSession Container for the JWT session. type JWTSession struct { JWTClaims *jwt.JWTClaims JWTHeader *jwt.Headers + ExpiresAt map[fosite.TokenType]time.Time + Username string + Subject string } func (j *JWTSession) GetJWTClaims() *jwt.JWTClaims { @@ -31,3 +38,36 @@ func (j *JWTSession) GetJWTHeader() *jwt.Headers { } return j.JWTHeader } + +func (s *JWTSession) SetExpiresAt(key fosite.TokenType, exp time.Time) { + if s.ExpiresAt == nil { + s.ExpiresAt = make(map[fosite.TokenType]time.Time) + } + s.ExpiresAt[key] = exp +} + +func (s *JWTSession) GetExpiresAt(key fosite.TokenType) time.Time { + if s.ExpiresAt == nil { + s.ExpiresAt = make(map[fosite.TokenType]time.Time) + } + + if _, ok := s.ExpiresAt[key]; !ok { + return time.Time{} + } + return s.ExpiresAt[key] +} + +func (s *JWTSession) GetUsername() string { + if s == nil { + return "" + } + return s.Username +} + +func (s *JWTSession) GetSubject() string { + if s == nil { + return "" + } + + return s.Subject +} diff --git a/handler/openid/flow_hybrid.go b/handler/openid/flow_hybrid.go index 742333ca6..2a880d6de 100644 --- a/handler/openid/flow_hybrid.go +++ b/handler/openid/flow_hybrid.go @@ -19,7 +19,7 @@ type OpenIDConnectHybridHandler struct { IDTokenHandleHelper *IDTokenHandleHelper ScopeStrategy fosite.ScopeStrategy - Enigma *jwt.RS256JWTStrategy + Enigma *jwt.RS256JWTStrategy } func (c *OpenIDConnectHybridHandler) HandleAuthorizeEndpointRequest(ctx context.Context, req *http.Request, ar fosite.AuthorizeRequester, resp fosite.AuthorizeResponder) error { @@ -69,7 +69,7 @@ func (c *OpenIDConnectHybridHandler) HandleAuthorizeEndpointRequest(ctx context. if err != nil { return err } - claims.CodeHash = []byte(base64.URLEncoding.EncodeToString([]byte(hash[:c.Enigma.GetSigningMethodLength()/2]))) + claims.CodeHash = []byte(base64.URLEncoding.EncodeToString([]byte(hash[:c.Enigma.GetSigningMethodLength() / 2]))) } if ar.GetResponseTypes().Has("token") { @@ -84,7 +84,7 @@ func (c *OpenIDConnectHybridHandler) HandleAuthorizeEndpointRequest(ctx context. if err != nil { return err } - claims.AccessTokenHash = []byte(base64.URLEncoding.EncodeToString([]byte(hash[:c.Enigma.GetSigningMethodLength()/2]))) + claims.AccessTokenHash = []byte(base64.URLEncoding.EncodeToString([]byte(hash[:c.Enigma.GetSigningMethodLength() / 2]))) } if !ar.GetGrantedScopes().Has("openid") { diff --git a/handler/openid/flow_hybrid_test.go b/handler/openid/flow_hybrid_test.go index fbcd72a64..db667e6f3 100644 --- a/handler/openid/flow_hybrid_test.go +++ b/handler/openid/flow_hybrid_test.go @@ -32,7 +32,7 @@ var hmacStrategy = &oauth2.HMACSHAStrategy{ type defaultSession struct { Claims *jwt.IDTokenClaims Headers *jwt.Headers - *oauth2.HMACSession + *fosite.DefaultSession } func (s *defaultSession) IDTokenHeaders() *jwt.Headers { @@ -116,8 +116,8 @@ func TestHybrid_HandleAuthorizeEndpointRequest(t *testing.T) { Claims: &jwt.IDTokenClaims{ Subject: "peter", }, - Headers: &jwt.Headers{}, - HMACSession: &oauth2.HMACSession{}, + Headers: &jwt.Headers{}, + DefaultSession: new(fosite.DefaultSession), } }, expectErr: fosite.ErrInvalidGrant, diff --git a/handler/openid/flow_implicit.go b/handler/openid/flow_implicit.go index 02441460e..f618b2861 100644 --- a/handler/openid/flow_implicit.go +++ b/handler/openid/flow_implicit.go @@ -16,9 +16,9 @@ import ( type OpenIDConnectImplicitHandler struct { AuthorizeImplicitGrantTypeHandler *oauth2.AuthorizeImplicitGrantTypeHandler *IDTokenHandleHelper - ScopeStrategy fosite.ScopeStrategy + ScopeStrategy fosite.ScopeStrategy - RS256JWTStrategy *jwt.RS256JWTStrategy + RS256JWTStrategy *jwt.RS256JWTStrategy } func (c *OpenIDConnectImplicitHandler) HandleAuthorizeEndpointRequest(ctx context.Context, req *http.Request, ar fosite.AuthorizeRequester, resp fosite.AuthorizeResponder) error { @@ -63,7 +63,7 @@ func (c *OpenIDConnectImplicitHandler) HandleAuthorizeEndpointRequest(ctx contex return err } - claims.AccessTokenHash = []byte(base64.URLEncoding.EncodeToString([]byte(hash[:c.RS256JWTStrategy.GetSigningMethodLength()/2]))) + claims.AccessTokenHash = []byte(base64.URLEncoding.EncodeToString([]byte(hash[:c.RS256JWTStrategy.GetSigningMethodLength() / 2]))) } else { resp.AddFragment("state", ar.GetState()) } diff --git a/handler/openid/flow_implicit_test.go b/handler/openid/flow_implicit_test.go index a26e8285d..e3e5406bf 100644 --- a/handler/openid/flow_implicit_test.go +++ b/handler/openid/flow_implicit_test.go @@ -22,6 +22,7 @@ func TestImplicit_HandleAuthorizeEndpointRequest(t *testing.T) { aresp := fosite.NewAuthorizeResponse() areq := fosite.NewAuthorizeRequest() httpreq := &http.Request{Form: url.Values{}} + areq.Session = new(fosite.DefaultSession) h := OpenIDConnectImplicitHandler{ AuthorizeImplicitGrantTypeHandler: &oauth2.AuthorizeImplicitGrantTypeHandler{ @@ -110,7 +111,7 @@ func TestImplicit_HandleAuthorizeEndpointRequest(t *testing.T) { Claims: &jwt.IDTokenClaims{ Subject: "peter", }, - Headers: &jwt.Headers{}, + Headers: &jwt.Headers{}, } areq.Form.Add("nonce", "some-random-foo-nonce-wow") }, diff --git a/handler/openid/strategy_jwt.go b/handler/openid/strategy_jwt.go index b69416c37..a2ef76564 100644 --- a/handler/openid/strategy_jwt.go +++ b/handler/openid/strategy_jwt.go @@ -16,12 +16,57 @@ const defaultExpiryTime = time.Hour type Session interface { IDTokenClaims() *jwt.IDTokenClaims IDTokenHeaders() *jwt.Headers + + fosite.Session } // IDTokenSession is a session container for the id token type DefaultSession struct { - Claims *jwt.IDTokenClaims - Headers *jwt.Headers + Claims *jwt.IDTokenClaims + Headers *jwt.Headers + ExpiresAt map[fosite.TokenType]time.Time + Username string + Subject string +} + +func NewDefaultSession() *DefaultSession { + return &DefaultSession{ + Claims: &jwt.IDTokenClaims{}, + Headers: &jwt.Headers{}, + } +} + +func (s *DefaultSession) SetExpiresAt(key fosite.TokenType, exp time.Time) { + if s.ExpiresAt == nil { + s.ExpiresAt = make(map[fosite.TokenType]time.Time) + } + s.ExpiresAt[key] = exp +} + +func (s *DefaultSession) GetExpiresAt(key fosite.TokenType) time.Time { + if s.ExpiresAt == nil { + s.ExpiresAt = make(map[fosite.TokenType]time.Time) + } + + if _, ok := s.ExpiresAt[key]; !ok { + return time.Time{} + } + return s.ExpiresAt[key] +} + +func (s *DefaultSession) GetUsername() string { + if s == nil { + return "" + } + return s.Username +} + +func (s *DefaultSession) GetSubject() string { + if s == nil { + return "" + } + + return s.Subject } func (s *DefaultSession) IDTokenHeaders() *jwt.Headers { diff --git a/integration/authorize_code_grant_test.go b/integration/authorize_code_grant_test.go index 7efca21d4..1dab0ff67 100644 --- a/integration/authorize_code_grant_test.go +++ b/integration/authorize_code_grant_test.go @@ -5,6 +5,7 @@ import ( "net/http" + "github.com/ory-am/fosite" "github.com/ory-am/fosite/compose" "github.com/ory-am/fosite/handler/oauth2" "github.com/stretchr/testify/assert" @@ -21,10 +22,8 @@ func TestAuthorizeCodeFlow(t *testing.T) { } func runAuthorizeCodeGrantTest(t *testing.T, strategy interface{}) { - f := compose.Compose(new(compose.Config), fositeStore, strategy, compose.OAuth2AuthorizeExplicitFactory) - ts := mockServer(t, f, &mySessionData{ - HMACSession: new(oauth2.HMACSession), - }) + f := compose.Compose(new(compose.Config), fositeStore, strategy, compose.OAuth2AuthorizeExplicitFactory, compose.OAuth2TokenIntrospectionFactory) + ts := mockServer(t, f, &fosite.DefaultSession{}) defer ts.Close() oauthClient := newOAuth2Client(ts) diff --git a/integration/authorize_implicit_grant_test.go b/integration/authorize_implicit_grant_test.go index f5ff7bc48..a189a38d5 100644 --- a/integration/authorize_implicit_grant_test.go +++ b/integration/authorize_implicit_grant_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/ory-am/fosite" "github.com/ory-am/fosite/compose" "github.com/ory-am/fosite/handler/oauth2" "github.com/pkg/errors" @@ -25,10 +26,8 @@ func TestAuthorizeImplicitFlow(t *testing.T) { } func runTestAuthorizeImplicitGrant(t *testing.T, strategy interface{}) { - f := compose.Compose(new(compose.Config), fositeStore, strategy, compose.OAuth2AuthorizeImplicitFactory) - ts := mockServer(t, f, &mySessionData{ - HMACSession: new(oauth2.HMACSession), - }) + f := compose.Compose(new(compose.Config), fositeStore, strategy, compose.OAuth2AuthorizeImplicitFactory, compose.OAuth2TokenIntrospectionFactory) + ts := mockServer(t, f, &fosite.DefaultSession{}) defer ts.Close() oauthClient := newOAuth2Client(ts) diff --git a/integration/client_credentials_grant_test.go b/integration/client_credentials_grant_test.go index 4ddfe90fe..a1178694f 100644 --- a/integration/client_credentials_grant_test.go +++ b/integration/client_credentials_grant_test.go @@ -3,6 +3,7 @@ package integration_test import ( "testing" + "github.com/ory-am/fosite" "github.com/ory-am/fosite/compose" "github.com/ory-am/fosite/handler/oauth2" "github.com/stretchr/testify/assert" @@ -19,10 +20,8 @@ func TestClientCredentialsFlow(t *testing.T) { } func runClientCredentialsGrantTest(t *testing.T, strategy oauth2.AccessTokenStrategy) { - f := compose.Compose(new(compose.Config), fositeStore, strategy, compose.OAuth2ClientCredentialsGrantFactory) - ts := mockServer(t, f, &mySessionData{ - HMACSession: new(oauth2.HMACSession), - }) + f := compose.Compose(new(compose.Config), fositeStore, strategy, compose.OAuth2ClientCredentialsGrantFactory, compose.OAuth2TokenIntrospectionFactory) + ts := mockServer(t, f, &fosite.DefaultSession{}) defer ts.Close() oauthClient := newOAuth2AppClient(ts) diff --git a/integration/helper_endpoints_test.go b/integration/helper_endpoints_test.go index 4050923f8..ffae03a98 100644 --- a/integration/helper_endpoints_test.go +++ b/integration/helper_endpoints_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/ory-am/fosite" - foauth "github.com/ory-am/fosite/handler/oauth2" "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) @@ -14,12 +13,7 @@ type stackTracer interface { StackTrace() errors.StackTrace } -type mySessionData struct { - Foo string - *foauth.HMACSession -} - -func tokenRevocationHandler(t *testing.T, oauth2 fosite.OAuth2Provider, session interface{}) func(rw http.ResponseWriter, req *http.Request) { +func tokenRevocationHandler(t *testing.T, oauth2 fosite.OAuth2Provider, session fosite.Session) func(rw http.ResponseWriter, req *http.Request) { return func(rw http.ResponseWriter, req *http.Request) { ctx := fosite.NewContext() err := oauth2.NewRevocationRequest(ctx, req) @@ -31,7 +25,7 @@ func tokenRevocationHandler(t *testing.T, oauth2 fosite.OAuth2Provider, session } } -func tokenIntrospectionHandler(t *testing.T, oauth2 fosite.OAuth2Provider, session interface{}) func(rw http.ResponseWriter, req *http.Request) { +func tokenIntrospectionHandler(t *testing.T, oauth2 fosite.OAuth2Provider, session fosite.Session) func(rw http.ResponseWriter, req *http.Request) { return func(rw http.ResponseWriter, req *http.Request) { ctx := fosite.NewContext() ar, err := oauth2.NewIntrospectionRequest(ctx, req, session) @@ -46,7 +40,7 @@ func tokenIntrospectionHandler(t *testing.T, oauth2 fosite.OAuth2Provider, sessi } } -func tokenInfoHandler(t *testing.T, oauth2 fosite.OAuth2Provider, session interface{}) func(rw http.ResponseWriter, req *http.Request) { +func tokenInfoHandler(t *testing.T, oauth2 fosite.OAuth2Provider, session fosite.Session) func(rw http.ResponseWriter, req *http.Request) { return func(rw http.ResponseWriter, req *http.Request) { ctx := fosite.NewContext() if _, err := oauth2.IntrospectToken(ctx, fosite.AccessTokenFromRequest(req), fosite.AccessToken, session); err != nil { @@ -61,7 +55,7 @@ func tokenInfoHandler(t *testing.T, oauth2 fosite.OAuth2Provider, session interf } } -func authEndpointHandler(t *testing.T, oauth2 fosite.OAuth2Provider, session interface{}) func(rw http.ResponseWriter, req *http.Request) { +func authEndpointHandler(t *testing.T, oauth2 fosite.OAuth2Provider, session fosite.Session) func(rw http.ResponseWriter, req *http.Request) { return func(rw http.ResponseWriter, req *http.Request) { ctx := fosite.NewContext() @@ -124,9 +118,7 @@ func tokenEndpointHandler(t *testing.T, oauth2 fosite.OAuth2Provider) func(rw ht req.ParseForm() ctx := fosite.NewContext() - accessRequest, err := oauth2.NewAccessRequest(ctx, req, &mySessionData{ - HMACSession: &foauth.HMACSession{}, - }) + accessRequest, err := oauth2.NewAccessRequest(ctx, req, &fosite.DefaultSession{}) if err != nil { t.Logf("Access request failed because %s.", err.Error()) t.Logf("Request: %s.", accessRequest) diff --git a/integration/helper_setup_test.go b/integration/helper_setup_test.go index b396dd541..56de09756 100644 --- a/integration/helper_setup_test.go +++ b/integration/helper_setup_test.go @@ -43,7 +43,6 @@ var fositeStore = &storage.MemoryStore{ type defaultSession struct { *openid.DefaultSession - *oauth2.HMACSession } var accessTokenLifespan = time.Hour @@ -80,7 +79,7 @@ var hmacStrategy = &oauth2.HMACSHAStrategy{ AuthorizeCodeLifespan: authCodeLifespan, } -func mockServer(t *testing.T, f fosite.OAuth2Provider, session interface{}) *httptest.Server { +func mockServer(t *testing.T, f fosite.OAuth2Provider, session fosite.Session) *httptest.Server { router := mux.NewRouter() router.HandleFunc("/auth", authEndpointHandler(t, f, session)) router.HandleFunc("/token", tokenEndpointHandler(t, f)) diff --git a/integration/introspect_token_test.go b/integration/introspect_token_test.go index 1fb47d59e..923d99dbc 100644 --- a/integration/introspect_token_test.go +++ b/integration/introspect_token_test.go @@ -4,6 +4,7 @@ import ( "testing" "encoding/json" + "github.com/ory-am/fosite" "github.com/ory-am/fosite/compose" "github.com/ory-am/fosite/handler/oauth2" "github.com/parnurzeal/gorequest" @@ -21,10 +22,8 @@ func TestIntrospectToken(t *testing.T) { } func runIntrospectTokenTest(t *testing.T, strategy oauth2.AccessTokenStrategy) { - f := compose.Compose(new(compose.Config), fositeStore, strategy, compose.OAuth2ClientCredentialsGrantFactory) - ts := mockServer(t, f, &mySessionData{ - HMACSession: new(oauth2.HMACSession), - }) + f := compose.Compose(new(compose.Config), fositeStore, strategy, compose.OAuth2ClientCredentialsGrantFactory, compose.OAuth2TokenIntrospectionFactory) + ts := mockServer(t, f, &fosite.DefaultSession{}) defer ts.Close() oauthClient := newOAuth2AppClient(ts) @@ -47,28 +46,28 @@ func runIntrospectTokenTest(t *testing.T, strategy oauth2.AccessTokenStrategy) { }, { prepare: func(s *gorequest.SuperAgent) *gorequest.SuperAgent { - return s.Set("Authorization", "bearer "+a.AccessToken) + return s.Set("Authorization", "bearer " + a.AccessToken) }, isActive: true, scopes: "fosite", }, { prepare: func(s *gorequest.SuperAgent) *gorequest.SuperAgent { - return s.Set("Authorization", "bearer "+a.AccessToken) + return s.Set("Authorization", "bearer " + a.AccessToken) }, isActive: true, scopes: "", }, { prepare: func(s *gorequest.SuperAgent) *gorequest.SuperAgent { - return s.Set("Authorization", "bearer "+a.AccessToken) + return s.Set("Authorization", "bearer " + a.AccessToken) }, isActive: false, scopes: "foo", }, { prepare: func(s *gorequest.SuperAgent) *gorequest.SuperAgent { - return s.Set("Authorization", "bearer "+b.AccessToken) + return s.Set("Authorization", "bearer " + b.AccessToken) }, isActive: false, scopes: "", diff --git a/integration/oidc_explicit_test.go b/integration/oidc_explicit_test.go index eb17c5cb4..934ac2335 100644 --- a/integration/oidc_explicit_test.go +++ b/integration/oidc_explicit_test.go @@ -20,7 +20,7 @@ func TestOpenIDConnectExplicitFlow(t *testing.T) { Claims: &jwt.IDTokenClaims{ Subject: "peter", }, - Headers: &jwt.Headers{}, + Headers: &jwt.Headers{}, }, } f := compose.ComposeAllEnabled(new(compose.Config), fositeStore, []byte("some-secret-thats-random"), internal.MustRSAKey()) diff --git a/integration/oidc_implicit_hybrid_test.go b/integration/oidc_implicit_hybrid_test.go index 0817566f8..d774bfdf2 100644 --- a/integration/oidc_implicit_hybrid_test.go +++ b/integration/oidc_implicit_hybrid_test.go @@ -25,7 +25,7 @@ func TestOIDCImplicitFlow(t *testing.T) { Claims: &jwt.IDTokenClaims{ Subject: "peter", }, - Headers: &jwt.Headers{}, + Headers: &jwt.Headers{}, }, } f := compose.ComposeAllEnabled(new(compose.Config), fositeStore, []byte("some-secret-thats-random"), internal.MustRSAKey()) @@ -74,7 +74,7 @@ func TestOIDCImplicitFlow(t *testing.T) { c.setup() var callbackURL *url.URL - authURL := strings.Replace(oauthClient.AuthCodeURL(state), "response_type=code", "response_type="+c.responseType, -1) + "&nonce=" + c.nonce + authURL := strings.Replace(oauthClient.AuthCodeURL(state), "response_type=code", "response_type=" + c.responseType, -1) + "&nonce=" + c.nonce client := &http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { callbackURL = req.URL diff --git a/integration/refresh_token_grant_test.go b/integration/refresh_token_grant_test.go index 49802002e..dd8607acc 100644 --- a/integration/refresh_token_grant_test.go +++ b/integration/refresh_token_grant_test.go @@ -6,11 +6,12 @@ import ( "net/http" "time" + "github.com/ory-am/fosite" "github.com/ory-am/fosite/compose" hst "github.com/ory-am/fosite/handler/oauth2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - oauth2 "golang.org/x/oauth2" + "golang.org/x/oauth2" ) func TestRefreshTokenFlow(t *testing.T) { @@ -28,10 +29,9 @@ func runRefreshTokenGrantTest(t *testing.T, strategy interface{}) { strategy, compose.OAuth2AuthorizeExplicitFactory, compose.OAuth2RefreshTokenGrantFactory, + compose.OAuth2TokenIntrospectionFactory, ) - ts := mockServer(t, f, &mySessionData{ - HMACSession: new(hst.HMACSession), - }) + ts := mockServer(t, f, &fosite.DefaultSession{}) defer ts.Close() oauthClient := newOAuth2Client(ts) @@ -66,8 +66,9 @@ func runRefreshTokenGrantTest(t *testing.T, strategy interface{}) { require.Nil(t, err, "(%d) %s", k, c.description) require.NotEmpty(t, token.AccessToken, "(%d) %s", k, c.description) + t.Logf("Token %s\n", token) token.Expiry = token.Expiry.Add(-time.Hour * 24) - t.Logf("Token %s", token) + t.Logf("Token %s\n", token) tokenSource := oauthClient.TokenSource(oauth2.NoContext, token) refreshed, err := tokenSource.Token() diff --git a/integration/resource_owner_password_credentials_grant_test.go b/integration/resource_owner_password_credentials_grant_test.go index 84ccbdf63..f4041224f 100644 --- a/integration/resource_owner_password_credentials_grant_test.go +++ b/integration/resource_owner_password_credentials_grant_test.go @@ -3,6 +3,7 @@ package integration_test import ( "testing" + "github.com/ory-am/fosite" "github.com/ory-am/fosite/compose" hst "github.com/ory-am/fosite/handler/oauth2" "github.com/stretchr/testify/assert" @@ -20,9 +21,7 @@ func TestResourceOwnerPasswordCredentialsFlow(t *testing.T) { func runResourceOwnerPasswordCredentialsGrantTest(t *testing.T, strategy hst.AccessTokenStrategy) { f := compose.Compose(new(compose.Config), fositeStore, strategy, compose.OAuth2ResourceOwnerPasswordCredentialsFactory) - ts := mockServer(t, f, &mySessionData{ - HMACSession: new(hst.HMACSession), - }) + ts := mockServer(t, f, &fosite.DefaultSession{}) defer ts.Close() var username, password string diff --git a/integration/revoke_token_test.go b/integration/revoke_token_test.go index e64c6224c..1f333bc12 100644 --- a/integration/revoke_token_test.go +++ b/integration/revoke_token_test.go @@ -3,6 +3,7 @@ package integration_test import ( "testing" + "github.com/ory-am/fosite" "github.com/ory-am/fosite/compose" "github.com/ory-am/fosite/handler/oauth2" "github.com/parnurzeal/gorequest" @@ -21,32 +22,30 @@ func TestRevokeToken(t *testing.T) { } func runRevokeTokenTest(t *testing.T, strategy oauth2.AccessTokenStrategy) { - f := compose.Compose(new(compose.Config), fositeStore, strategy, compose.OAuth2ClientCredentialsGrantFactory, compose.OAuth2TokenRevocationFactory) - ts := mockServer(t, f, &mySessionData{ - HMACSession: new(oauth2.HMACSession), - }) + f := compose.Compose(new(compose.Config), fositeStore, strategy, compose.OAuth2ClientCredentialsGrantFactory, compose.OAuth2TokenIntrospectionFactory, compose.OAuth2TokenRevocationFactory) + ts := mockServer(t, f, &fosite.DefaultSession{}) defer ts.Close() oauthClient := newOAuth2AppClient(ts) token, err := oauthClient.Token(goauth.NoContext) assert.Nil(t, err) - resp, _, errs := gorequest.New().Post(ts.URL+"/revoke"). + resp, _, errs := gorequest.New().Post(ts.URL + "/revoke"). SetBasicAuth(oauthClient.ClientID, oauthClient.ClientSecret). Type("form"). SendStruct(map[string]string{"token": "asdf"}).End() assert.Len(t, errs, 0) assert.Equal(t, 200, resp.StatusCode) - resp, _, errs = gorequest.New().Post(ts.URL+"/revoke"). + resp, _, errs = gorequest.New().Post(ts.URL + "/revoke"). SetBasicAuth(oauthClient.ClientID, oauthClient.ClientSecret). Type("form"). SendStruct(map[string]string{"token": token.AccessToken}).End() assert.Len(t, errs, 0) assert.Equal(t, 200, resp.StatusCode) - hres, _, errs := gorequest.New().Get(ts.URL+"/info"). - Set("Authorization", "bearer "+token.AccessToken). + hres, _, errs := gorequest.New().Get(ts.URL + "/info"). + Set("Authorization", "bearer " + token.AccessToken). End() require.Len(t, errs, 0) assert.Equal(t, http.StatusUnauthorized, hres.StatusCode) diff --git a/internal/access_request.go b/internal/access_request.go index b2cf6d0f2..1e243513f 100644 --- a/internal/access_request.go +++ b/internal/access_request.go @@ -109,9 +109,9 @@ func (_mr *_MockAccessRequesterRecorder) GetRequestedScopes() *gomock.Call { return _mr.mock.ctrl.RecordCall(_mr.mock, "GetRequestedScopes") } -func (_m *MockAccessRequester) GetSession() interface{} { +func (_m *MockAccessRequester) GetSession() fosite.Session { ret := _m.ctrl.Call(_m, "GetSession") - ret0, _ := ret[0].(interface{}) + ret0, _ := ret[0].(fosite.Session) return ret0 } @@ -143,7 +143,7 @@ func (_mr *_MockAccessRequesterRecorder) SetRequestedScopes(arg0 interface{}) *g return _mr.mock.ctrl.RecordCall(_mr.mock, "SetRequestedScopes", arg0) } -func (_m *MockAccessRequester) SetSession(_param0 interface{}) { +func (_m *MockAccessRequester) SetSession(_param0 fosite.Session) { _m.ctrl.Call(_m, "SetSession", _param0) } diff --git a/internal/access_token_storage.go b/internal/access_token_storage.go index 7ce1df1e0..3956edace 100644 --- a/internal/access_token_storage.go +++ b/internal/access_token_storage.go @@ -50,7 +50,7 @@ func (_mr *_MockAccessTokenStorageRecorder) DeleteAccessTokenSession(arg0, arg1 return _mr.mock.ctrl.RecordCall(_mr.mock, "DeleteAccessTokenSession", arg0, arg1) } -func (_m *MockAccessTokenStorage) GetAccessTokenSession(_param0 context.Context, _param1 string, _param2 interface{}) (fosite.Requester, error) { +func (_m *MockAccessTokenStorage) GetAccessTokenSession(_param0 context.Context, _param1 string, _param2 fosite.Session) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetAccessTokenSession", _param0, _param1, _param2) ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) diff --git a/internal/authorize_code_storage.go b/internal/authorize_code_storage.go index f8269e45e..ef7431901 100644 --- a/internal/authorize_code_storage.go +++ b/internal/authorize_code_storage.go @@ -50,7 +50,7 @@ func (_mr *_MockAuthorizeCodeStorageRecorder) DeleteAuthorizeCodeSession(arg0, a return _mr.mock.ctrl.RecordCall(_mr.mock, "DeleteAuthorizeCodeSession", arg0, arg1) } -func (_m *MockAuthorizeCodeStorage) GetAuthorizeCodeSession(_param0 context.Context, _param1 string, _param2 interface{}) (fosite.Requester, error) { +func (_m *MockAuthorizeCodeStorage) GetAuthorizeCodeSession(_param0 context.Context, _param1 string, _param2 fosite.Session) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetAuthorizeCodeSession", _param0, _param1, _param2) ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) diff --git a/internal/authorize_request.go b/internal/authorize_request.go index 74179534e..46174ddd0 100644 --- a/internal/authorize_request.go +++ b/internal/authorize_request.go @@ -129,9 +129,9 @@ func (_mr *_MockAuthorizeRequesterRecorder) GetResponseTypes() *gomock.Call { return _mr.mock.ctrl.RecordCall(_mr.mock, "GetResponseTypes") } -func (_m *MockAuthorizeRequester) GetSession() interface{} { +func (_m *MockAuthorizeRequester) GetSession() fosite.Session { ret := _m.ctrl.Call(_m, "GetSession") - ret0, _ := ret[0].(interface{}) + ret0, _ := ret[0].(fosite.Session) return ret0 } @@ -191,7 +191,7 @@ func (_mr *_MockAuthorizeRequesterRecorder) SetResponseTypeHandled(arg0 interfac return _mr.mock.ctrl.RecordCall(_mr.mock, "SetResponseTypeHandled", arg0) } -func (_m *MockAuthorizeRequester) SetSession(_param0 interface{}) { +func (_m *MockAuthorizeRequester) SetSession(_param0 fosite.Session) { _m.ctrl.Call(_m, "SetSession", _param0) } diff --git a/internal/oauth2_client_storage.go b/internal/oauth2_client_storage.go index 7a7dcc6c1..491986fd4 100644 --- a/internal/oauth2_client_storage.go +++ b/internal/oauth2_client_storage.go @@ -50,7 +50,7 @@ func (_mr *_MockClientCredentialsGrantStorageRecorder) DeleteAccessTokenSession( return _mr.mock.ctrl.RecordCall(_mr.mock, "DeleteAccessTokenSession", arg0, arg1) } -func (_m *MockClientCredentialsGrantStorage) GetAccessTokenSession(_param0 context.Context, _param1 string, _param2 interface{}) (fosite.Requester, error) { +func (_m *MockClientCredentialsGrantStorage) GetAccessTokenSession(_param0 context.Context, _param1 string, _param2 fosite.Session) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetAccessTokenSession", _param0, _param1, _param2) ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) diff --git a/internal/oauth2_explicit_storage.go b/internal/oauth2_explicit_storage.go index 4f77e35bc..6aa44afe1 100644 --- a/internal/oauth2_explicit_storage.go +++ b/internal/oauth2_explicit_storage.go @@ -50,7 +50,7 @@ func (_mr *_MockAuthorizeCodeGrantStorageRecorder) DeleteAuthorizeCodeSession(ar return _mr.mock.ctrl.RecordCall(_mr.mock, "DeleteAuthorizeCodeSession", arg0, arg1) } -func (_m *MockAuthorizeCodeGrantStorage) GetAuthorizeCodeSession(_param0 context.Context, _param1 string, _param2 interface{}) (fosite.Requester, error) { +func (_m *MockAuthorizeCodeGrantStorage) GetAuthorizeCodeSession(_param0 context.Context, _param1 string, _param2 fosite.Session) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetAuthorizeCodeSession", _param0, _param1, _param2) ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) diff --git a/internal/oauth2_owner_storage.go b/internal/oauth2_owner_storage.go index 6c147d7fa..6e13fac85 100644 --- a/internal/oauth2_owner_storage.go +++ b/internal/oauth2_owner_storage.go @@ -80,7 +80,7 @@ func (_mr *_MockResourceOwnerPasswordCredentialsGrantStorageRecorder) DeleteRefr return _mr.mock.ctrl.RecordCall(_mr.mock, "DeleteRefreshTokenSession", arg0, arg1) } -func (_m *MockResourceOwnerPasswordCredentialsGrantStorage) GetAccessTokenSession(_param0 context.Context, _param1 string, _param2 interface{}) (fosite.Requester, error) { +func (_m *MockResourceOwnerPasswordCredentialsGrantStorage) GetAccessTokenSession(_param0 context.Context, _param1 string, _param2 fosite.Session) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetAccessTokenSession", _param0, _param1, _param2) ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) @@ -91,7 +91,7 @@ func (_mr *_MockResourceOwnerPasswordCredentialsGrantStorageRecorder) GetAccessT return _mr.mock.ctrl.RecordCall(_mr.mock, "GetAccessTokenSession", arg0, arg1, arg2) } -func (_m *MockResourceOwnerPasswordCredentialsGrantStorage) GetRefreshTokenSession(_param0 context.Context, _param1 string, _param2 interface{}) (fosite.Requester, error) { +func (_m *MockResourceOwnerPasswordCredentialsGrantStorage) GetRefreshTokenSession(_param0 context.Context, _param1 string, _param2 fosite.Session) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetRefreshTokenSession", _param0, _param1, _param2) ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) diff --git a/internal/oauth2_refresh_storage.go b/internal/oauth2_refresh_storage.go index 2228abaa3..7f8d652e8 100644 --- a/internal/oauth2_refresh_storage.go +++ b/internal/oauth2_refresh_storage.go @@ -50,7 +50,7 @@ func (_mr *_MockRefreshTokenGrantStorageRecorder) DeleteRefreshTokenSession(arg0 return _mr.mock.ctrl.RecordCall(_mr.mock, "DeleteRefreshTokenSession", arg0, arg1) } -func (_m *MockRefreshTokenGrantStorage) GetRefreshTokenSession(_param0 context.Context, _param1 string, _param2 interface{}) (fosite.Requester, error) { +func (_m *MockRefreshTokenGrantStorage) GetRefreshTokenSession(_param0 context.Context, _param1 string, _param2 fosite.Session) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetRefreshTokenSession", _param0, _param1, _param2) ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) diff --git a/internal/oauth2_revoke_storage.go b/internal/oauth2_revoke_storage.go index 00ac2ad79..19e63dc2b 100644 --- a/internal/oauth2_revoke_storage.go +++ b/internal/oauth2_revoke_storage.go @@ -70,7 +70,7 @@ func (_mr *_MockTokenRevocationStorageRecorder) DeleteRefreshTokenSession(arg0, return _mr.mock.ctrl.RecordCall(_mr.mock, "DeleteRefreshTokenSession", arg0, arg1) } -func (_m *MockTokenRevocationStorage) GetAccessTokenSession(_param0 context.Context, _param1 string, _param2 interface{}) (fosite.Requester, error) { +func (_m *MockTokenRevocationStorage) GetAccessTokenSession(_param0 context.Context, _param1 string, _param2 fosite.Session) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetAccessTokenSession", _param0, _param1, _param2) ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) @@ -81,7 +81,7 @@ func (_mr *_MockTokenRevocationStorageRecorder) GetAccessTokenSession(arg0, arg1 return _mr.mock.ctrl.RecordCall(_mr.mock, "GetAccessTokenSession", arg0, arg1, arg2) } -func (_m *MockTokenRevocationStorage) GetRefreshTokenSession(_param0 context.Context, _param1 string, _param2 interface{}) (fosite.Requester, error) { +func (_m *MockTokenRevocationStorage) GetRefreshTokenSession(_param0 context.Context, _param1 string, _param2 fosite.Session) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetRefreshTokenSession", _param0, _param1, _param2) ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) diff --git a/internal/oauth2_storage.go b/internal/oauth2_storage.go index 8f4ecccdd..416df3970 100644 --- a/internal/oauth2_storage.go +++ b/internal/oauth2_storage.go @@ -90,7 +90,7 @@ func (_mr *_MockCoreStorageRecorder) DeleteRefreshTokenSession(arg0, arg1 interf return _mr.mock.ctrl.RecordCall(_mr.mock, "DeleteRefreshTokenSession", arg0, arg1) } -func (_m *MockCoreStorage) GetAccessTokenSession(_param0 context.Context, _param1 string, _param2 interface{}) (fosite.Requester, error) { +func (_m *MockCoreStorage) GetAccessTokenSession(_param0 context.Context, _param1 string, _param2 fosite.Session) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetAccessTokenSession", _param0, _param1, _param2) ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) @@ -101,7 +101,7 @@ func (_mr *_MockCoreStorageRecorder) GetAccessTokenSession(arg0, arg1, arg2 inte return _mr.mock.ctrl.RecordCall(_mr.mock, "GetAccessTokenSession", arg0, arg1, arg2) } -func (_m *MockCoreStorage) GetAuthorizeCodeSession(_param0 context.Context, _param1 string, _param2 interface{}) (fosite.Requester, error) { +func (_m *MockCoreStorage) GetAuthorizeCodeSession(_param0 context.Context, _param1 string, _param2 fosite.Session) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetAuthorizeCodeSession", _param0, _param1, _param2) ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) @@ -112,7 +112,7 @@ func (_mr *_MockCoreStorageRecorder) GetAuthorizeCodeSession(arg0, arg1, arg2 in return _mr.mock.ctrl.RecordCall(_mr.mock, "GetAuthorizeCodeSession", arg0, arg1, arg2) } -func (_m *MockCoreStorage) GetRefreshTokenSession(_param0 context.Context, _param1 string, _param2 interface{}) (fosite.Requester, error) { +func (_m *MockCoreStorage) GetRefreshTokenSession(_param0 context.Context, _param1 string, _param2 fosite.Session) (fosite.Requester, error) { ret := _m.ctrl.Call(_m, "GetRefreshTokenSession", _param0, _param1, _param2) ret0, _ := ret[0].(fosite.Requester) ret1, _ := ret[1].(error) diff --git a/internal/request.go b/internal/request.go index f26ad3366..2f4603330 100644 --- a/internal/request.go +++ b/internal/request.go @@ -99,9 +99,9 @@ func (_mr *_MockRequesterRecorder) GetRequestedScopes() *gomock.Call { return _mr.mock.ctrl.RecordCall(_mr.mock, "GetRequestedScopes") } -func (_m *MockRequester) GetSession() interface{} { +func (_m *MockRequester) GetSession() fosite.Session { ret := _m.ctrl.Call(_m, "GetSession") - ret0, _ := ret[0].(interface{}) + ret0, _ := ret[0].(fosite.Session) return ret0 } @@ -133,7 +133,7 @@ func (_mr *_MockRequesterRecorder) SetRequestedScopes(arg0 interface{}) *gomock. return _mr.mock.ctrl.RecordCall(_mr.mock, "SetRequestedScopes", arg0) } -func (_m *MockRequester) SetSession(_param0 interface{}) { +func (_m *MockRequester) SetSession(_param0 fosite.Session) { _m.ctrl.Call(_m, "SetSession", _param0) } diff --git a/introspect.go b/introspect.go index 3577430ed..408f98e99 100644 --- a/introspect.go +++ b/introspect.go @@ -22,7 +22,7 @@ func AccessTokenFromRequest(req *http.Request) string { return split[1] } -func (f *Fosite) IntrospectToken(ctx context.Context, token string, tokenType TokenType, session interface{}, scopes ...string) (AccessRequester, error) { +func (f *Fosite) IntrospectToken(ctx context.Context, token string, tokenType TokenType, session Session, scopes ...string) (AccessRequester, error) { var found bool = false ar := NewAccessRequest(session) diff --git a/introspection_request_handler.go b/introspection_request_handler.go index 128f9c15f..29f273c56 100644 --- a/introspection_request_handler.go +++ b/introspection_request_handler.go @@ -5,7 +5,6 @@ import ( "golang.org/x/net/context" "net/http" "strings" - "time" ) // NewIntrospectionRequest initiates token introspection as defined in @@ -87,7 +86,7 @@ import ( // Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW // // token=mF_9.B5f-4.1JqM&token_type_hint=access_token -func (f *Fosite) NewIntrospectionRequest(ctx context.Context, r *http.Request, session interface{}) (IntrospectionResponder, error) { +func (f *Fosite) NewIntrospectionRequest(ctx context.Context, r *http.Request, session Session) (IntrospectionResponder, error) { if r.Method != "POST" { return nil, errors.Wrap(ErrInvalidRequest, "HTTP method is not POST") } else if err := r.ParseForm(); err != nil { @@ -134,9 +133,8 @@ func (f *Fosite) NewIntrospectionRequest(ctx context.Context, r *http.Request, s } type IntrospectionResponse struct { - Active bool - AccessRequester AccessRequester - ExpiresAt time.Time + Active bool `json:"active"` + AccessRequester AccessRequester `json:",extra"` } func (r *IntrospectionResponse) IsActive() bool { @@ -146,7 +144,3 @@ func (r *IntrospectionResponse) IsActive() bool { func (r *IntrospectionResponse) GetAccessRequester() AccessRequester { return r.AccessRequester } - -func (r *IntrospectionResponse) GetExpiresAt() time.Time { - return r.ExpiresAt -} diff --git a/introspection_request_handler_test.go b/introspection_request_handler_test.go index 1728e715a..8e4a74507 100644 --- a/introspection_request_handler_test.go +++ b/introspection_request_handler_test.go @@ -3,28 +3,24 @@ package fosite_test import ( "github.com/golang/mock/gomock" "github.com/ory-am/fosite" + . "github.com/ory-am/fosite" "github.com/ory-am/fosite/compose" "github.com/ory-am/fosite/internal" + "github.com/ory-am/fosite/storage" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "net/http" "net/url" "testing" - "time" - - . "github.com/ory-am/fosite" - "github.com/ory-am/fosite/storage" - "github.com/pkg/errors" ) func TestIntrospectionResponse(t *testing.T) { r := &fosite.IntrospectionResponse{ AccessRequester: fosite.NewAccessRequest(nil), - ExpiresAt: time.Now(), Active: true, } assert.Equal(t, r.AccessRequester, r.GetAccessRequester()) - assert.Equal(t, r.ExpiresAt, r.GetExpiresAt()) assert.Equal(t, r.Active, r.IsActive()) } diff --git a/introspection_response_writer.go b/introspection_response_writer.go index 4d95fefc8..a5e4914a5 100644 --- a/introspection_response_writer.go +++ b/introspection_response_writer.go @@ -179,18 +179,22 @@ func (f *Fosite) WriteIntrospectionResponse(rw http.ResponseWriter, r Introspect } _ = json.NewEncoder(rw).Encode(struct { - Active bool `json:"active"` - ClientID string `json:"client_id"` - Scope string `json:"scope"` - ExpiresAt int64 `json:"exp"` - IssuedAt int64 `json:"iat"` - Extra interface{} `json:",inline"` + Active bool `json:"active"` + ClientID string `json:"client_id,omitempty"` + Scope string `json:"scope,omitempty"` + ExpiresAt int64 `json:"exp,omitempty"` + IssuedAt int64 `json:"iat,omitempty"` + Subject string `json:"sub,omitempty"` + Username string `json:"username,omitempty"` + Session Session `json:"sess"` }{ Active: true, ClientID: r.GetAccessRequester().GetClient().GetID(), Scope: strings.Join(r.GetAccessRequester().GetGrantedScopes(), " "), - ExpiresAt: r.GetExpiresAt().Unix(), + ExpiresAt: r.GetAccessRequester().GetSession().GetExpiresAt(AccessToken).Unix(), IssuedAt: r.GetAccessRequester().GetRequestedAt().Unix(), - Extra: r.GetAccessRequester().GetSession(), + Subject: r.GetAccessRequester().GetSession().GetSubject(), + Username: r.GetAccessRequester().GetSession().GetUsername(), + Session: r.GetAccessRequester().GetSession(), }) } diff --git a/oauth2.go b/oauth2.go index 835f675e6..74af39e82 100644 --- a/oauth2.go +++ b/oauth2.go @@ -52,7 +52,7 @@ type OAuth2Provider interface { // If an authorization request is missing the "response_type" parameter, // or if the response type is not understood, the authorization server // MUST return an error response as described in Section 4.1.2.1. - NewAuthorizeResponse(ctx context.Context, req *http.Request, requester AuthorizeRequester, session interface{}) (AuthorizeResponder, error) + NewAuthorizeResponse(ctx context.Context, req *http.Request, requester AuthorizeRequester, session Session) (AuthorizeResponder, error) // WriteAuthorizeError returns the error codes to the redirection endpoint or shows the error to the user, if no valid // redirect uri was given. Implements rfc6749#section-4.1.2.1 @@ -91,7 +91,7 @@ type OAuth2Provider interface { // * https://tools.ietf.org/html/rfc6749#section-3.2.1 (everything) // // Furthermore the registered handlers should implement their specs accordingly. - NewAccessRequest(ctx context.Context, req *http.Request, session interface{}) (AccessRequester, error) + NewAccessRequest(ctx context.Context, req *http.Request, session Session) (AccessRequester, error) // NewAccessResponse creates a new access response and validates that access_token and token_type are set. // @@ -125,11 +125,11 @@ type OAuth2Provider interface { // IntrospectToken returns token metadata, if the token is valid. Tokens generated by the authorization endpoint, // such as the authorization code, can not be introspected. - IntrospectToken(ctx context.Context, token string, tokenType TokenType, session interface{}, scope ...string) (AccessRequester, error) + IntrospectToken(ctx context.Context, token string, tokenType TokenType, session Session, scope ...string) (AccessRequester, error) // NewIntrospectionRequest initiates token introspection as defined in // https://tools.ietf.org/search/rfc7662#section-2.1 - NewIntrospectionRequest(ctx context.Context, r *http.Request, session interface{}) (IntrospectionResponder, error) + NewIntrospectionRequest(ctx context.Context, r *http.Request, session Session) (IntrospectionResponder, error) // WriteIntrospectionError responds with an error if token introspection failed as defined in // https://tools.ietf.org/search/rfc7662#section-2.3 @@ -149,9 +149,6 @@ type IntrospectionResponder interface { // AccessRequester returns nil when IsActive() is false and the original access request object otherwise. GetAccessRequester() AccessRequester - - // GetExpiresAt returns the expiration date. - GetExpiresAt() time.Time } // Requester is an abstract interface for handling requests in Fosite. @@ -181,10 +178,10 @@ type Requester interface { GrantScope(scope string) // GetSession returns a pointer to the request's session or nil if none is set. - GetSession() (session interface{}) + GetSession() (session Session) // GetSession sets the request's session pointer. - SetSession(session interface{}) + SetSession(session Session) // GetRequestForm returns the request's form input. GetRequestForm() url.Values diff --git a/request.go b/request.go index 44f4f94d5..38c70c2e5 100644 --- a/request.go +++ b/request.go @@ -9,13 +9,13 @@ import ( // Request is an implementation of Requester type Request struct { - ID string `json:"id" gorethink:"id"` - RequestedAt time.Time `json:"requestedAt" gorethink:"requestedAt"` - Client Client `json:"client" gorethink:"client"` - Scopes Arguments `json:"scopes" gorethink:"scopes"` - GrantedScopes Arguments `json:"grantedScopes" gorethink:"grantedScopes"` - Form url.Values `json:"form" gorethink:"form"` - Session interface{} `json:"session" gorethink:"session"` + ID string `json:"id" gorethink:"id"` + RequestedAt time.Time `json:"requestedAt" gorethink:"requestedAt"` + Client Client `json:"client" gorethink:"client"` + Scopes Arguments `json:"scopes" gorethink:"scopes"` + GrantedScopes Arguments `json:"grantedScopes" gorethink:"grantedScopes"` + Form url.Values `json:"form" gorethink:"form"` + Session Session `json:"session" gorethink:"session"` } func NewRequest() *Request { @@ -79,11 +79,11 @@ func (a *Request) GrantScope(scope string) { a.GrantedScopes = append(a.GrantedScopes, scope) } -func (a *Request) SetSession(session interface{}) { +func (a *Request) SetSession(session Session) { a.Session = session } -func (a *Request) GetSession() interface{} { +func (a *Request) GetSession() Session { return a.Session } diff --git a/request_test.go b/request_test.go index 2027ad7ee..2517f0180 100644 --- a/request_test.go +++ b/request_test.go @@ -16,7 +16,7 @@ func TestRequest(t *testing.T) { Scopes: Arguments{}, GrantedScopes: []string{}, Form: url.Values{"foo": []string{"bar"}}, - Session: 1234, + Session: new(DefaultSession), } assert.Equal(t, r.RequestedAt, r.GetRequestedAt()) @@ -34,7 +34,7 @@ func TestMergeRequest(t *testing.T) { Scopes: Arguments{"asdff"}, GrantedScopes: []string{"asdf"}, Form: url.Values{"foo": []string{"fasdf"}}, - Session: 54321, + Session: new(DefaultSession), } b := &Request{ RequestedAt: time.Now(), @@ -42,7 +42,7 @@ func TestMergeRequest(t *testing.T) { Scopes: Arguments{}, GrantedScopes: []string{}, Form: url.Values{}, - Session: 12345, + Session: new(DefaultSession), } b.Merge(a) diff --git a/session.go b/session.go new file mode 100644 index 000000000..2bf313fd4 --- /dev/null +++ b/session.go @@ -0,0 +1,63 @@ +package fosite + +import "time" + +// Session is an interface that is used to store session data between OAuth2 requests. It can be used to look up +// when a session expires or what the subject's name was. +type Session interface { + // SetExpiresAt sets the expiration time of a token. + // + // session.SetExpiresAt(fosite.AccessToken, time.Now().Add(time.Hour)) + SetExpiresAt(key TokenType, exp time.Time) + + // SetExpiresAt returns expiration time of a token if set, or time.IsZero() if not. + // + // session.GetExpiresAt(fosite.AccessToken) + GetExpiresAt(key TokenType) time.Time + + // GetUsername returns the username, if set. This is optional and only used during token introspection. + GetUsername() string + + // GetSubject returns the subject, if set. This is optional and only used during token introspection. + GetSubject() string +} + +// DefaultSession is a default implementation of the session interface. +type DefaultSession struct { + ExpiresAt map[TokenType]time.Time + Username string + Subject string +} + +func (s *DefaultSession) SetExpiresAt(key TokenType, exp time.Time) { + if s.ExpiresAt == nil { + s.ExpiresAt = make(map[TokenType]time.Time) + } + s.ExpiresAt[key] = exp +} + +func (s *DefaultSession) GetExpiresAt(key TokenType) time.Time { + if s.ExpiresAt == nil { + s.ExpiresAt = make(map[TokenType]time.Time) + } + + if _, ok := s.ExpiresAt[key]; !ok { + return time.Time{} + } + return s.ExpiresAt[key] +} + +func (s *DefaultSession) GetUsername() string { + if s == nil { + return "" + } + return s.Username +} + +func (s *DefaultSession) GetSubject() string { + if s == nil { + return "" + } + + return s.Subject +} diff --git a/storage/memory.go b/storage/memory.go index e9e941492..13e35a203 100644 --- a/storage/memory.go +++ b/storage/memory.go @@ -99,7 +99,7 @@ func (s *MemoryStore) CreateAuthorizeCodeSession(_ context.Context, code string, return nil } -func (s *MemoryStore) GetAuthorizeCodeSession(_ context.Context, code string, _ interface{}) (fosite.Requester, error) { +func (s *MemoryStore) GetAuthorizeCodeSession(_ context.Context, code string, _ fosite.Session) (fosite.Requester, error) { rel, ok := s.AuthorizeCodes[code] if !ok { return nil, fosite.ErrNotFound @@ -118,7 +118,7 @@ func (s *MemoryStore) CreateAccessTokenSession(_ context.Context, signature stri return nil } -func (s *MemoryStore) GetAccessTokenSession(_ context.Context, signature string, _ interface{}) (fosite.Requester, error) { +func (s *MemoryStore) GetAccessTokenSession(_ context.Context, signature string, _ fosite.Session) (fosite.Requester, error) { rel, ok := s.AccessTokens[signature] if !ok { return nil, fosite.ErrNotFound @@ -137,7 +137,7 @@ func (s *MemoryStore) CreateRefreshTokenSession(_ context.Context, signature str return nil } -func (s *MemoryStore) GetRefreshTokenSession(_ context.Context, signature string, _ interface{}) (fosite.Requester, error) { +func (s *MemoryStore) GetRefreshTokenSession(_ context.Context, signature string, _ fosite.Session) (fosite.Requester, error) { rel, ok := s.RefreshTokens[signature] if !ok { return nil, fosite.ErrNotFound