Skip to content

Commit 3e182ca

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents 3501dcb + 0f29369 commit 3e182ca

File tree

5 files changed

+52
-7
lines changed

5 files changed

+52
-7
lines changed

google/google.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,16 @@ func (cs computeSource) Token() (*oauth2.Token, error) {
194194
if res.ExpiresInSec == 0 || res.AccessToken == "" {
195195
return nil, fmt.Errorf("oauth2/google: incomplete token received from metadata")
196196
}
197-
return &oauth2.Token{
197+
tok := &oauth2.Token{
198198
AccessToken: res.AccessToken,
199199
TokenType: res.TokenType,
200200
Expiry: time.Now().Add(time.Duration(res.ExpiresInSec) * time.Second),
201-
}, nil
201+
}
202+
// NOTE(cbro): add hidden metadata about where the token is from.
203+
// This is needed for detection by client libraries to know that credentials come from the metadata server.
204+
// This may be removed in a future version of this library.
205+
return tok.WithExtra(map[string]interface{}{
206+
"oauth2.google.tokenSource": "compute-metadata",
207+
"oauth2.google.serviceAccount": acct,
208+
}), nil
202209
}

jwt/jwt.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ type Config struct {
6666
// request. If empty, the value of TokenURL is used as the
6767
// intended audience.
6868
Audience string
69+
70+
// PrivateClaims optionally specifies custom private claims in the JWT.
71+
// See http://tools.ietf.org/html/draft-jones-json-web-token-10#section-4.3
72+
PrivateClaims map[string]interface{}
73+
74+
// UseIDToken optionally specifies whether ID token should be used instead
75+
// of access token when the server returns both.
76+
UseIDToken bool
6977
}
7078

7179
// TokenSource returns a JWT TokenSource using the configuration
@@ -97,9 +105,10 @@ func (js jwtSource) Token() (*oauth2.Token, error) {
97105
}
98106
hc := oauth2.NewClient(js.ctx, nil)
99107
claimSet := &jws.ClaimSet{
100-
Iss: js.conf.Email,
101-
Scope: strings.Join(js.conf.Scopes, " "),
102-
Aud: js.conf.TokenURL,
108+
Iss: js.conf.Email,
109+
Scope: strings.Join(js.conf.Scopes, " "),
110+
Aud: js.conf.TokenURL,
111+
PrivateClaims: js.conf.PrivateClaims,
103112
}
104113
if subject := js.conf.Subject; subject != "" {
105114
claimSet.Sub = subject
@@ -166,5 +175,11 @@ func (js jwtSource) Token() (*oauth2.Token, error) {
166175
}
167176
token.Expiry = time.Unix(claimSet.Exp, 0)
168177
}
178+
if js.conf.UseIDToken {
179+
if tokenRes.IDToken == "" {
180+
return nil, fmt.Errorf("oauth2: response doesn't have JWT token")
181+
}
182+
token.AccessToken = tokenRes.IDToken
183+
}
169184
return token, nil
170185
}

jwt/jwt_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"fmt"
1212
"net/http"
1313
"net/http/httptest"
14+
"reflect"
1415
"strings"
1516
"testing"
1617

@@ -221,6 +222,16 @@ func TestJWTFetch_AssertionPayload(t *testing.T) {
221222
TokenURL: ts.URL,
222223
Audience: "https://example.com",
223224
},
225+
{
226+
227+
PrivateKey: dummyPrivateKey,
228+
PrivateKeyID: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
229+
TokenURL: ts.URL,
230+
PrivateClaims: map[string]interface{}{
231+
"private0": "claim0",
232+
"private1": "claim1",
233+
},
234+
},
224235
} {
225236
t.Run(conf.Email, func(t *testing.T) {
226237
_, err := conf.TokenSource(context.Background()).Token()
@@ -261,6 +272,18 @@ func TestJWTFetch_AssertionPayload(t *testing.T) {
261272
if got, want := claimSet.Prn, conf.Subject; got != want {
262273
t.Errorf("payload prn = %q; want %q", got, want)
263274
}
275+
if len(conf.PrivateClaims) > 0 {
276+
var got interface{}
277+
if err := json.Unmarshal(gotjson, &got); err != nil {
278+
t.Errorf("failed to parse payload; err = %q", err)
279+
}
280+
m := got.(map[string]interface{})
281+
for v, k := range conf.PrivateClaims {
282+
if !reflect.DeepEqual(m[v], k) {
283+
t.Errorf("payload private claims key = %q: got %#v; want %#v", v, m[v], k)
284+
}
285+
}
286+
}
264287
})
265288
}
266289
}

oauth2.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ var (
117117
// ApprovalForce forces the users to view the consent dialog
118118
// and confirm the permissions request at the URL returned
119119
// from AuthCodeURL, even if they've already done so.
120-
ApprovalForce AuthCodeOption = SetAuthURLParam("approval_prompt", "force")
120+
ApprovalForce AuthCodeOption = SetAuthURLParam("prompt", "consent")
121121
)
122122

123123
// An AuthCodeOption is passed to Config.AuthCodeURL.

oauth2_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func newConf(url string) *Config {
4343
func TestAuthCodeURL(t *testing.T) {
4444
conf := newConf("server")
4545
url := conf.AuthCodeURL("foo", AccessTypeOffline, ApprovalForce)
46-
const want = "server/auth?access_type=offline&approval_prompt=force&client_id=CLIENT_ID&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=foo"
46+
const want = "server/auth?access_type=offline&client_id=CLIENT_ID&prompt=consent&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=foo"
4747
if got := url; got != want {
4848
t.Errorf("got auth code URL = %q; want %q", got, want)
4949
}

0 commit comments

Comments
 (0)