From 304453937792e337fa8e8829cb088f2c4addd91a Mon Sep 17 00:00:00 2001 From: lyric Date: Wed, 16 Nov 2016 14:55:20 +0800 Subject: [PATCH] add refreshing token config --- README.md | 33 +++++++++++++++++------------ example/server/server.go | 12 +++++------ manage/config.go | 14 +++++++++++-- manage/manage_test.go | 34 +++++++++++++++++++----------- manage/manager.go | 31 ++++++++++++++++++--------- server/server_test.go | 5 +++-- store/client.go | 45 +++++++++++++++++++++------------------- store/client_test.go | 23 ++++++++++++++++++++ 8 files changed, 131 insertions(+), 66 deletions(-) create mode 100644 store/client_test.go diff --git a/README.md b/README.md index 0e913ff..5cc7aca 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ import ( "net/http" "gopkg.in/oauth2.v3/manage" + "gopkg.in/oauth2.v3/models" "gopkg.in/oauth2.v3/server" "gopkg.in/oauth2.v3/store" ) @@ -52,8 +53,15 @@ func main() { manager := manage.NewDefaultManager() // token memory store manager.MustTokenStorage(store.NewMemoryTokenStore()) - // client test store - manager.MapClientStorage(store.NewTestClientStore()) + + // client memory store + clientStore := store.NewClientStore() + clientStore.Set("000000", &models.Client{ + ID: "000000", + Secret: "999999", + Domain: "http://localhost", + }) + manager.MapClientStorage(clientStore) srv := server.NewDefaultServer(manager) srv.SetAllowGetAccessRequest(true) @@ -76,7 +84,6 @@ func main() { http.ListenAndServe(":9096", nil) } - ``` ### Build and run @@ -89,12 +96,12 @@ $ ./server ### Open in your web browser ``` -http://localhost:9096/token?grant_type=client_credentials&client_id=1&client_secret=11&scope=read +http://localhost:9096/token?grant_type=client_credentials&client_id=000000&client_secret=999999&scope=read ``` ``` json { - "access_token": "ACPT7UYYNVWS2OAPFOHVUW", + "access_token": "J86XVRYSNFCFI233KXDL0Q", "expires_in": 7200, "scope": "read", "token_type": "Bearer" @@ -103,12 +110,12 @@ http://localhost:9096/token?grant_type=client_credentials&client_id=1&client_sec ## Features -* Easy to use -* Based on the [RFC 6749](https://tools.ietf.org/html/rfc6749) implementation -* Token storage support TTL -* Support custom extension field -* Support custom scope -* Support custom expiration time of the access token +* easy to use +* based on the [RFC 6749](https://tools.ietf.org/html/rfc6749) implementation +* token storage support TTL +* support custom expiration time of the access token +* support custom extension field +* support custom scope ## Example @@ -132,8 +139,8 @@ Copyright (c) 2016 Lyric [License-Image]: https://img.shields.io/npm/l/express.svg [Build-Status-Url]: https://travis-ci.org/go-oauth2/oauth2 [Build-Status-Image]: https://travis-ci.org/go-oauth2/oauth2.svg?branch=master -[Release-Url]: https://github.com/go-oauth2/oauth2/releases/tag/v3.5.3 -[Release-image]: http://img.shields.io/badge/release-v3.5.3-1eb0fc.svg +[Release-Url]: https://github.com/go-oauth2/oauth2/releases/tag/v3.6.0 +[Release-image]: http://img.shields.io/badge/release-v3.6.0-1eb0fc.svg [ReportCard-Url]: https://goreportcard.com/report/gopkg.in/oauth2.v3 [ReportCard-Image]: https://goreportcard.com/badge/gopkg.in/oauth2.v3 [GoDoc-Url]: https://godoc.org/gopkg.in/oauth2.v3 diff --git a/example/server/server.go b/example/server/server.go index 50c3964..47127d9 100644 --- a/example/server/server.go +++ b/example/server/server.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "log" "net/http" "net/url" @@ -11,7 +10,6 @@ import ( "gopkg.in/oauth2.v3/models" "gopkg.in/oauth2.v3/server" "gopkg.in/oauth2.v3/store" - "gopkg.in/session.v1" ) @@ -28,17 +26,19 @@ func main() { manager := manage.NewDefaultManager() // token store manager.MustTokenStorage(store.NewMemoryTokenStore()) - // client store - manager.MapClientStorage(store.NewTestClientStore(&models.Client{ + + clientStore := store.NewClientStore() + clientStore.Set("222222", &models.Client{ ID: "222222", Secret: "22222222", Domain: "http://localhost:9094", - })) + }) + manager.MapClientStorage(clientStore) srv := server.NewServer(server.NewConfig(), manager) srv.SetUserAuthorizationHandler(userAuthorizeHandler) srv.SetInternalErrorHandler(func(err error) { - fmt.Println("internal error:", err.Error()) + log.Println("[oauth2] error:", err.Error()) }) http.HandleFunc("/login", loginHandler) diff --git a/manage/config.go b/manage/config.go index ae1886b..5ef0c3f 100644 --- a/manage/config.go +++ b/manage/config.go @@ -10,8 +10,18 @@ type Config struct { RefreshTokenExp time.Duration // whether to generate the refreshing token IsGenerateRefresh bool - // whether to reset the refreshing expiration time +} + +// RefreshingConfig refreshing token config +type RefreshingConfig struct { + // whether to generate the refreshing token + IsGenerateRefresh bool + // whether to reset the refreshing create time IsResetRefreshTime bool + // whether to remove access token + IsRemoveAccess bool + // whether to remove refreshing token + IsRemoveRefreshing bool } // default configs @@ -21,5 +31,5 @@ var ( DefaultImplicitTokenCfg = &Config{AccessTokenExp: time.Hour * 1} DefaultPasswordTokenCfg = &Config{AccessTokenExp: time.Hour * 2, RefreshTokenExp: time.Hour * 24 * 7, IsGenerateRefresh: true} DefaultClientTokenCfg = &Config{AccessTokenExp: time.Hour * 2} - DefaultRefreshTokenCfg = &Config{} + DefaultRefreshTokenCfg = &RefreshingConfig{IsRemoveAccess: true, IsRemoveRefreshing: true} ) diff --git a/manage/manage_test.go b/manage/manage_test.go index 36e66e0..d153d6d 100644 --- a/manage/manage_test.go +++ b/manage/manage_test.go @@ -5,6 +5,7 @@ import ( "gopkg.in/oauth2.v3" "gopkg.in/oauth2.v3/manage" + "gopkg.in/oauth2.v3/models" "gopkg.in/oauth2.v3/store" . "github.com/smartystreets/goconvey/convey" @@ -13,9 +14,24 @@ import ( func TestManager(t *testing.T) { Convey("Manager test", t, func() { manager := manage.NewDefaultManager() - manager.MapClientStorage(store.NewTestClientStore()) + manager.MustTokenStorage(store.NewMemoryTokenStore()) + clientStore := store.NewClientStore() + clientStore.Set("1", &models.Client{ + ID: "1", + Secret: "11", + Domain: "http://localhost", + }) + manager.MapClientStorage(clientStore) + + tgr := &oauth2.TokenGenerateRequest{ + ClientID: "1", + UserID: "123456", + RedirectURI: "http://localhost/oauth2", + Scope: "all", + } + Convey("CheckInterface test", func() { err := manager.CheckInterface() So(err, ShouldBeNil) @@ -28,28 +44,22 @@ func TestManager(t *testing.T) { }) Convey("Token test", func() { - testManager(manager) + testManager(tgr, manager) }) }) } -func testManager(manager oauth2.Manager) { - reqParams := &oauth2.TokenGenerateRequest{ - ClientID: "1", - UserID: "123456", - RedirectURI: "http://localhost/oauth2", - Scope: "all", - } - cti, err := manager.GenerateAuthToken(oauth2.Code, reqParams) +func testManager(tgr *oauth2.TokenGenerateRequest, manager oauth2.Manager) { + cti, err := manager.GenerateAuthToken(oauth2.Code, tgr) So(err, ShouldBeNil) code := cti.GetCode() So(code, ShouldNotBeEmpty) atParams := &oauth2.TokenGenerateRequest{ - ClientID: reqParams.ClientID, + ClientID: tgr.ClientID, ClientSecret: "11", - RedirectURI: reqParams.RedirectURI, + RedirectURI: tgr.RedirectURI, Code: code, } ati, err := manager.GenerateAccessToken(oauth2.AuthorizationCode, atParams) diff --git a/manage/manager.go b/manage/manager.go index 40e8da1..013c4ec 100644 --- a/manage/manager.go +++ b/manage/manager.go @@ -35,6 +35,7 @@ type Manager struct { injector inject.Injector codeExp time.Duration gtcfg map[oauth2.GrantType]*Config + rcfg *RefreshingConfig validateURI ValidateURIHandler } @@ -52,8 +53,6 @@ func (m *Manager) grantConfig(gt oauth2.GrantType) *Config { return DefaultPasswordTokenCfg case oauth2.ClientCredentials: return DefaultClientTokenCfg - case oauth2.Refreshing: - return DefaultRefreshTokenCfg } return &Config{} } @@ -84,8 +83,8 @@ func (m *Manager) SetClientTokenCfg(cfg *Config) { } // SetRefreshTokenCfg set the refreshing token config -func (m *Manager) SetRefreshTokenCfg(cfg *Config) { - m.gtcfg[oauth2.Refreshing] = cfg +func (m *Manager) SetRefreshTokenCfg(cfg *RefreshingConfig) { + m.rcfg = cfg } // SetValidateURIHandler set the validates that RedirectURI is contained in baseURI @@ -282,6 +281,7 @@ func (m *Manager) GenerateAccessToken(gt oauth2.GrantType, tgr *oauth2.TokenGene tgr.AccessTokenExp = exp } } + cli, err := m.GetClient(tgr.ClientID) if err != nil { return @@ -297,6 +297,7 @@ func (m *Manager) GenerateAccessToken(gt oauth2.GrantType, tgr *oauth2.TokenGene CreateAt: time.Now(), } gcfg := m.grantConfig(gt) + av, rv, terr := gen.Token(td, gcfg.IsGenerateRefresh) if terr != nil { err = terr @@ -358,7 +359,10 @@ func (m *Manager) RefreshAccessToken(tgr *oauth2.TokenGenerateRequest) (accessTo CreateAt: time.Now(), } - rcfg := m.grantConfig(oauth2.Refreshing) + rcfg := DefaultRefreshTokenCfg + if v := m.rcfg; v != nil { + rcfg = v + } tv, rv, terr := gen.Token(td, rcfg.IsGenerateRefresh) if terr != nil { @@ -368,26 +372,33 @@ func (m *Manager) RefreshAccessToken(tgr *oauth2.TokenGenerateRequest) (accessTo ti.SetAccess(tv) ti.SetAccessCreateAt(td.CreateAt) + if rcfg.IsResetRefreshTime { ti.SetRefreshCreateAt(td.CreateAt) } + if scope := tgr.Scope; scope != "" { ti.SetScope(scope) } + if rv != "" { ti.SetRefresh(rv) } + if verr := stor.Create(ti); verr != nil { err = verr return } - // remove the old access token - if verr := stor.RemoveByAccess(oldAccess); verr != nil { - err = verr - return + if rcfg.IsRemoveAccess { + // remove the old access token + if verr := stor.RemoveByAccess(oldAccess); verr != nil { + err = verr + return + } } - if rv != "" { + + if rcfg.IsRemoveRefreshing && rv != "" { // remove the old refresh token if verr := stor.RemoveByRefresh(oldRefresh); verr != nil { err = verr diff --git a/server/server_test.go b/server/server_test.go index 33fc9c2..a41e8cc 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -30,11 +30,13 @@ func init() { } func clientStore(domain string) oauth2.ClientStore { - return store.NewTestClientStore(&models.Client{ + clientStore := store.NewClientStore() + clientStore.Set(clientID, &models.Client{ ID: clientID, Secret: clientSecret, Domain: domain, }) + return clientStore } func testServer(t *testing.T, w http.ResponseWriter, r *http.Request) { @@ -221,7 +223,6 @@ func TestRefreshing(t *testing.T) { defer csrv.Close() manager.MapClientStorage(clientStore(csrv.URL)) - manager.SetRefreshTokenCfg(&manage.Config{IsGenerateRefresh: false}) srv = server.NewDefaultServer(manager) srv.SetUserAuthorizationHandler(func(w http.ResponseWriter, r *http.Request) (userID string, err error) { userID = "000000" diff --git a/store/client.go b/store/client.go index c300364..e71a02d 100644 --- a/store/client.go +++ b/store/client.go @@ -1,37 +1,40 @@ package store import ( + "errors" + "sync" + "gopkg.in/oauth2.v3" - "gopkg.in/oauth2.v3/models" ) -// NewTestClientStore create to client information store instance -func NewTestClientStore(clients ...*models.Client) oauth2.ClientStore { - data := map[string]*models.Client{ - "1": &models.Client{ - ID: "1", - Secret: "11", - Domain: "http://localhost", - UserID: "000000", - }, - } - for _, cli := range clients { - data[cli.ID] = cli - } - return &TestClientStore{ - data: data, +func NewClientStore() *ClientStore { + return &ClientStore{ + data: make(map[string]oauth2.ClientInfo), } } -// TestClientStore client information store -type TestClientStore struct { - data map[string]*models.Client +// ClientStore client information store +type ClientStore struct { + sync.RWMutex + data map[string]oauth2.ClientInfo } // GetByID according to the ID for the client information -func (ts *TestClientStore) GetByID(id string) (cli oauth2.ClientInfo, err error) { - if c, ok := ts.data[id]; ok { +func (cs *ClientStore) GetByID(id string) (cli oauth2.ClientInfo, err error) { + cs.RLock() + defer cs.RUnlock() + if c, ok := cs.data[id]; ok { cli = c + return } + err = errors.New("not found") + return +} + +// Set set client information +func (cs *ClientStore) Set(id string, cli oauth2.ClientInfo) (err error) { + cs.Lock() + defer cs.Unlock() + cs.data[id] = cli return } diff --git a/store/client_test.go b/store/client_test.go new file mode 100644 index 0000000..4c45665 --- /dev/null +++ b/store/client_test.go @@ -0,0 +1,23 @@ +package store_test + +import ( + "testing" + + "gopkg.in/oauth2.v3/models" + "gopkg.in/oauth2.v3/store" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestClientStore(t *testing.T) { + Convey("Test client store", t, func() { + clientStore := store.NewClientStore() + + err := clientStore.Set("1", &models.Client{ID: "1", Secret: "2"}) + So(err, ShouldBeNil) + + cli, err := clientStore.GetByID("1") + So(err, ShouldBeNil) + So(cli.GetID(), ShouldEqual, "1") + }) +}