github.com/coreos/mantle@v0.13.0/auth/google.go (about) 1 // Copyright 2014 CoreOS, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package auth provides Google oauth2 and Azure credential bindings for mantle. 16 package auth 17 18 import ( 19 "encoding/json" 20 "fmt" 21 "log" 22 "net/http" 23 "os" 24 "os/user" 25 "path/filepath" 26 27 "golang.org/x/oauth2" 28 "golang.org/x/oauth2/google" 29 ) 30 31 // client registered under the coreos-gce-testing project as 'mantle' 32 var conf = oauth2.Config{ 33 ClientID: "1053977531921-s05q1c3kf23pdq86bmqv5qcga21c0ra3.apps.googleusercontent.com", 34 ClientSecret: "pgt0XUBTCfMwsqf2Q6cVdxTO", 35 Endpoint: oauth2.Endpoint{ 36 AuthURL: "https://accounts.google.com/o/oauth2/auth", 37 TokenURL: "https://accounts.google.com/o/oauth2/token", 38 }, 39 RedirectURL: "urn:ietf:wg:oauth:2.0:oob", 40 Scopes: []string{"https://www.googleapis.com/auth/devstorage.full_control", 41 "https://www.googleapis.com/auth/compute"}, 42 } 43 44 func writeCache(cachePath string, tok *oauth2.Token) error { 45 file, err := os.OpenFile(cachePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) 46 if err != nil { 47 return err 48 } 49 defer file.Close() 50 51 if err := json.NewEncoder(file).Encode(tok); err != nil { 52 return err 53 } 54 return nil 55 } 56 57 func readCache(cachePath string) (*oauth2.Token, error) { 58 file, err := os.Open(cachePath) 59 if err != nil { 60 return nil, err 61 } 62 defer file.Close() 63 64 tok := &oauth2.Token{} 65 if err := json.NewDecoder(file).Decode(tok); err != nil { 66 return nil, err 67 } 68 69 // make sure token is refreshable 70 if tok != nil && !tok.Valid() { 71 ts := conf.TokenSource(oauth2.NoContext, tok) 72 tok, err = ts.Token() 73 if err != nil || !tok.Valid() { 74 fmt.Printf("Could not refresh cached token: %v\n", err) 75 return nil, nil 76 } 77 } 78 return tok, nil 79 } 80 81 func getToken() (*oauth2.Token, error) { 82 userInfo, err := user.Current() 83 if err != nil { 84 return nil, err 85 } 86 87 cachePath := filepath.Join(userInfo.HomeDir, ".mantle-cache-google.json") 88 tok, err := readCache(cachePath) 89 if err != nil { 90 log.Printf("Error reading google token cache file: %v", err) 91 } 92 if tok == nil { 93 url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline) 94 fmt.Printf("Visit the URL for the auth dialog: %v\n", url) 95 fmt.Print("Enter token: ") 96 97 var code string 98 if _, err := fmt.Scan(&code); err != nil { 99 return nil, err 100 } 101 tok, err = conf.Exchange(oauth2.NoContext, code) 102 if err != nil { 103 return nil, err 104 } 105 err = writeCache(cachePath, tok) 106 if err != nil { 107 log.Printf("Error writing google token cache file: %v", err) 108 } 109 } 110 return tok, nil 111 } 112 113 // GoogleClient provides an http.Client authorized with an oauth2 token 114 // that is automatically cached and refreshed from a file named 115 // '.mantle-cache-google.json'. This uses interactive oauth2 116 // authorization and requires a user follow to follow a web link and 117 // paste in an authorization token. 118 func GoogleClient() (*http.Client, error) { 119 tok, err := getToken() 120 if err != nil { 121 return nil, err 122 } 123 return conf.Client(oauth2.NoContext, tok), nil 124 } 125 126 // GoogleTokenSource provides an outh2.TokenSource authorized in the 127 // same manner as GoogleClient. 128 func GoogleTokenSource() (oauth2.TokenSource, error) { 129 tok, err := getToken() 130 if err != nil { 131 return nil, err 132 } 133 return conf.TokenSource(oauth2.NoContext, tok), nil 134 } 135 136 // GoogleServiceClient fetchs a token from Google Compute Engine's 137 // metadata service. This should be used on GCE vms. The Default account 138 // is used. 139 func GoogleServiceClient() *http.Client { 140 return &http.Client{ 141 Transport: &oauth2.Transport{ 142 Source: google.ComputeTokenSource(""), 143 }, 144 } 145 } 146 147 // GoogleServiceTokenSource provides an oauth2.TokenSource authorized in 148 // the same manner as GoogleServiceClient(). 149 func GoogleServiceTokenSource() oauth2.TokenSource { 150 return google.ComputeTokenSource("") 151 } 152 153 // GoogleClientFromJSONKey provides an http.Client authorized with an 154 // oauth2 token retrieved using a Google Developers service account's 155 // private JSON key file. 156 func GoogleClientFromJSONKey(jsonKey []byte, scope ...string) (*http.Client, error) { 157 if scope == nil { 158 scope = conf.Scopes 159 } 160 jwtConf, err := google.JWTConfigFromJSON(jsonKey, scope...) 161 if err != nil { 162 return nil, err 163 } 164 165 return jwtConf.Client(oauth2.NoContext), nil 166 167 } 168 169 // GoogleTokenSourceFromJSONKey provides an oauth2.TokenSource 170 // authorized in the same manner as GoogleClientFromJSONKey. 171 func GoogleTokenSourceFromJSONKey(jsonKey []byte, scope ...string) (oauth2.TokenSource, error) { 172 if scope == nil { 173 scope = conf.Scopes 174 } 175 176 jwtConf, err := google.JWTConfigFromJSON(jsonKey, scope...) 177 if err != nil { 178 return nil, err 179 } 180 181 return jwtConf.TokenSource(oauth2.NoContext), nil 182 }