@@ -6,12 +6,19 @@ package clientcredentials
66
77import  (
88	"context" 
9+ 	"encoding/base64" 
10+ 	"encoding/json" 
911	"io" 
1012	"io/ioutil" 
1113	"net/http" 
1214	"net/http/httptest" 
1315	"net/url" 
16+ 	"strings" 
1417	"testing" 
18+ 	"time" 
19+ 
20+ 	"golang.org/x/oauth2" 
21+ 	"golang.org/x/oauth2/jws" 
1522)
1623
1724func  newConf (serverURL  string ) * Config  {
@@ -111,6 +118,136 @@ func TestTokenRequest(t *testing.T) {
111118	}
112119}
113120
121+ var  dummyPrivateKey  =  []byte (`-----BEGIN RSA PRIVATE KEY----- 
122+ MIIEpAIBAAKCAQEAx4fm7dngEmOULNmAs1IGZ9Apfzh+BkaQ1dzkmbUgpcoghucE 
123+ DZRnAGd2aPyB6skGMXUytWQvNYav0WTR00wFtX1ohWTfv68HGXJ8QXCpyoSKSSFY 
124+ fuP9X36wBSkSX9J5DVgiuzD5VBdzUISSmapjKm+DcbRALjz6OUIPEWi1Tjl6p5RK 
125+ 1w41qdbmt7E5/kGhKLDuT7+M83g4VWhgIvaAXtnhklDAggilPPa8ZJ1IFe31lNlr 
126+ k4DRk38nc6sEutdf3RL7QoH7FBusI7uXV03DC6dwN1kP4GE7bjJhcRb/7jYt7CQ9 
127+ /E9Exz3c0yAp0yrTg0Fwh+qxfH9dKwN52S7SBwIDAQABAoIBAQCaCs26K07WY5Jt 
128+ 3a2Cw3y2gPrIgTCqX6hJs7O5ByEhXZ8nBwsWANBUe4vrGaajQHdLj5OKfsIDrOvn 
129+ 2NI1MqflqeAbu/kR32q3tq8/Rl+PPiwUsW3E6Pcf1orGMSNCXxeducF2iySySzh3 
130+ nSIhCG5uwJDWI7a4+9KiieFgK1pt/Iv30q1SQS8IEntTfXYwANQrfKUVMmVF9aIK 
131+ 6/WZE2yd5+q3wVVIJ6jsmTzoDCX6QQkkJICIYwCkglmVy5AeTckOVwcXL0jqw5Kf 
132+ 5/soZJQwLEyBoQq7Kbpa26QHq+CJONetPP8Ssy8MJJXBT+u/bSseMb3Zsr5cr43e 
133+ DJOhwsThAoGBAPY6rPKl2NT/K7XfRCGm1sbWjUQyDShscwuWJ5+kD0yudnT/ZEJ1 
134+ M3+KS/iOOAoHDdEDi9crRvMl0UfNa8MAcDKHflzxg2jg/QI+fTBjPP5GOX0lkZ9g 
135+ z6VePoVoQw2gpPFVNPPTxKfk27tEzbaffvOLGBEih0Kb7HTINkW8rIlzAoGBAM9y 
136+ 1yr+jvfS1cGFtNU+Gotoihw2eMKtIqR03Yn3n0PK1nVCDKqwdUqCypz4+ml6cxRK 
137+ J8+Pfdh7D+ZJd4LEG6Y4QRDLuv5OA700tUoSHxMSNn3q9As4+T3MUyYxWKvTeu3U 
138+ f2NWP9ePU0lV8ttk7YlpVRaPQmc1qwooBA/z/8AdAoGAW9x0HWqmRICWTBnpjyxx 
139+ QGlW9rQ9mHEtUotIaRSJ6K/F3cxSGUEkX1a3FRnp6kPLcckC6NlqdNgNBd6rb2rA 
140+ cPl/uSkZP42Als+9YMoFPU/xrrDPbUhu72EDrj3Bllnyb168jKLa4VBOccUvggxr 
141+ Dm08I1hgYgdN5huzs7y6GeUCgYEAj+AZJSOJ6o1aXS6rfV3mMRve9bQ9yt8jcKXw 
142+ 5HhOCEmMtaSKfnOF1Ziih34Sxsb7O2428DiX0mV/YHtBnPsAJidL0SdLWIapBzeg 
143+ KHArByIRkwE6IvJvwpGMdaex1PIGhx5i/3VZL9qiq/ElT05PhIb+UXgoWMabCp84 
144+ OgxDK20CgYAeaFo8BdQ7FmVX2+EEejF+8xSge6WVLtkaon8bqcn6P0O8lLypoOhd 
145+ mJAYH8WU+UAy9pecUnDZj14LAGNVmYcse8HFX71MoshnvCTFEPVo4rZxIAGwMpeJ 
146+ 5jgQ3slYLpqrGlcbLgUXBUgzEO684Wk/UV9DFPlHALVqCfXQ9dpJPg== 
147+ -----END RSA PRIVATE KEY-----` )
148+ 
149+ func  TestTokenJWTRequest (t  * testing.T ) {
150+ 	var  assertion  string 
151+ 	audience  :=  "audience1" 
152+ 	scopes  :=  "scope1 scope2" 
153+ 	ts  :=  httptest .NewServer (http .HandlerFunc (func (w  http.ResponseWriter , r  * http.Request ) {
154+ 		if  r .URL .String () !=  "/token"  {
155+ 			t .Errorf ("authenticate client request URL = %q; want %q" , r .URL , "/token" )
156+ 		}
157+ 		if  got , want  :=  r .Header .Get ("Content-Type" ), "application/x-www-form-urlencoded" ; got  !=  want  {
158+ 			t .Errorf ("Content-Type header = %q; want %q" , got , want )
159+ 		}
160+ 		err  :=  r .ParseForm ()
161+ 		if  err  !=  nil  {
162+ 			t .Fatal (err )
163+ 		}
164+ 
165+ 		if  got , want  :=  r .Form .Get ("scope" ), scopes ; got  !=  want  {
166+ 			t .Errorf ("scope = %q; want %q" , got , want )
167+ 		}
168+ 		if  got , want  :=  r .Form .Get ("audience" ), audience ; got  !=  want  {
169+ 			t .Errorf ("audience = %q; want %q" , got , want )
170+ 		}
171+ 		if  got , want  :=  r .Form .Get ("grant_type" ), "client_credentials" ; got  !=  want  {
172+ 			t .Errorf ("grant_type = %q; want %q" , got , want )
173+ 		}
174+ 		expectedAssertionType  :=  "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" 
175+ 		if  got , want  :=  r .Form .Get ("client_assertion_type" ), expectedAssertionType ; got  !=  want  {
176+ 			t .Errorf ("client_assertion_type = %q; want %q" , got , want )
177+ 		}
178+ 
179+ 		assertion  =  r .Form .Get ("client_assertion" )
180+ 
181+ 		w .Header ().Set ("Content-Type" , "application/json" )
182+ 		w .Write ([]byte (`{ 
183+ 			"access_token": "90d64460d14870c08c81352a05dedd3465940a7c", 
184+ 			"token_type": "bearer", 
185+ 			"expires_in": 3600 
186+ 		}` ))
187+ 	}))
188+ 	defer  ts .Close ()
189+ 
190+ 	for  _ , conf  :=  range  []* Config {
191+ 		{
192+ 			ClientID :       "CLIENT_ID" ,
193+ 			Scopes :         strings .Split (scopes , " " ),
194+ 			TokenURL :       ts .URL  +  "/token" ,
195+ 			EndpointParams : url.Values {"audience" : {audience }},
196+ 			AuthStyle :      oauth2 .AuthStylePrivateKeyJWT ,
197+ 			PrivateKey :     dummyPrivateKey ,
198+ 			KeyID :          "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ,
199+ 		},
200+ 		{
201+ 			ClientID :       "CLIENT_ID_set_jwt_expiration_time" ,
202+ 			Scopes :         strings .Split (scopes , " " ),
203+ 			TokenURL :       ts .URL  +  "/token" ,
204+ 			EndpointParams : url.Values {"audience" : {audience }},
205+ 			AuthStyle :      oauth2 .AuthStylePrivateKeyJWT ,
206+ 			PrivateKey :     dummyPrivateKey ,
207+ 			KeyID :          "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ,
208+ 			JWTExpires :     time .Minute ,
209+ 		},
210+ 	} {
211+ 		t .Run (conf .ClientID , func (t  * testing.T ) {
212+ 			_ , err  :=  conf .TokenSource (context .Background ()).Token ()
213+ 			if  err  !=  nil  {
214+ 				t .Fatalf ("Failed to fetch token: %v" , err )
215+ 			}
216+ 			parts  :=  strings .Split (assertion , "." )
217+ 			if  len (parts ) !=  3  {
218+ 				t .Fatalf ("assertion = %q; want 3 parts" , assertion )
219+ 			}
220+ 			gotJson , err  :=  base64 .RawURLEncoding .DecodeString (parts [1 ])
221+ 			if  err  !=  nil  {
222+ 				t .Fatalf ("invalid token payload; err = %v" , err )
223+ 			}
224+ 			claimSet  :=  jws.ClaimSet {}
225+ 			if  err  :=  json .Unmarshal (gotJson , & claimSet ); err  !=  nil  {
226+ 				t .Errorf ("failed to unmarshal json token payload = %q; err = %v" , gotJson , err )
227+ 			}
228+ 			if  got , want  :=  claimSet .Iss , conf .ClientID ; got  !=  want  {
229+ 				t .Errorf ("payload iss = %q; want %q" , got , want )
230+ 			}
231+ 			if  claimSet .Jti  ==  ""  {
232+ 				t .Errorf ("payload jti is empty" )
233+ 			}
234+ 			expectedDuration  :=  time .Hour 
235+ 			if  conf .JWTExpires  >  0  {
236+ 				expectedDuration  =  conf .JWTExpires 
237+ 			}
238+ 			if  got , want  :=  claimSet .Exp , time .Now ().Add (expectedDuration ).Unix (); got  !=  want  {
239+ 				t .Errorf ("payload exp = %q; want %q" , got , want )
240+ 			}
241+ 			if  got , want  :=  claimSet .Aud , conf .TokenURL ; got  !=  want  {
242+ 				t .Errorf ("payload aud = %q; want %q" , got , want )
243+ 			}
244+ 			if  got , want  :=  claimSet .Sub , conf .ClientID ; got  !=  want  {
245+ 				t .Errorf ("payload sub = %q; want %q" , got , want )
246+ 			}
247+ 		})
248+ 	}
249+ }
250+ 
114251func  TestTokenRefreshRequest (t  * testing.T ) {
115252	ts  :=  httptest .NewServer (http .HandlerFunc (func (w  http.ResponseWriter , r  * http.Request ) {
116253		if  r .URL .String () ==  "/somethingelse"  {
0 commit comments