From 227bfbf02f8ec12f97ecad7273d7fd2b58af42c6 Mon Sep 17 00:00:00 2001 From: Burcu Dogan Date: Mon, 19 May 2014 00:14:56 +0200 Subject: [PATCH 1/2] Introduction of cache interface and a file cacher. --- cache.go | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ jwt.go | 6 ++++++ oauth2.go | 8 ++++++++ 3 files changed, 72 insertions(+) create mode 100644 cache.go diff --git a/cache.go b/cache.go new file mode 100644 index 0000000..fae7217 --- /dev/null +++ b/cache.go @@ -0,0 +1,58 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oauth2 + +import ( + "encoding/json" + "io/ioutil" +) + +// Cache represents a token cacher. +type Cache interface { + // Read reads a cache token from the specified file. + Read() (token *Token, err error) + // Write writes a token to the specified file. + Write(token *Token) (err error) +} + +// NewFileCache creates a new file cache. +func NewFileCache(filename string) *FileCache { + return &FileCache{filename: filename} +} + +// FileCache represents a file based token cacher. +type FileCache struct { + filename string +} + +// Read reads a cache token from the specified file. +func (f *FileCache) Read() (token *Token, err error) { + data, err := ioutil.ReadFile(f.filename) + if err != nil { + return nil, err + } + token = &Token{} + err = json.Unmarshal(data, &token) + return token, err +} + +// Write writes a token to the specified file. +func (f *FileCache) Write(token *Token) error { + data, err := json.Marshal(token) + if err != nil { + return err + } + return ioutil.WriteFile(f.filename, data, 0644) +} diff --git a/jwt.go b/jwt.go index b5f25e3..b72e49a 100644 --- a/jwt.go +++ b/jwt.go @@ -66,6 +66,7 @@ type JWTConfig struct { opts *JWTOptions aud string signature []byte + cache Cache } // Options returns JWT options. @@ -153,3 +154,8 @@ func (c *JWTConfig) FetchToken(existing *Token) (token *Token, err error) { token.Expiry = time.Now().Add(time.Duration(b.ExpiresIn) * time.Second) return } + +// Cache returns a cache if specified, otherwise nil. +func (c *JWTConfig) Cache() Cache { + return c.cache +} diff --git a/oauth2.go b/oauth2.go index ad18c48..d5761ba 100644 --- a/oauth2.go +++ b/oauth2.go @@ -74,6 +74,7 @@ type TokenFetcher interface { // If the implementation doesn't know how to retrieve a new token, // it returns an error. FetchToken(existing *Token) (*Token, error) + Cache() Cache } // Options represents options to provide OAuth 2.0 client credentials @@ -138,6 +139,8 @@ type Config struct { authURL string // TokenURL is the URL used to retrieve OAuth tokens. tokenURL string + + cache Cache } // Options returns options. @@ -224,6 +227,11 @@ func (c *Config) FetchToken(existing *Token) (*Token, error) { return existing, err } +// Cache returns a cache if specified, otherwise nil. +func (c *Config) Cache() Cache { + return c.cache +} + // Checks if all required configuration fields have non-zero values. func (c *Config) validate() error { if c.opts.ClientID == "" { From 2e00ad50b11c6884953da5c8b778745df4f748ab Mon Sep 17 00:00:00 2001 From: Burcu Dogan Date: Mon, 19 May 2014 00:20:42 +0200 Subject: [PATCH 2/2] Authorized transport should read from cache and write to cache. --- transport.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/transport.go b/transport.go index ee104c5..67c67c5 100644 --- a/transport.go +++ b/transport.go @@ -104,6 +104,12 @@ func NewAuthorizedTransport(fetcher TokenFetcher, token *Token) Transport { // If token is expired, tries to refresh/fetch a new token. func (t *authorizedTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) { token := t.Token() + cache := t.fetcher.Cache() + + if token == nil && cache != nil { + // Try to read from cache initially + token, _ := cache.Read() + } if token == nil || token.Expired() { // Check if the token is refreshable. // If token is refreshable, don't return an error, @@ -169,6 +175,11 @@ func (t *authorizedTransport) RefreshToken() error { } t.token = token + cache := t.fetcher.Cache() + if cache != nil { + cache.Write(token) + } + return nil }