1 // Copyright 2023 Google LLC 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 idtoken 16 17 import ( 18 "errors" 19 "fmt" 20 "net/http" 21 "os" 22 23 "cloud.google.com/go/auth" 24 "cloud.google.com/go/auth/internal" 25 "cloud.google.com/go/auth/internal/credsfile" 26 "cloud.google.com/go/compute/metadata" 27 ) 28 29 // ComputeTokenFormat dictates the the token format when requesting an ID token 30 // from the compute metadata service. 31 type ComputeTokenFormat int 32 33 const ( 34 // ComputeTokenFormatDefault means the same as [ComputeTokenFormatFull]. 35 ComputeTokenFormatDefault ComputeTokenFormat = iota 36 // ComputeTokenFormatStandard mean only standard JWT fields will be included 37 // in the token. 38 ComputeTokenFormatStandard 39 // ComputeTokenFormatFull means the token will include claims about the 40 // virtual machine instance and its project. 41 ComputeTokenFormatFull 42 // ComputeTokenFormatFullWithLicense means the same as 43 // [ComputeTokenFormatFull] with the addition of claims about licenses 44 // associated with the instance. 45 ComputeTokenFormatFullWithLicense 46 ) 47 48 // Options for the configuration of creation of an ID token with 49 // [NewCredentials]. 50 type Options struct { 51 // Audience is the `aud` field for the token, such as an API endpoint the 52 // token will grant access to. Required. 53 Audience string 54 // ComputeTokenFormat dictates the the token format when requesting an ID 55 // token from the compute metadata service. Optional. 56 ComputeTokenFormat ComputeTokenFormat 57 // CustomClaims specifies private non-standard claims for an ID token. 58 // Optional. 59 CustomClaims map[string]interface{} 60 61 // CredentialsFile overrides detection logic and sources a credential file 62 // from the provided filepath. Optional. 63 CredentialsFile string 64 // CredentialsJSON overrides detection logic and uses the JSON bytes as the 65 // source for the credential. Optional. 66 CredentialsJSON []byte 67 // Client configures the underlying client used to make network requests 68 // when fetching tokens. If provided this should be a fully authenticated 69 // client. Optional. 70 Client *http.Client 71 } 72 73 func (o *Options) client() *http.Client { 74 if o == nil || o.Client == nil { 75 return internal.CloneDefaultClient() 76 } 77 return o.Client 78 } 79 80 func (o *Options) validate() error { 81 if o == nil { 82 return errors.New("idtoken: opts must be provided") 83 } 84 if o.Audience == "" { 85 return errors.New("idtoken: audience must be specified") 86 } 87 return nil 88 } 89 90 // NewCredentials creates a [cloud.google.com/go/auth.Credentials] that 91 // returns ID tokens configured by the opts provided. The parameter 92 // opts.Audience may not be empty. 93 func NewCredentials(opts *Options) (*auth.Credentials, error) { 94 if err := opts.validate(); err != nil { 95 return nil, err 96 } 97 if b := opts.jsonBytes(); b != nil { 98 return credsFromBytes(b, opts) 99 } 100 if metadata.OnGCE() { 101 return computeCredentials(opts) 102 } 103 return nil, fmt.Errorf("idtoken: couldn't find any credentials") 104 } 105 106 func (o *Options) jsonBytes() []byte { 107 if o.CredentialsJSON != nil { 108 return o.CredentialsJSON 109 } 110 var fnOverride string 111 if o != nil { 112 fnOverride = o.CredentialsFile 113 } 114 filename := credsfile.GetFileNameFromEnv(fnOverride) 115 if filename != "" { 116 b, _ := os.ReadFile(filename) 117 return b 118 } 119 return nil 120 } 121 122 // Payload represents a decoded payload of an ID token. 123 type Payload struct { 124 Issuer string `json:"iss"` 125 Audience string `json:"aud"` 126 Expires int64 `json:"exp"` 127 IssuedAt int64 `json:"iat"` 128 Subject string `json:"sub,omitempty"` 129 Claims map[string]interface{} `json:"-"` 130 } 131