• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *    * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *    * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *
15  *    * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 package com.google.auth.oauth2;
33 
34 import static org.junit.Assert.assertEquals;
35 import static org.junit.Assert.assertFalse;
36 import static org.junit.Assert.assertNull;
37 import static org.junit.Assert.assertSame;
38 import static org.junit.Assert.fail;
39 
40 import com.google.auth.TestUtils;
41 import java.io.IOException;
42 import java.net.URI;
43 import java.net.URL;
44 import java.util.Arrays;
45 import java.util.Date;
46 import java.util.HashMap;
47 import java.util.List;
48 import java.util.Map;
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 import org.junit.runners.JUnit4;
52 
53 /** Unit Tests for UserAuthorizer */
54 @RunWith(JUnit4.class)
55 public class UserAuthorizerTest {
56   private static final String CLIENT_ID_VALUE = "ya29.1.AADtN_UtlxN3PuGAxrN2XQnZTVRvDyVWnYq4I6dws";
57   private static final String CLIENT_SECRET = "jakuaL9YyieakhECKL2SwZcu";
58   private static final String REFRESH_TOKEN = "1/Tl6awhpFjkMkSJoj1xsli0H2eL5YsMgU_NKPY2TyGWY";
59   private static final String ACCESS_TOKEN_VALUE = "1/MkSJoj1xsli0AccessToken_NKPY2";
60   private static final List<String> GRANTED_SCOPES = Arrays.asList("scope1", "scope2");
61   private static final String GRANTED_SCOPES_STRING = String.join(" ", GRANTED_SCOPES);
62   private static final String DUMMY_SCOPE = "dummy_scope";
63   private static final List<String> DUMMY_SCOPES = Arrays.asList(DUMMY_SCOPE);
64   private static final Long EXPIRATION_TIME = 504000300L;
65   private static final AccessToken ACCESS_TOKEN =
66       AccessToken.newBuilder()
67           .setTokenValue(ACCESS_TOKEN_VALUE)
68           .setExpirationTime(new Date(EXPIRATION_TIME))
69           .setScopes(GRANTED_SCOPES)
70           .build();
71   private static final ClientId CLIENT_ID = ClientId.of(CLIENT_ID_VALUE, CLIENT_SECRET);
72   private static final String USER_ID = "foo@bar.com";
73   private static final URI CALLBACK_URI = URI.create("/testcallback");
74   private static final String CODE = "thisistheend";
75   private static final URI BASE_URI = URI.create("http://example.com/foo");
76   private static final PKCEProvider pkce = new DefaultPKCEProvider();
77 
78   @Test
constructorMinimum()79   public void constructorMinimum() {
80     TokenStore store = new MemoryTokensStorage();
81 
82     UserAuthorizer authorizer =
83         UserAuthorizer.newBuilder()
84             .setClientId(CLIENT_ID)
85             .setScopes(DUMMY_SCOPES)
86             .setTokenStore(store)
87             .build();
88 
89     assertSame(CLIENT_ID, authorizer.getClientId());
90     assertSame(store, authorizer.getTokenStore());
91     assertEquals(DUMMY_SCOPES, authorizer.getScopes());
92     assertEquals(UserAuthorizer.DEFAULT_CALLBACK_URI, authorizer.getCallbackUri());
93   }
94 
95   @Test
constructorCommon()96   public void constructorCommon() {
97     TokenStore store = new MemoryTokensStorage();
98 
99     UserAuthorizer authorizer =
100         UserAuthorizer.newBuilder()
101             .setClientId(CLIENT_ID)
102             .setScopes(DUMMY_SCOPES)
103             .setTokenStore(store)
104             .setCallbackUri(CALLBACK_URI)
105             .build();
106 
107     assertSame(CLIENT_ID, authorizer.getClientId());
108     assertSame(store, authorizer.getTokenStore());
109     assertEquals(DUMMY_SCOPES, authorizer.getScopes());
110     assertEquals(CALLBACK_URI, authorizer.getCallbackUri());
111   }
112 
113   @Test(expected = NullPointerException.class)
constructorCommon_nullClientId_throws()114   public void constructorCommon_nullClientId_throws() {
115     UserAuthorizer.newBuilder().setScopes(DUMMY_SCOPES).setCallbackUri(CALLBACK_URI).build();
116   }
117 
118   @Test(expected = NullPointerException.class)
constructorCommon_nullScopes_throws()119   public void constructorCommon_nullScopes_throws() {
120     UserAuthorizer.newBuilder().setClientId(CLIENT_ID).build();
121   }
122 
123   @Test
getCallbackUri_relativeToBase()124   public void getCallbackUri_relativeToBase() {
125     final URI callbackURI = URI.create("/bar");
126     final URI expectedCallbackURI = URI.create("http://example.com/bar");
127     UserAuthorizer authorizer =
128         UserAuthorizer.newBuilder()
129             .setClientId(CLIENT_ID)
130             .setScopes(DUMMY_SCOPES)
131             .setCallbackUri(callbackURI)
132             .build();
133 
134     URI absoluteCallbackURI = authorizer.getCallbackUri(BASE_URI);
135 
136     assertEquals(expectedCallbackURI, absoluteCallbackURI);
137   }
138 
139   @Test
getAuthorizationUrl()140   public void getAuthorizationUrl() throws IOException {
141     final String CUSTOM_STATE = "custom_state";
142     final String PROTOCOL = "https";
143     final String HOST = "accounts.test.com";
144     final String PATH = "/o/o/oauth2/auth";
145     final URI AUTH_URI = URI.create(PROTOCOL + "://" + HOST + PATH);
146     final String EXPECTED_CALLBACK = "http://example.com" + CALLBACK_URI.toString();
147     UserAuthorizer authorizer =
148         UserAuthorizer.newBuilder()
149             .setClientId(CLIENT_ID)
150             .setScopes(DUMMY_SCOPES)
151             .setCallbackUri(CALLBACK_URI)
152             .setUserAuthUri(AUTH_URI)
153             .setPKCEProvider(pkce)
154             .build();
155 
156     URL authorizationUrl = authorizer.getAuthorizationUrl(USER_ID, CUSTOM_STATE, BASE_URI);
157 
158     assertEquals(PROTOCOL, authorizationUrl.getProtocol());
159     assertEquals(-1, authorizationUrl.getPort());
160     assertEquals(PATH, authorizationUrl.getPath());
161     assertEquals(HOST, authorizationUrl.getHost());
162     String query = authorizationUrl.getQuery();
163     Map<String, String> parameters = TestUtils.parseQuery(query);
164     assertEquals(CUSTOM_STATE, parameters.get("state"));
165     assertEquals(USER_ID, parameters.get("login_hint"));
166     assertEquals(EXPECTED_CALLBACK, parameters.get("redirect_uri"));
167     assertEquals(CLIENT_ID_VALUE, parameters.get("client_id"));
168     assertEquals(DUMMY_SCOPE, parameters.get("scope"));
169     assertEquals("code", parameters.get("response_type"));
170     assertEquals(pkce.getCodeChallenge(), parameters.get("code_challenge"));
171     assertEquals(pkce.getCodeChallengeMethod(), parameters.get("code_challenge_method"));
172   }
173 
174   @Test
getAuthorizationUrl_additionalParameters()175   public void getAuthorizationUrl_additionalParameters() throws IOException {
176     final String CUSTOM_STATE = "custom_state";
177     final String PROTOCOL = "https";
178     final String HOST = "accounts.test.com";
179     final String PATH = "/o/o/oauth2/auth";
180     final URI AUTH_URI = URI.create(PROTOCOL + "://" + HOST + PATH);
181     final String EXPECTED_CALLBACK = "http://example.com" + CALLBACK_URI.toString();
182     UserAuthorizer authorizer =
183         UserAuthorizer.newBuilder()
184             .setClientId(CLIENT_ID)
185             .setScopes(DUMMY_SCOPES)
186             .setCallbackUri(CALLBACK_URI)
187             .setUserAuthUri(AUTH_URI)
188             .build();
189     Map<String, String> additionalParameters = new HashMap<String, String>();
190     additionalParameters.put("param1", "value1");
191     additionalParameters.put("param2", "value2");
192 
193     // Verify that the authorization URL doesn't include the additional parameters if they are not
194     // passed in.
195     URL authorizationUrl = authorizer.getAuthorizationUrl(USER_ID, CUSTOM_STATE, BASE_URI);
196     String query = authorizationUrl.getQuery();
197     Map<String, String> parameters = TestUtils.parseQuery(query);
198     assertFalse(parameters.containsKey("param1"));
199     assertFalse(parameters.containsKey("param2"));
200 
201     // Verify that the authorization URL includes the additional parameters if they are passed in.
202     authorizationUrl =
203         authorizer.getAuthorizationUrl(USER_ID, CUSTOM_STATE, BASE_URI, additionalParameters);
204     query = authorizationUrl.getQuery();
205     parameters = TestUtils.parseQuery(query);
206     assertEquals("value1", parameters.get("param1"));
207     assertEquals("value2", parameters.get("param2"));
208 
209     // Verify that the authorization URL doesn't include the additional parameters passed in the
210     // previous call to the authorizer
211     authorizationUrl = authorizer.getAuthorizationUrl(USER_ID, CUSTOM_STATE, BASE_URI);
212     query = authorizationUrl.getQuery();
213     parameters = TestUtils.parseQuery(query);
214     assertFalse(parameters.containsKey("param1"));
215     assertFalse(parameters.containsKey("param2"));
216   }
217 
218   @Test
getCredentials_noCredentials_returnsNull()219   public void getCredentials_noCredentials_returnsNull() throws IOException {
220     UserAuthorizer authorizer =
221         UserAuthorizer.newBuilder()
222             .setClientId(CLIENT_ID)
223             .setScopes(DUMMY_SCOPES)
224             .setTokenStore(new MemoryTokensStorage())
225             .build();
226 
227     UserCredentials credentials = authorizer.getCredentials(USER_ID);
228 
229     assertNull(credentials);
230   }
231 
232   @Test
getCredentials_storedCredentials_returnsStored()233   public void getCredentials_storedCredentials_returnsStored() throws IOException {
234     TokenStore tokenStore = new MemoryTokensStorage();
235 
236     UserCredentials initialCredentials =
237         UserCredentials.newBuilder()
238             .setClientId(CLIENT_ID_VALUE)
239             .setClientSecret(CLIENT_SECRET)
240             .setRefreshToken(REFRESH_TOKEN)
241             .setAccessToken(ACCESS_TOKEN)
242             .build();
243 
244     UserAuthorizer authorizer =
245         UserAuthorizer.newBuilder()
246             .setClientId(CLIENT_ID)
247             .setScopes(DUMMY_SCOPES)
248             .setTokenStore(tokenStore)
249             .build();
250     authorizer.storeCredentials(USER_ID, initialCredentials);
251 
252     UserCredentials credentials = authorizer.getCredentials(USER_ID);
253 
254     assertEquals(REFRESH_TOKEN, credentials.getRefreshToken());
255     assertEquals(ACCESS_TOKEN_VALUE, credentials.getAccessToken().getTokenValue());
256     assertEquals(EXPIRATION_TIME, credentials.getAccessToken().getExpirationTimeMillis());
257     assertEquals(GRANTED_SCOPES, credentials.getAccessToken().getScopes());
258   }
259 
260   @Test(expected = NullPointerException.class)
getCredentials_nullUserId_throws()261   public void getCredentials_nullUserId_throws() throws IOException {
262     TokenStore tokenStore = new MemoryTokensStorage();
263     UserAuthorizer authorizer =
264         UserAuthorizer.newBuilder()
265             .setClientId(CLIENT_ID)
266             .setScopes(DUMMY_SCOPES)
267             .setTokenStore(tokenStore)
268             .build();
269 
270     authorizer.getCredentials(null);
271   }
272 
273   @Test
getCredentials_refreshedToken_stored()274   public void getCredentials_refreshedToken_stored() throws IOException {
275     final String accessTokenValue1 = "1/MkSJoj1xsli0AccessToken_NKPY2";
276     final String accessTokenValue2 = "2/MkSJoj1xsli0AccessToken_NKPY2";
277     AccessToken accessToken1 =
278         AccessToken.newBuilder()
279             .setTokenValue(accessTokenValue1)
280             .setExpirationTime(new Date(EXPIRATION_TIME))
281             .setScopes(GRANTED_SCOPES)
282             .build();
283     MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory();
284     transportFactory.transport.addClient(CLIENT_ID_VALUE, CLIENT_SECRET);
285     transportFactory.transport.addRefreshToken(
286         REFRESH_TOKEN, accessTokenValue2, GRANTED_SCOPES_STRING);
287     TokenStore tokenStore = new MemoryTokensStorage();
288     UserAuthorizer authorizer =
289         UserAuthorizer.newBuilder()
290             .setClientId(CLIENT_ID)
291             .setScopes(DUMMY_SCOPES)
292             .setTokenStore(tokenStore)
293             .setHttpTransportFactory(transportFactory)
294             .build();
295 
296     UserCredentials originalCredentials =
297         UserCredentials.newBuilder()
298             .setClientId(CLIENT_ID_VALUE)
299             .setClientSecret(CLIENT_SECRET)
300             .setRefreshToken(REFRESH_TOKEN)
301             .setAccessToken(accessToken1)
302             .setHttpTransportFactory(transportFactory)
303             .build();
304 
305     authorizer.storeCredentials(USER_ID, originalCredentials);
306 
307     UserCredentials credentials1 = authorizer.getCredentials(USER_ID);
308 
309     assertEquals(REFRESH_TOKEN, credentials1.getRefreshToken());
310     assertEquals(accessTokenValue1, credentials1.getAccessToken().getTokenValue());
311     assertEquals(GRANTED_SCOPES, credentials1.getAccessToken().getScopes());
312 
313     // Refresh the token to get update from token server
314     credentials1.refresh();
315     assertEquals(REFRESH_TOKEN, credentials1.getRefreshToken());
316     assertEquals(accessTokenValue2, credentials1.getAccessToken().getTokenValue());
317     assertEquals(GRANTED_SCOPES, credentials1.getAccessToken().getScopes());
318 
319     // Load a second credentials instance
320     UserCredentials credentials2 = authorizer.getCredentials(USER_ID);
321 
322     // Verify that token refresh stored the updated tokens
323     assertEquals(REFRESH_TOKEN, credentials2.getRefreshToken());
324     assertEquals(accessTokenValue2, credentials2.getAccessToken().getTokenValue());
325     assertEquals(GRANTED_SCOPES, credentials2.getAccessToken().getScopes());
326   }
327 
328   @Test
getCredentials_refreshedToken_different_granted_scopes()329   public void getCredentials_refreshedToken_different_granted_scopes() throws IOException {
330     final String accessTokenValue1 = "1/MkSJoj1xsli0AccessToken_NKPY2";
331     final String accessTokenValue2 = "2/MkSJoj1xsli0AccessToken_NKPY2";
332     final List<String> grantedRefreshScopes = Arrays.asList("scope3");
333     AccessToken accessToken1 =
334         AccessToken.newBuilder()
335             .setTokenValue(accessTokenValue1)
336             .setExpirationTime(new Date(EXPIRATION_TIME))
337             .setScopes(GRANTED_SCOPES)
338             .build();
339     MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory();
340     transportFactory.transport.addClient(CLIENT_ID_VALUE, CLIENT_SECRET);
341     transportFactory.transport.addRefreshToken(REFRESH_TOKEN, accessTokenValue2, "scope3");
342     TokenStore tokenStore = new MemoryTokensStorage();
343     UserAuthorizer authorizer =
344         UserAuthorizer.newBuilder()
345             .setClientId(CLIENT_ID)
346             .setScopes(DUMMY_SCOPES)
347             .setTokenStore(tokenStore)
348             .setHttpTransportFactory(transportFactory)
349             .build();
350 
351     UserCredentials originalCredentials =
352         UserCredentials.newBuilder()
353             .setClientId(CLIENT_ID_VALUE)
354             .setClientSecret(CLIENT_SECRET)
355             .setRefreshToken(REFRESH_TOKEN)
356             .setAccessToken(accessToken1)
357             .setHttpTransportFactory(transportFactory)
358             .build();
359 
360     authorizer.storeCredentials(USER_ID, originalCredentials);
361 
362     UserCredentials credentials1 = authorizer.getCredentials(USER_ID);
363 
364     assertEquals(REFRESH_TOKEN, credentials1.getRefreshToken());
365     assertEquals(accessTokenValue1, credentials1.getAccessToken().getTokenValue());
366     assertEquals(GRANTED_SCOPES, credentials1.getAccessToken().getScopes());
367 
368     // Refresh the token to get update from token server
369     credentials1.refresh();
370     assertEquals(REFRESH_TOKEN, credentials1.getRefreshToken());
371     assertEquals(accessTokenValue2, credentials1.getAccessToken().getTokenValue());
372     assertEquals(grantedRefreshScopes, credentials1.getAccessToken().getScopes());
373 
374     // Load a second credentials instance
375     UserCredentials credentials2 = authorizer.getCredentials(USER_ID);
376 
377     // Verify that token refresh stored the updated tokens
378     assertEquals(REFRESH_TOKEN, credentials2.getRefreshToken());
379     assertEquals(accessTokenValue2, credentials2.getAccessToken().getTokenValue());
380     assertEquals(grantedRefreshScopes, credentials2.getAccessToken().getScopes());
381   }
382 
383   @Test
getCredentialsFromCode_conevertsCodeToTokens()384   public void getCredentialsFromCode_conevertsCodeToTokens() throws IOException {
385     MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory();
386     transportFactory.transport.addClient(CLIENT_ID_VALUE, CLIENT_SECRET);
387     transportFactory.transport.addAuthorizationCode(
388         CODE, REFRESH_TOKEN, ACCESS_TOKEN_VALUE, GRANTED_SCOPES_STRING, null);
389     TokenStore tokenStore = new MemoryTokensStorage();
390     UserAuthorizer authorizer =
391         UserAuthorizer.newBuilder()
392             .setClientId(CLIENT_ID)
393             .setScopes(DUMMY_SCOPES)
394             .setTokenStore(tokenStore)
395             .setHttpTransportFactory(transportFactory)
396             .build();
397 
398     UserCredentials credentials = authorizer.getCredentialsFromCode(CODE, BASE_URI);
399 
400     assertEquals(REFRESH_TOKEN, credentials.getRefreshToken());
401     assertEquals(ACCESS_TOKEN_VALUE, credentials.getAccessToken().getTokenValue());
402     assertEquals(GRANTED_SCOPES, credentials.getAccessToken().getScopes());
403   }
404 
405   @Test
getCredentialsFromCode_additionalParameters()406   public void getCredentialsFromCode_additionalParameters() throws IOException {
407     MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory();
408     transportFactory.transport.addClient(CLIENT_ID_VALUE, CLIENT_SECRET);
409 
410     Map<String, String> additionalParameters = new HashMap<String, String>();
411     additionalParameters.put("param1", "value1");
412     additionalParameters.put("param2", "value2");
413 
414     String code2 = "code2";
415     String refreshToken2 = "refreshToken2";
416     String accessTokenValue2 = "accessTokenValue2";
417 
418     transportFactory.transport.addAuthorizationCode(
419         CODE, REFRESH_TOKEN, ACCESS_TOKEN_VALUE, GRANTED_SCOPES_STRING, null);
420     transportFactory.transport.addAuthorizationCode(
421         code2, refreshToken2, accessTokenValue2, GRANTED_SCOPES_STRING, additionalParameters);
422 
423     TokenStore tokenStore = new MemoryTokensStorage();
424     UserAuthorizer authorizer =
425         UserAuthorizer.newBuilder()
426             .setClientId(CLIENT_ID)
427             .setScopes(DUMMY_SCOPES)
428             .setTokenStore(tokenStore)
429             .setHttpTransportFactory(transportFactory)
430             .build();
431 
432     // Verify that the additional parameters are not attached to the post body when not specified
433     UserCredentials credentials = authorizer.getCredentialsFromCode(CODE, BASE_URI);
434     assertEquals(REFRESH_TOKEN, credentials.getRefreshToken());
435     assertEquals(ACCESS_TOKEN_VALUE, credentials.getAccessToken().getTokenValue());
436     assertEquals(GRANTED_SCOPES, credentials.getAccessToken().getScopes());
437 
438     // Verify that the additional parameters are attached to the post body when specified
439     credentials = authorizer.getCredentialsFromCode(code2, BASE_URI, additionalParameters);
440     assertEquals(refreshToken2, credentials.getRefreshToken());
441     assertEquals(accessTokenValue2, credentials.getAccessToken().getTokenValue());
442     assertEquals(GRANTED_SCOPES, credentials.getAccessToken().getScopes());
443 
444     // Verify that the additional parameters from previous request are not attached to the post body
445     credentials = authorizer.getCredentialsFromCode(CODE, BASE_URI);
446     assertEquals(REFRESH_TOKEN, credentials.getRefreshToken());
447     assertEquals(ACCESS_TOKEN_VALUE, credentials.getAccessToken().getTokenValue());
448     assertEquals(GRANTED_SCOPES, credentials.getAccessToken().getScopes());
449   }
450 
451   @Test(expected = NullPointerException.class)
getCredentialsFromCode_nullCode_throws()452   public void getCredentialsFromCode_nullCode_throws() throws IOException {
453     UserAuthorizer authorizer =
454         UserAuthorizer.newBuilder()
455             .setClientId(CLIENT_ID)
456             .setScopes(DUMMY_SCOPES)
457             .setTokenStore(new MemoryTokensStorage())
458             .build();
459 
460     authorizer.getCredentialsFromCode(null, BASE_URI);
461   }
462 
463   @Test
getAndStoreCredentialsFromCode_getAndStoresCredentials()464   public void getAndStoreCredentialsFromCode_getAndStoresCredentials() throws IOException {
465     final String accessTokenValue1 = "1/MkSJoj1xsli0AccessToken_NKPY2";
466     final String accessTokenValue2 = "2/MkSJoj1xsli0AccessToken_NKPY2";
467     MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory();
468     transportFactory.transport.addClient(CLIENT_ID_VALUE, CLIENT_SECRET);
469     transportFactory.transport.addAuthorizationCode(
470         CODE, REFRESH_TOKEN, accessTokenValue1, GRANTED_SCOPES_STRING, null);
471     TokenStore tokenStore = new MemoryTokensStorage();
472     UserAuthorizer authorizer =
473         UserAuthorizer.newBuilder()
474             .setClientId(CLIENT_ID)
475             .setScopes(DUMMY_SCOPES)
476             .setTokenStore(tokenStore)
477             .setHttpTransportFactory(transportFactory)
478             .build();
479 
480     UserCredentials credentials1 =
481         authorizer.getAndStoreCredentialsFromCode(USER_ID, CODE, BASE_URI);
482 
483     assertEquals(REFRESH_TOKEN, credentials1.getRefreshToken());
484     assertEquals(GRANTED_SCOPES, credentials1.getAccessToken().getScopes());
485     assertEquals(accessTokenValue1, credentials1.getAccessToken().getTokenValue());
486 
487     // Refresh the token to get update from token server
488     transportFactory.transport.addRefreshToken(REFRESH_TOKEN, accessTokenValue2);
489     credentials1.refresh();
490     assertEquals(REFRESH_TOKEN, credentials1.getRefreshToken());
491     assertEquals(accessTokenValue2, credentials1.getAccessToken().getTokenValue());
492 
493     // Load a second credentials instance
494     UserCredentials credentials2 = authorizer.getCredentials(USER_ID);
495 
496     // Verify that token refresh stored the updated tokens
497     assertEquals(REFRESH_TOKEN, credentials2.getRefreshToken());
498     assertEquals(GRANTED_SCOPES, credentials2.getAccessToken().getScopes());
499     assertEquals(accessTokenValue2, credentials2.getAccessToken().getTokenValue());
500   }
501 
502   @Test(expected = NullPointerException.class)
getAndStoreCredentialsFromCode_nullCode_throws()503   public void getAndStoreCredentialsFromCode_nullCode_throws() throws IOException {
504     UserAuthorizer authorizer =
505         UserAuthorizer.newBuilder()
506             .setClientId(CLIENT_ID)
507             .setScopes(DUMMY_SCOPES)
508             .setTokenStore(new MemoryTokensStorage())
509             .build();
510 
511     authorizer.getAndStoreCredentialsFromCode(USER_ID, null, BASE_URI);
512   }
513 
514   @Test(expected = NullPointerException.class)
getAndStoreCredentialsFromCode_nullUserId_throws()515   public void getAndStoreCredentialsFromCode_nullUserId_throws() throws IOException {
516     UserAuthorizer authorizer =
517         UserAuthorizer.newBuilder()
518             .setClientId(CLIENT_ID)
519             .setScopes(DUMMY_SCOPES)
520             .setTokenStore(new MemoryTokensStorage())
521             .build();
522 
523     authorizer.getAndStoreCredentialsFromCode(null, CODE, BASE_URI);
524   }
525 
526   @Test
revokeAuthorization_revokesAndClears()527   public void revokeAuthorization_revokesAndClears() throws IOException {
528     TokenStore tokenStore = new MemoryTokensStorage();
529     MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory();
530     transportFactory.transport.addClient(CLIENT_ID_VALUE, CLIENT_SECRET);
531     transportFactory.transport.addRefreshToken(
532         REFRESH_TOKEN, ACCESS_TOKEN_VALUE, GRANTED_SCOPES_STRING);
533     UserCredentials initialCredentials =
534         UserCredentials.newBuilder()
535             .setClientId(CLIENT_ID_VALUE)
536             .setClientSecret(CLIENT_SECRET)
537             .setRefreshToken(REFRESH_TOKEN)
538             .setAccessToken(ACCESS_TOKEN)
539             .build();
540 
541     UserAuthorizer authorizer =
542         UserAuthorizer.newBuilder()
543             .setClientId(CLIENT_ID)
544             .setScopes(DUMMY_SCOPES)
545             .setTokenStore(tokenStore)
546             .setHttpTransportFactory(transportFactory)
547             .build();
548 
549     authorizer.storeCredentials(USER_ID, initialCredentials);
550 
551     UserCredentials credentials1 = authorizer.getCredentials(USER_ID);
552 
553     assertEquals(REFRESH_TOKEN, credentials1.getRefreshToken());
554     credentials1.refresh();
555     assertEquals(ACCESS_TOKEN_VALUE, credentials1.getAccessToken().getTokenValue());
556     assertEquals(GRANTED_SCOPES, credentials1.getAccessToken().getScopes());
557 
558     authorizer.revokeAuthorization(USER_ID);
559 
560     try {
561       credentials1.refresh();
562       fail("Credentials should not refresh after revoke.");
563     } catch (IOException expected) {
564       // Expected
565     }
566     UserCredentials credentials2 = authorizer.getCredentials(USER_ID);
567     assertNull(credentials2);
568   }
569 
570   @Test(expected = IllegalArgumentException.class)
nullCodeVerifierPKCEProvider()571   public void nullCodeVerifierPKCEProvider() {
572     PKCEProvider pkce =
573         new PKCEProvider() {
574           @Override
575           public String getCodeVerifier() {
576             return null;
577           }
578 
579           @Override
580           public String getCodeChallengeMethod() {
581             return "dummy string";
582           }
583 
584           @Override
585           public String getCodeChallenge() {
586             return "dummy string";
587           }
588         };
589 
590     UserAuthorizer authorizer =
591         UserAuthorizer.newBuilder()
592             .setClientId(CLIENT_ID)
593             .setScopes(DUMMY_SCOPES)
594             .setTokenStore(new MemoryTokensStorage())
595             .setPKCEProvider(pkce)
596             .build();
597   }
598 
599   @Test(expected = IllegalArgumentException.class)
nullCodeChallengePKCEProvider()600   public void nullCodeChallengePKCEProvider() {
601     PKCEProvider pkce =
602         new PKCEProvider() {
603           @Override
604           public String getCodeVerifier() {
605             return "dummy string";
606           }
607 
608           @Override
609           public String getCodeChallengeMethod() {
610             return "dummy string";
611           }
612 
613           @Override
614           public String getCodeChallenge() {
615             return null;
616           }
617         };
618 
619     UserAuthorizer authorizer =
620         UserAuthorizer.newBuilder()
621             .setClientId(CLIENT_ID)
622             .setScopes(DUMMY_SCOPES)
623             .setTokenStore(new MemoryTokensStorage())
624             .setPKCEProvider(pkce)
625             .build();
626   }
627 
628   @Test(expected = IllegalArgumentException.class)
nullCodeChallengeMethodPKCEProvider()629   public void nullCodeChallengeMethodPKCEProvider() {
630     PKCEProvider pkce =
631         new PKCEProvider() {
632           @Override
633           public String getCodeVerifier() {
634             return "dummy string";
635           }
636 
637           @Override
638           public String getCodeChallengeMethod() {
639             return null;
640           }
641 
642           @Override
643           public String getCodeChallenge() {
644             return "dummy string";
645           }
646         };
647 
648     UserAuthorizer authorizer =
649         UserAuthorizer.newBuilder()
650             .setClientId(CLIENT_ID)
651             .setScopes(DUMMY_SCOPES)
652             .setTokenStore(new MemoryTokensStorage())
653             .setPKCEProvider(pkce)
654             .build();
655   }
656 }
657