Skip to content

Commit 950ef44

Browse files
wlheebradfitz
authored andcommitted
jwt: support PrivateClaims in Config
This would help add extra claim for certain 2-leg JWT exchange. For example, Google service account key can be used to generate an OIDC token, but Google TokenURL requires "target_audience" claims set. See this example usage: https://gist.github.com/wlhee/64bc518190053e2122ca1909c2977c67#file-exmaple-go-L29 Change-Id: Ic10b006e45a34210634c5a76261a7e3706066965 GitHub-Last-Rev: 7a6e247 GitHub-Pull-Request: #374 Reviewed-on: https://go-review.googlesource.com/c/oauth2/+/166220 Reviewed-by: Brad Fitzpatrick <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 9f33145 commit 950ef44

File tree

2 files changed

+41
-3
lines changed

2 files changed

+41
-3
lines changed

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
}

0 commit comments

Comments
 (0)