Skip to content

Commit 5a6f7b3

Browse files
mdariiMaxim Darii
authored andcommitted
move the scope from assertion payload to the request parameter
1 parent cf14319 commit 5a6f7b3

File tree

2 files changed

+65
-3
lines changed

2 files changed

+65
-3
lines changed

jwt/jwt.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ func (js jwtSource) Token() (*oauth2.Token, error) {
105105
hc := oauth2.NewClient(js.ctx, nil)
106106
claimSet := &jws.ClaimSet{
107107
Iss: js.conf.Email,
108-
Scope: strings.Join(js.conf.Scopes, " "),
109108
Aud: js.conf.TokenURL,
110109
PrivateClaims: js.conf.PrivateClaims,
111110
}
@@ -130,6 +129,9 @@ func (js jwtSource) Token() (*oauth2.Token, error) {
130129
v := url.Values{}
131130
v.Set("grant_type", defaultGrantType)
132131
v.Set("assertion", payload)
132+
if len(js.conf.Scopes) > 0 {
133+
v.Set("scope", strings.Join(js.conf.Scopes, " "))
134+
}
133135
resp, err := hc.PostForm(js.conf.TokenURL, v)
134136
if err != nil {
135137
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)

jwt/jwt_test.go

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,9 @@ func TestJWTFetch_AssertionPayload(t *testing.T) {
256256
if got, want := claimSet.Iss, conf.Email; got != want {
257257
t.Errorf("payload email = %q; want %q", got, want)
258258
}
259-
if got, want := claimSet.Scope, strings.Join(conf.Scopes, " "); got != want {
260-
t.Errorf("payload scope = %q; want %q", got, want)
259+
// Scope should NOT be in the JWT claim set according to RFC 7521
260+
if claimSet.Scope != "" {
261+
t.Errorf("payload scope should be empty but got %q; scopes should be sent as request parameter", claimSet.Scope)
261262
}
262263
aud := conf.TokenURL
263264
if conf.Audience != "" {
@@ -316,3 +317,62 @@ func TestTokenRetrieveError(t *testing.T) {
316317
t.Fatalf("got %#v, expected %#v", errStr, expected)
317318
}
318319
}
320+
321+
func TestJWTFetch_ScopeParameter(t *testing.T) {
322+
var receivedScope string
323+
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
324+
r.ParseForm()
325+
receivedScope = r.Form.Get("scope")
326+
327+
w.Header().Set("Content-Type", "application/json")
328+
w.Write([]byte(`{
329+
"access_token": "90d64460d14870c08c81352a05dedd3465940a7c",
330+
"scope": "user",
331+
"token_type": "bearer",
332+
"expires_in": 3600
333+
}`))
334+
}))
335+
defer ts.Close()
336+
337+
tests := []struct {
338+
name string
339+
scopes []string
340+
expectedScope string
341+
}{
342+
{
343+
name: "no scopes",
344+
scopes: nil,
345+
expectedScope: "",
346+
},
347+
{
348+
name: "single scope",
349+
scopes: []string{"read"},
350+
expectedScope: "read",
351+
},
352+
{
353+
name: "multiple scopes",
354+
scopes: []string{"read", "write", "admin"},
355+
expectedScope: "read write admin",
356+
},
357+
}
358+
359+
for _, tt := range tests {
360+
t.Run(tt.name, func(t *testing.T) {
361+
conf := &Config{
362+
363+
PrivateKey: dummyPrivateKey,
364+
TokenURL: ts.URL,
365+
Scopes: tt.scopes,
366+
}
367+
368+
_, err := conf.TokenSource(context.Background()).Token()
369+
if err != nil {
370+
t.Fatalf("Failed to fetch token: %v", err)
371+
}
372+
373+
if got, want := receivedScope, tt.expectedScope; got != want {
374+
t.Errorf("received scope parameter = %q; want %q", got, want)
375+
}
376+
})
377+
}
378+
}

0 commit comments

Comments
 (0)