Skip to content

Commit

Permalink
Support token cache for OAuth and JWT token
Browse files Browse the repository at this point in the history
  • Loading branch information
shinfan committed Aug 29, 2018
1 parent a9fa4f1 commit ac3cdab
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 10 deletions.
9 changes: 9 additions & 0 deletions go/oauth2l/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,12 @@ $ oauth2l test ya29.justkiddingmadethisoneup
$ echo $?
1
```

### reset

Reset all tokens cached locally. We cache previously retrieved tokens in the
file `~/.oauth2l.token`.

```bash
$ oauth2l reset
```
6 changes: 4 additions & 2 deletions go/oauth2l/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ var (
)

func help() {
fmt.Println("Usage: oauth2l {fetch|header|info|test} " +
fmt.Println("Usage: oauth2l {fetch|header|info|test|reset} " +
"[--jwt] [--json] [--sso] [--ssocli] {scope|aud|email}")
}

Expand Down Expand Up @@ -69,7 +69,7 @@ func parseScopes(scopes []string) string {
}

func main() {
if len(os.Args) < 3 {
if len(os.Args) < 2 {
help()
return
}
Expand Down Expand Up @@ -144,6 +144,8 @@ func main() {
}
} else if task, ok := infoTasks[cmd]; ok {
task(flagSet.Args()[len(flagSet.Args()) - 1])
} else if cmd == "reset" {
util.Reset()
} else {
// Unknown command, print usage.
help()
Expand Down
131 changes: 131 additions & 0 deletions go/oauth2l/util/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//
// Copyright 2018 Google Inc.
//
// 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 util

import (
"log"
"io/ioutil"
"encoding/json"
"os"
"github.com/google/oauth2l/go/sgauth"
"os/user"
)

const (
cacheFileName = ".oauth2l.token"
)

// The key struct that used to identify an auth token fetch operation.
type CacheKey struct {
// The JSON credentials content downloaded from Google Cloud Console.
CredentialsJSON string
// If specified, use OAuth. Otherwise, JWT.
Scope string
// The audience field for JWT auth
Audience string
// The Google API key
APIKey string
}

func LookupCache(settings *sgauth.Settings) (*sgauth.Token, error) {
var token sgauth.Token
var cache, err = loadCache()
if err != nil {
return nil, err
}
key, err := json.Marshal(createKey(settings))
if err != nil {
return nil, err
}
val := cache[string(key)]
err = json.Unmarshal(val, &token)
if err != nil {
return nil, err
}
return &token, nil
}

func InsertCache(settings *sgauth.Settings, token *sgauth.Token) error {
var cache, err = loadCache()
if err != nil {
return err
}
val, err := json.Marshal(*token)
if err != nil {
return err
}
key, err := json.Marshal(createKey(settings))
if err != nil {
return err
}
cache[string(key)] = val
data, err := json.Marshal(cache)
if err != nil {
return err
}
return ioutil.WriteFile(cacheLocation(), data, 0666)
}

func ClearCache() error {
if _, err := os.Stat(cacheLocation()); os.IsNotExist(err) {
// Noop if file does not exist.
return nil
}
return os.Remove(cacheLocation())
}

func loadCache() (map[string][]byte, error) {
if _, err := os.Stat(cacheLocation()); os.IsNotExist(err) {
// Create the cache file if not existing.
f, err := os.OpenFile(cacheLocation(), os.O_RDONLY|os.O_CREATE, 0666)
if err != nil {
log.Fatal(err)
return nil, err
}
f.Close()
}
data, err := ioutil.ReadFile(cacheLocation())
if err != nil {
log.Fatal(err)
return nil, err
}
m := map[string][]byte{}
if len(data) > 0 {
err = json.Unmarshal(data, &m)
if err != nil {
log.Fatal(err)
return nil, err
}
}
return m, nil
}

func cacheLocation() string {
usr, err := user.Current()
if err != nil {
log.Fatal( err )
}
return usr.HomeDir + "/" + cacheFileName
}

func createKey(settings *sgauth.Settings) CacheKey {
return CacheKey{
CredentialsJSON: settings.CredentialsJSON,
Scope: settings.Scope,
Audience: settings.Audience,
APIKey: settings.APIKey,
}
}

34 changes: 26 additions & 8 deletions go/oauth2l/util/tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,14 @@ func PrintToken(tokenType string, token string, headerFormat bool) {
// Fetches and prints the token in plain text with the given settings
// using Google Authenticator.
func Fetch(settings *sgauth.Settings) {
token, err := sgauth.FetchToken(context.Background(), settings)
if err != nil {
fmt.Println(err)
}
token := fetchToken(settings)
PrintToken(token.TokenType, token.AccessToken, false)
}

// Fetches and prints the token in header format with the given settings
// using Google Authenticator.
func Header(settings *sgauth.Settings) {
token, err := sgauth.FetchToken(context.Background(), settings)
if err != nil {
fmt.Println(err)
}
token := fetchToken(settings)
PrintToken(token.TokenType, token.AccessToken, true)
}

Expand All @@ -79,6 +73,14 @@ func Test(token string) {
}
}

// Reset the cache
func Reset() {
err := ClearCache()
if err != nil {
fmt.Print(err)
}
}

func getTokenInfo(token string) (string, error) {
c := http.DefaultClient
resp, err := c.Get(googleTokenInfoURLPrefix + token)
Expand All @@ -91,3 +93,19 @@ func getTokenInfo(token string) (string, error) {
}
return string(data), err
}

func fetchToken(settings *sgauth.Settings) (*sgauth.Token) {
token, _ := LookupCache(settings)
if token != nil {
return token
}
token, err := sgauth.FetchToken(context.Background(), settings)
if err != nil {
fmt.Println(err)
}
err = InsertCache(settings, token)
if err != nil {
fmt.Println(err)
}
return token
}

0 comments on commit ac3cdab

Please sign in to comment.