Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(58)

Side by Side Diff: Src/GoogleApis.Auth.Tests/OAuth2/AuthorizationCodeFlowTests.cs

Issue 13972043: Issue 351: Reimplement OAuth2 (Step 3 - Tests, Flows and Credential) (Closed) Base URL: https://google-api-dotnet-client.googlecode.com/hg/
Patch Set: minor Created 10 years, 9 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 Copyright 2013 Google Inc
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 using System;
18 using System.Collections.Generic;
19 using System.Linq;
20 using System.Net.Http;
21 using System.Text;
22 using System.Threading;
23 using System.Threading.Tasks;
24
25 using Moq;
26 using NUnit.Framework;
27
28 using Google.Apis.Auth.OAuth2.Requests;
29 using Google.Apis.Auth.OAuth2.Responses;
30 using Google.Apis.Http;
31 using Google.Apis.Json;
32 using Google.Apis.Testing;
33 using Google.Apis.Tests;
34 using Google.Apis.Util;
35 using Google.Apis.Util.Store;
36
37 namespace Google.Apis.Auth.OAuth2
38 {
39 /// <summary>Tests for <see cref="Google.Apis.Auth.OAuth2.AuthorizationCodeF low"/>.</summary>
40 [TestFixture]
41 public class AuthorizationCodeFlowTests
42 {
43 private const string TokenUrl = "https://token.com";
44 private const string AuthorizationCodeUrl = "https://authorization.com";
45
46 #region Constructor
47
48 [Test]
49 public void TestConstructor_ArgumentException()
50 {
51 // ClientSecrets are missing.
52 try
53 {
54 new AuthorizationCodeFlow(new AuthorizationCodeFlow.Initializer(
55 "https://authorization_code.com", "https://token.com"));
56 Assert.Fail();
57 }
58 catch (ArgumentException ex)
59 {
60 Assert.True(ex.Message.Contains("client secret or client secret stream MUST be set"),
61 "User MUST specify client secrets!");
62 }
63 }
64
65 [Test]
66 public void TestConstructor_DefaultValues()
67 {
68 var flow = CreateFlow();
69 Assert.NotNull(flow.AccessMethod);
70 Assert.That(flow.AccessMethod, Is.InstanceOf<BearerToken.Authorizati onHeaderAccessMethod>());
71 Assert.That(flow.AuthorizationServerUrl, Is.EqualTo("https://authori zation.com"));
72 Assert.NotNull(flow.ClientSecrets);
73 Assert.That(flow.ClientSecrets.ClientId, Is.EqualTo("id"));
74 Assert.That(flow.ClientSecrets.ClientSecret, Is.EqualTo("secret"));
75 Assert.That(flow.Clock, Is.InstanceOf<SystemClock>());
76 Assert.Null(flow.DataStore);
77 Assert.NotNull(flow.HttpClient);
78 Assert.NotNull(flow.Scopes);
79 Assert.That(flow.TokenServerEncodedUrl, Is.EqualTo("https://token.co m"));
80
81 Assert.That(flow.HttpClient.MessageHandler.UnsuccessfulResponseHandl ers.Count(), Is.EqualTo(1));
82 Assert.That(flow.HttpClient.MessageHandler.UnsuccessfulResponseHandl ers.First(),
83 Is.InstanceOf<BackOffHandler>());
84 }
85
86 #endregion
87
88 #region LoadToken
89
90 [Test]
91 public void LoadToken_NoDataStore()
92 {
93 var flow = CreateFlow();
94 Assert.Null(flow.LoadToken("user", CancellationToken.None).Result);
95 }
96
97 [Test]
98 public void LoadToken_NullResponse()
99 {
100 TaskCompletionSource<TokenResponse> tcs = new TaskCompletionSource<T okenResponse>();
101 tcs.SetResult(null);
102 Assert.Null(SubtestLoadToken(tcs));
103 }
104
105 [Test]
106 public void LoadToken_TokenResponse()
107 {
108 TokenResponse response = new TokenResponse
109 {
110 AccessToken = "access"
111 };
112
113 TaskCompletionSource<TokenResponse> tcs = new TaskCompletionSource<T okenResponse>();
114 tcs.SetResult(response);
115 var result = SubtestLoadToken(tcs);
116 Assert.That(result, Is.EqualTo(response));
117 }
118
119 private TokenResponse SubtestLoadToken(TaskCompletionSource<TokenRespons e> tcs)
120 {
121 var mock = new Mock<IDataStore>();
122 mock.Setup(ds => ds.Get<TokenResponse>("user")).Returns(tcs.Task);
123 var flow = CreateFlow(dataStore: mock.Object);
124 var result = flow.LoadToken("user", CancellationToken.None).Result;
125 // Verify Get("user") was called.
126 mock.Verify(ds => ds.Get<TokenResponse>("user"));
127 return result;
128 }
129
130 #endregion
131
132 #region CreateAuthorizationCodeRequest
133
134 [Test]
135 public void TestCreateAuthorizationCodeRequest()
136 {
137 var request = CreateFlow(scopes: new[] { "a", "b" }).CreateAuthoriza tionCodeRequest("redirect");
138 Assert.That(request.AuthorizationServerUrl, Is.EqualTo(new Uri(Autho rizationCodeUrl)));
139 Assert.That(request.ClientId, Is.EqualTo("id"));
140 Assert.That(request.RedirectUri, Is.EqualTo("redirect"));
141 Assert.That(request.ResponseType, Is.EqualTo("code"));
142 Assert.That(request.Scope, Is.EqualTo("a b"));
143 Assert.Null(request.State);
144 }
145
146 #endregion
147
148 [Test]
149 public void TestExchangeCodeForToken()
150 {
151 var mock = new Mock<IDataStore>();
152 var handler = new FetchTokenMessageHandler();
153 handler.AuthorizationCodeTokenRequest = new AuthorizationCodeTokenRe quest()
154 {
155 Code = "c0de",
156 RedirectUri = "redIrect",
157 Scope = "a"
158 };
159 MockHttpClientFactory mockFactory = new MockHttpClientFactory(handle r);
160
161 TaskCompletionSource<object> tcs = new TaskCompletionSource<object>( );
162 tcs.SetResult(null);
163 mock.Setup(ds => ds.Store("uSer", It.IsAny<TokenResponse>())).Return s(tcs.Task);
164
165 var flow = CreateFlow(httpClientFactory: mockFactory, scopes: new[] { "a" }, dataStore: mock.Object);
166 var response = flow.ExchangeCodeForToken("uSer", "c0de", "redIrect", CancellationToken.None).Result;
167 SubtestTokenResponse(response);
168
169 mock.Verify(ds => ds.Store("uSer", It.IsAny<TokenResponse>()));
170 }
171
172 [Test]
173 public void TestRefreshToken()
174 {
175 var mock = new Mock<IDataStore>();
176 var handler = new FetchTokenMessageHandler();
177 handler.RefreshTokenRequest = new RefreshTokenRequest()
178 {
179 RefreshToken = "REFRESH",
180 Scope = "a"
181 };
182 MockHttpClientFactory mockFactory = new MockHttpClientFactory(handle r);
183
184 TaskCompletionSource<object> tcs = new TaskCompletionSource<object>( );
185 tcs.SetResult(null);
186 mock.Setup(ds => ds.Store("uSer", It.IsAny<TokenResponse>())).Return s(tcs.Task);
187
188 var flow = CreateFlow(httpClientFactory: mockFactory, scopes: new[] { "a" }, dataStore: mock.Object);
189 var response = flow.RefreshToken("uSer", "REFRESH", CancellationToke n.None).Result;
190 SubtestTokenResponse(response);
191
192
193 mock.Verify(ds => ds.Store("uSer", It.IsAny<TokenResponse>()));
194 }
195
196 #region FetchToken
197
198 /// <summary>
199 /// Fetch token message handler, which expects an authorization code tok en request or a refresh token request.
200 /// It verifies all the query parameters are valid and return an error r esponse in case <see cref="Error"/>·
201 /// is <c>true</c>.
202 /// </summary>
203 public class FetchTokenMessageHandler : CountableMessageHandler
204 {
205 internal AuthorizationCodeTokenRequest AuthorizationCodeTokenRequest { get; set; }
206 internal RefreshTokenRequest RefreshTokenRequest { get; set; }
207 internal bool Error { get; set; }
208
209 protected override async Task<HttpResponseMessage> SendAsyncCore(Htt pRequestMessage request,
210 CancellationToken taskCancellationToken)
211 {
212 Assert.That(request.RequestUri, Is.EqualTo(new Uri(TokenUrl)));
213
214 if (AuthorizationCodeTokenRequest != null)
215 {
216 // Verify right parameters.
217 var content = await request.Content.ReadAsStringAsync();
218 foreach (var parameter in content.Split('&'))
219 {
220 var keyValue = parameter.Split('=');
221 switch (keyValue[0])
222 {
223 case "code":
224 Assert.That(keyValue[1], Is.EqualTo("c0de"));
225 break;
226 case "redirect_uri":
227 Assert.That(keyValue[1], Is.EqualTo("redIrect")) ;
228 break;
229 case "scope":
230 Assert.That(keyValue[1], Is.EqualTo("a"));
231 break;
232 case "grant_type":
233 Assert.That(keyValue[1], Is.EqualTo("authorizati on_code"));
234 break;
235 case "client_id":
236 Assert.That(keyValue[1], Is.EqualTo("id"));
237 break;
238 case "client_secret":
239 Assert.That(keyValue[1], Is.EqualTo("secret"));
240 break;
241 default:
242 throw new ArgumentOutOfRangeException("Invalid p arameter!");
243 }
244 }
245 }
246 else
247 {
248 // Verify right parameters.
249 var content = await request.Content.ReadAsStringAsync();
250 foreach (var parameter in content.Split('&'))
251 {
252 var keyValue = parameter.Split('=');
253 switch (keyValue[0])
254 {
255 case "refresh_token":
256 Assert.That(keyValue[1], Is.EqualTo("REFRESH"));
257 break;
258 case "scope":
259 Assert.That(keyValue[1], Is.EqualTo("a"));
260 break;
261 case "grant_type":
262 Assert.That(keyValue[1], Is.EqualTo("refresh_tok en"));
263 break;
264 case "client_id":
265 Assert.That(keyValue[1], Is.EqualTo("id"));
266 break;
267 case "client_secret":
268 Assert.That(keyValue[1], Is.EqualTo("secret"));
269 break;
270 default:
271 throw new ArgumentOutOfRangeException("Invalid p arameter!");
272 }
273 }
274 }
275
276 var response = new HttpResponseMessage();
277 if (Error)
278 {
279 response.StatusCode = System.Net.HttpStatusCode.BadRequest;
280 var serializedObject = NewtonsoftJsonSerializer.Instance.Ser ialize(new TokenErrorResponse
281 {
282 Error = "error",
283 ErrorDescription = "desc",
284 ErrorUri = "uri"
285 });
286 response.Content = new StringContent(serializedObject, Encod ing.UTF8);
287 }
288 else
289 {
290 var serializedObject = NewtonsoftJsonSerializer.Instance.Ser ialize(new TokenResponse
291 {
292 AccessToken = "a",
293 RefreshToken = "r",
294 ExpiresInSeconds = 100,
295 Scope = "b",
296 });
297 response.Content = new StringContent(serializedObject, Encod ing.UTF8);
298 }
299
300 return response;
301 }
302 }
303
304 [Test]
305 public void TestFetchToken_AuthorizationCodeRequest()
306 {
307 var handler = new FetchTokenMessageHandler();
308 handler.AuthorizationCodeTokenRequest = new AuthorizationCodeTokenRe quest()
309 {
310 Code = "c0de",
311 RedirectUri = "redIrect",
312 Scope = "a"
313 };
314 MockHttpClientFactory mockFactory = new MockHttpClientFactory(handle r);
315
316 var flow = CreateFlow(httpClientFactory: mockFactory);
317 var response = flow.FetchToken("user", handler.AuthorizationCodeToke nRequest,
318 CancellationToken.None).Result;
319 SubtestTokenResponse(response);
320 }
321
322 [Test]
323 public void TestFetchToken_RefreshTokenRequest()
324 {
325 var handler = new FetchTokenMessageHandler();
326 handler.RefreshTokenRequest = new RefreshTokenRequest()
327 {
328 RefreshToken = "REFRESH",
329 Scope = "a"
330 };
331
332 MockHttpClientFactory mockFactory = new MockHttpClientFactory(handle r);
333
334 var flow = CreateFlow(httpClientFactory: mockFactory);
335 var response = flow.FetchToken("user", handler.RefreshTokenRequest, CancellationToken.None).Result;
336 SubtestTokenResponse(response);
337 }
338
339 [Test]
340 public void TestFetchToken_AuthorizationCodeRequest_Error()
341 {
342 var handler = new FetchTokenMessageHandler();
343 handler.AuthorizationCodeTokenRequest = new AuthorizationCodeTokenRe quest()
344 {
345 Code = "c0de",
346 RedirectUri = "redIrect",
347 Scope = "a"
348 };
349 handler.Error = true;
350 SubtestFetchToken_Error(handler);
351 }
352
353 [Test]
354 public void TestFetchToken_RefreshTokenRequest_Error()
355 {
356 var handler = new FetchTokenMessageHandler();
357 handler.RefreshTokenRequest = new RefreshTokenRequest()
358 {
359 RefreshToken = "REFRESH",
360 Scope = "a"
361 };
362 handler.Error = true;
363 SubtestFetchToken_Error(handler);
364 }
365
366 /// <summary>Subtest for receiving an error token response.</summary>
367 /// <param name="handler">The message handler</param>
368 private void SubtestFetchToken_Error(FetchTokenMessageHandler handler)
369 {
370 MockHttpClientFactory mockFactory = new MockHttpClientFactory(handle r);
371 var flow = CreateFlow(httpClientFactory: mockFactory);
372 try
373 {
374 var request =
375 (TokenRequest)handler.AuthorizationCodeTokenRequest ?? (Toke nRequest)handler.RefreshTokenRequest;
376 var result = flow.FetchToken("user", request, CancellationToken. None).Result;
377 Assert.Fail();
378 }
379 catch (AggregateException aex)
380 {
381 var ex = aex.InnerException as TokenResponseException;
382 Assert.IsNotNull(ex);
383 var result = ex.Error;
384 Assert.That(result.Error, Is.EqualTo("error"));
385 Assert.That(result.ErrorDescription, Is.EqualTo("desc"));
386 Assert.That(result.ErrorUri, Is.EqualTo("uri"));
387 }
388 }
389
390 #endregion
391
392 /// <summary>Creates an authorization code flow with the given parameter s.</summary>
393 /// <param name="dataStore">The data store.</param>
394 /// <param name="scopes">The Scopes</param>
395 /// <param name="httpClientFactory">The HTTP client factory. If not set the default will be used</param>
396 /// <returns>Authorization code flow</returns>
397 private AuthorizationCodeFlow CreateFlow(IDataStore dataStore = null, IE numerable<string> scopes = null,
398 IHttpClientFactory httpClientFactory = null)
399 {
400 var secrets = new ClientSecrets() { ClientId = "id", ClientSecret = "secret" };
401 var initializer = new AuthorizationCodeFlow.Initializer(Authorizatio nCodeUrl, TokenUrl)
402 {
403 ClientSecrets = secrets,
404 HttpClientFactory = httpClientFactory
405 };
406
407 if (dataStore != null)
408 {
409 initializer.DataStore = dataStore;
410 }
411 if (scopes != null)
412 {
413 initializer.Scopes = scopes;
414 }
415 return new AuthorizationCodeFlow(initializer);
416 }
417
418 /// <summary>Verifies that the token response contains the expected data .</summary>
419 /// <param name="response">The token response</param>
420 private void SubtestTokenResponse(TokenResponse response)
421 {
422 Assert.That(response.RefreshToken, Is.EqualTo("r"));
423 Assert.That(response.ExpiresInSeconds, Is.EqualTo(100));
424 Assert.That(response.Scope, Is.EqualTo("b"));
425 }
426 }
427 }
OLDNEW

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b