Remove BPA from Makefile
[icn.git] / cmd / bpa-operator / vendor / golang.org / x / oauth2 / google / google.go
1 // Copyright 2014 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package google
6
7 import (
8         "context"
9         "encoding/json"
10         "errors"
11         "fmt"
12         "net/url"
13         "strings"
14         "time"
15
16         "cloud.google.com/go/compute/metadata"
17         "golang.org/x/oauth2"
18         "golang.org/x/oauth2/jwt"
19 )
20
21 // Endpoint is Google's OAuth 2.0 endpoint.
22 var Endpoint = oauth2.Endpoint{
23         AuthURL:   "https://accounts.google.com/o/oauth2/auth",
24         TokenURL:  "https://oauth2.googleapis.com/token",
25         AuthStyle: oauth2.AuthStyleInParams,
26 }
27
28 // JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow.
29 const JWTTokenURL = "https://oauth2.googleapis.com/token"
30
31 // ConfigFromJSON uses a Google Developers Console client_credentials.json
32 // file to construct a config.
33 // client_credentials.json can be downloaded from
34 // https://console.developers.google.com, under "Credentials". Download the Web
35 // application credentials in the JSON format and provide the contents of the
36 // file as jsonKey.
37 func ConfigFromJSON(jsonKey []byte, scope ...string) (*oauth2.Config, error) {
38         type cred struct {
39                 ClientID     string   `json:"client_id"`
40                 ClientSecret string   `json:"client_secret"`
41                 RedirectURIs []string `json:"redirect_uris"`
42                 AuthURI      string   `json:"auth_uri"`
43                 TokenURI     string   `json:"token_uri"`
44         }
45         var j struct {
46                 Web       *cred `json:"web"`
47                 Installed *cred `json:"installed"`
48         }
49         if err := json.Unmarshal(jsonKey, &j); err != nil {
50                 return nil, err
51         }
52         var c *cred
53         switch {
54         case j.Web != nil:
55                 c = j.Web
56         case j.Installed != nil:
57                 c = j.Installed
58         default:
59                 return nil, fmt.Errorf("oauth2/google: no credentials found")
60         }
61         if len(c.RedirectURIs) < 1 {
62                 return nil, errors.New("oauth2/google: missing redirect URL in the client_credentials.json")
63         }
64         return &oauth2.Config{
65                 ClientID:     c.ClientID,
66                 ClientSecret: c.ClientSecret,
67                 RedirectURL:  c.RedirectURIs[0],
68                 Scopes:       scope,
69                 Endpoint: oauth2.Endpoint{
70                         AuthURL:  c.AuthURI,
71                         TokenURL: c.TokenURI,
72                 },
73         }, nil
74 }
75
76 // JWTConfigFromJSON uses a Google Developers service account JSON key file to read
77 // the credentials that authorize and authenticate the requests.
78 // Create a service account on "Credentials" for your project at
79 // https://console.developers.google.com to download a JSON key file.
80 func JWTConfigFromJSON(jsonKey []byte, scope ...string) (*jwt.Config, error) {
81         var f credentialsFile
82         if err := json.Unmarshal(jsonKey, &f); err != nil {
83                 return nil, err
84         }
85         if f.Type != serviceAccountKey {
86                 return nil, fmt.Errorf("google: read JWT from JSON credentials: 'type' field is %q (expected %q)", f.Type, serviceAccountKey)
87         }
88         scope = append([]string(nil), scope...) // copy
89         return f.jwtConfig(scope), nil
90 }
91
92 // JSON key file types.
93 const (
94         serviceAccountKey  = "service_account"
95         userCredentialsKey = "authorized_user"
96 )
97
98 // credentialsFile is the unmarshalled representation of a credentials file.
99 type credentialsFile struct {
100         Type string `json:"type"` // serviceAccountKey or userCredentialsKey
101
102         // Service Account fields
103         ClientEmail  string `json:"client_email"`
104         PrivateKeyID string `json:"private_key_id"`
105         PrivateKey   string `json:"private_key"`
106         TokenURL     string `json:"token_uri"`
107         ProjectID    string `json:"project_id"`
108
109         // User Credential fields
110         // (These typically come from gcloud auth.)
111         ClientSecret string `json:"client_secret"`
112         ClientID     string `json:"client_id"`
113         RefreshToken string `json:"refresh_token"`
114 }
115
116 func (f *credentialsFile) jwtConfig(scopes []string) *jwt.Config {
117         cfg := &jwt.Config{
118                 Email:        f.ClientEmail,
119                 PrivateKey:   []byte(f.PrivateKey),
120                 PrivateKeyID: f.PrivateKeyID,
121                 Scopes:       scopes,
122                 TokenURL:     f.TokenURL,
123         }
124         if cfg.TokenURL == "" {
125                 cfg.TokenURL = JWTTokenURL
126         }
127         return cfg
128 }
129
130 func (f *credentialsFile) tokenSource(ctx context.Context, scopes []string) (oauth2.TokenSource, error) {
131         switch f.Type {
132         case serviceAccountKey:
133                 cfg := f.jwtConfig(scopes)
134                 return cfg.TokenSource(ctx), nil
135         case userCredentialsKey:
136                 cfg := &oauth2.Config{
137                         ClientID:     f.ClientID,
138                         ClientSecret: f.ClientSecret,
139                         Scopes:       scopes,
140                         Endpoint:     Endpoint,
141                 }
142                 tok := &oauth2.Token{RefreshToken: f.RefreshToken}
143                 return cfg.TokenSource(ctx, tok), nil
144         case "":
145                 return nil, errors.New("missing 'type' field in credentials")
146         default:
147                 return nil, fmt.Errorf("unknown credential type: %q", f.Type)
148         }
149 }
150
151 // ComputeTokenSource returns a token source that fetches access tokens
152 // from Google Compute Engine (GCE)'s metadata server. It's only valid to use
153 // this token source if your program is running on a GCE instance.
154 // If no account is specified, "default" is used.
155 // If no scopes are specified, a set of default scopes are automatically granted.
156 // Further information about retrieving access tokens from the GCE metadata
157 // server can be found at https://cloud.google.com/compute/docs/authentication.
158 func ComputeTokenSource(account string, scope ...string) oauth2.TokenSource {
159         return oauth2.ReuseTokenSource(nil, computeSource{account: account, scopes: scope})
160 }
161
162 type computeSource struct {
163         account string
164         scopes  []string
165 }
166
167 func (cs computeSource) Token() (*oauth2.Token, error) {
168         if !metadata.OnGCE() {
169                 return nil, errors.New("oauth2/google: can't get a token from the metadata service; not running on GCE")
170         }
171         acct := cs.account
172         if acct == "" {
173                 acct = "default"
174         }
175         tokenURI := "instance/service-accounts/" + acct + "/token"
176         if len(cs.scopes) > 0 {
177                 v := url.Values{}
178                 v.Set("scopes", strings.Join(cs.scopes, ","))
179                 tokenURI = tokenURI + "?" + v.Encode()
180         }
181         tokenJSON, err := metadata.Get(tokenURI)
182         if err != nil {
183                 return nil, err
184         }
185         var res struct {
186                 AccessToken  string `json:"access_token"`
187                 ExpiresInSec int    `json:"expires_in"`
188                 TokenType    string `json:"token_type"`
189         }
190         err = json.NewDecoder(strings.NewReader(tokenJSON)).Decode(&res)
191         if err != nil {
192                 return nil, fmt.Errorf("oauth2/google: invalid token JSON from metadata: %v", err)
193         }
194         if res.ExpiresInSec == 0 || res.AccessToken == "" {
195                 return nil, fmt.Errorf("oauth2/google: incomplete token received from metadata")
196         }
197         return &oauth2.Token{
198                 AccessToken: res.AccessToken,
199                 TokenType:   res.TokenType,
200                 Expiry:      time.Now().Add(time.Duration(res.ExpiresInSec) * time.Second),
201         }, nil
202 }