1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package credentials
16
17 import (
18 "context"
19 "encoding/json"
20 "errors"
21 "fmt"
22 "net/http"
23 "os"
24 "time"
25
26 "cloud.google.com/go/auth"
27 "cloud.google.com/go/auth/internal"
28 "cloud.google.com/go/auth/internal/credsfile"
29 "cloud.google.com/go/compute/metadata"
30 )
31
32 const (
33
34 jwtTokenURL = "https://oauth2.googleapis.com/token"
35
36
37 googleAuthURL = "https://accounts.google.com/o/oauth2/auth"
38 googleTokenURL = "https://oauth2.googleapis.com/token"
39
40
41 adcSetupURL = "https://cloud.google.com/docs/authentication/external/set-up-adc"
42 )
43
44 var (
45
46 allowOnGCECheck = true
47 )
48
49
50 func OnGCE() bool {
51
52 return allowOnGCECheck && metadata.OnGCE()
53 }
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 func DetectDefault(opts *DetectOptions) (*auth.Credentials, error) {
73 if err := opts.validate(); err != nil {
74 return nil, err
75 }
76 if opts.CredentialsJSON != nil {
77 return readCredentialsFileJSON(opts.CredentialsJSON, opts)
78 }
79 if opts.CredentialsFile != "" {
80 return readCredentialsFile(opts.CredentialsFile, opts)
81 }
82 if filename := os.Getenv(credsfile.GoogleAppCredsEnvVar); filename != "" {
83 if creds, err := readCredentialsFile(filename, opts); err == nil {
84 return creds, err
85 }
86 }
87
88 fileName := credsfile.GetWellKnownFileName()
89 if b, err := os.ReadFile(fileName); err == nil {
90 return readCredentialsFileJSON(b, opts)
91 }
92
93 if OnGCE() {
94 return auth.NewCredentials(&auth.CredentialsOptions{
95 TokenProvider: computeTokenProvider(opts.EarlyTokenRefresh, opts.Scopes...),
96 ProjectIDProvider: auth.CredentialsPropertyFunc(func(context.Context) (string, error) {
97 return metadata.ProjectID()
98 }),
99 UniverseDomainProvider: &internal.ComputeUniverseDomainProvider{},
100 }), nil
101 }
102
103 return nil, fmt.Errorf("credentials: could not find default credentials. See %v for more information", adcSetupURL)
104 }
105
106
107 type DetectOptions struct {
108
109
110
111 Scopes []string
112
113
114 Audience string
115
116
117 Subject string
118
119
120 EarlyTokenRefresh time.Duration
121
122
123
124 AuthHandlerOptions *auth.AuthorizationHandlerOptions
125
126
127
128 TokenURL string
129
130
131 STSAudience string
132
133
134
135 CredentialsFile string
136
137
138
139 CredentialsJSON []byte
140
141
142
143 UseSelfSignedJWT bool
144
145
146 Client *http.Client
147
148
149
150 UniverseDomain string
151 }
152
153 func (o *DetectOptions) validate() error {
154 if o == nil {
155 return errors.New("credentials: options must be provided")
156 }
157 if len(o.Scopes) > 0 && o.Audience != "" {
158 return errors.New("credentials: both scopes and audience were provided")
159 }
160 if len(o.CredentialsJSON) > 0 && o.CredentialsFile != "" {
161 return errors.New("credentials: both credentials file and JSON were provided")
162 }
163 return nil
164 }
165
166 func (o *DetectOptions) tokenURL() string {
167 if o.TokenURL != "" {
168 return o.TokenURL
169 }
170 return googleTokenURL
171 }
172
173 func (o *DetectOptions) scopes() []string {
174 scopes := make([]string, len(o.Scopes))
175 copy(scopes, o.Scopes)
176 return scopes
177 }
178
179 func (o *DetectOptions) client() *http.Client {
180 if o.Client != nil {
181 return o.Client
182 }
183 return internal.CloneDefaultClient()
184 }
185
186 func readCredentialsFile(filename string, opts *DetectOptions) (*auth.Credentials, error) {
187 b, err := os.ReadFile(filename)
188 if err != nil {
189 return nil, err
190 }
191 return readCredentialsFileJSON(b, opts)
192 }
193
194 func readCredentialsFileJSON(b []byte, opts *DetectOptions) (*auth.Credentials, error) {
195
196 config := clientCredConfigFromJSON(b, opts)
197 if config != nil {
198 if config.AuthHandlerOpts == nil {
199 return nil, errors.New("credentials: auth handler must be specified for this credential filetype")
200 }
201 tp, err := auth.New3LOTokenProvider(config)
202 if err != nil {
203 return nil, err
204 }
205 return auth.NewCredentials(&auth.CredentialsOptions{
206 TokenProvider: tp,
207 JSON: b,
208 }), nil
209 }
210 return fileCredentials(b, opts)
211 }
212
213 func clientCredConfigFromJSON(b []byte, opts *DetectOptions) *auth.Options3LO {
214 var creds credsfile.ClientCredentialsFile
215 var c *credsfile.Config3LO
216 if err := json.Unmarshal(b, &creds); err != nil {
217 return nil
218 }
219 switch {
220 case creds.Web != nil:
221 c = creds.Web
222 case creds.Installed != nil:
223 c = creds.Installed
224 default:
225 return nil
226 }
227 if len(c.RedirectURIs) < 1 {
228 return nil
229 }
230 var handleOpts *auth.AuthorizationHandlerOptions
231 if opts.AuthHandlerOptions != nil {
232 handleOpts = &auth.AuthorizationHandlerOptions{
233 Handler: opts.AuthHandlerOptions.Handler,
234 State: opts.AuthHandlerOptions.State,
235 PKCEOpts: opts.AuthHandlerOptions.PKCEOpts,
236 }
237 }
238 return &auth.Options3LO{
239 ClientID: c.ClientID,
240 ClientSecret: c.ClientSecret,
241 RedirectURL: c.RedirectURIs[0],
242 Scopes: opts.scopes(),
243 AuthURL: c.AuthURI,
244 TokenURL: c.TokenURI,
245 Client: opts.client(),
246 EarlyTokenExpiry: opts.EarlyTokenRefresh,
247 AuthHandlerOpts: handleOpts,
248
249
250 AuthStyle: auth.StyleInParams,
251 }
252 }
253
View as plain text