/* * Copyright (c) 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.api.client.auth.oauth2; import com.google.api.client.http.BasicAuthentication; import com.google.api.client.http.HttpRequest; import com.google.api.client.http.HttpRequestFactory; import com.google.api.client.http.HttpStatusCodes; import com.google.api.client.http.LowLevelHttpRequest; import com.google.api.client.http.LowLevelHttpResponse; import com.google.api.client.http.UrlEncodedContent; import com.google.api.client.testing.http.HttpTesting; import com.google.api.client.testing.http.MockHttpTransport; import com.google.api.client.testing.http.MockLowLevelHttpRequest; import com.google.api.client.testing.http.MockLowLevelHttpResponse; import java.util.Map; /** * Tests {@link Credential} and {@link BearerToken}. * * @author Yaniv Inbar */ public class CredentialTest extends AuthenticationTestBase { public void testConstructor_header() throws Exception { Credential credential = new Credential(BearerToken.authorizationHeaderAccessMethod()).setAccessToken(ACCESS_TOKEN); HttpRequest request = subtestConstructor(credential); assertEquals("Bearer abc", request.getHeaders().getAuthorization()); } public void testConstructor_queryParam() throws Exception { Credential credential = new Credential(BearerToken.queryParameterAccessMethod()).setAccessToken(ACCESS_TOKEN); HttpRequest request = subtestConstructor(credential); assertEquals(ACCESS_TOKEN, request.getUrl().get("access_token")); } public void testConstructor_body() throws Exception { Credential credential = new Credential(BearerToken.formEncodedBodyAccessMethod()).setAccessToken(ACCESS_TOKEN); HttpRequest request = subtestConstructor(credential); assertEquals(ACCESS_TOKEN, ((Map<?, ?>) ((UrlEncodedContent) request.getContent()).getData()).get("access_token")); } private HttpRequest subtestConstructor(Credential credential) throws Exception { MockHttpTransport transport = new MockHttpTransport(); HttpRequestFactory requestFactory = transport.createRequestFactory(credential); HttpRequest request = requestFactory.buildDeleteRequest(HttpTesting.SIMPLE_GENERIC_URL); request.execute(); return request; } public void testConstructor_expiredHeader() throws Exception { HttpRequest request = subtestConstructor_expired(BearerToken.authorizationHeaderAccessMethod(), new CheckAuth() { public boolean checkAuth(MockLowLevelHttpRequest req) { return req.getFirstHeaderValue("Authorization").equals("Bearer def"); } }); assertEquals("Bearer def", request.getHeaders().getAuthorization()); } public void testConstructor_expiredQueryParam() throws Exception { HttpRequest request = subtestConstructor_expired(BearerToken.queryParameterAccessMethod(), new CheckAuth() { public boolean checkAuth(MockLowLevelHttpRequest req) { return req.getUrl().contains("access_token=def"); } }); assertEquals(NEW_ACCESS_TOKEN, request.getUrl().get("access_token")); } public void testConstructor_expiredBody() throws Exception { HttpRequest request = subtestConstructor_expired(BearerToken.formEncodedBodyAccessMethod(), new CheckAuth() { public boolean checkAuth(MockLowLevelHttpRequest req) { return NEW_ACCESS_TOKEN.equals(((Map<?, ?>) ((UrlEncodedContent) req .getStreamingContent()).getData()).get("access_token")); } }); assertEquals(NEW_ACCESS_TOKEN, ((Map<?, ?>) ((UrlEncodedContent) request.getContent()).getData()).get("access_token")); } interface CheckAuth { boolean checkAuth(MockLowLevelHttpRequest req); } private HttpRequest subtestConstructor_expired( Credential.AccessMethod method, final CheckAuth checkAuth) throws Exception { final Credential credential = new Credential.Builder(method).setTransport(new AccessTokenTransport()) .setJsonFactory(JSON_FACTORY) .setTokenServerUrl(TOKEN_SERVER_URL) .setClientAuthentication(new BasicAuthentication(CLIENT_ID, CLIENT_SECRET)) .build() .setAccessToken(ACCESS_TOKEN) .setRefreshToken(REFRESH_TOKEN); class MyTransport extends MockHttpTransport { boolean resetAccessToken; @Override public LowLevelHttpRequest buildRequest(String method, String url) { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() { MockLowLevelHttpResponse response = new MockLowLevelHttpResponse(); if (!checkAuth.checkAuth(this)) { response.setStatusCode(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED); if (resetAccessToken) { credential.setAccessToken(NEW_ACCESS_TOKEN); } } return response; } }; } } MyTransport transport = new MyTransport(); HttpRequestFactory requestFactory = transport.createRequestFactory(credential); HttpRequest request = requestFactory.buildDeleteRequest(HttpTesting.SIMPLE_GENERIC_URL); request.execute(); credential.setAccessToken(ACCESS_TOKEN); transport.resetAccessToken = true; request.execute(); return request; } public void testRefreshToken_noRefreshToken() throws Exception { Credential access = new Credential(BearerToken.queryParameterAccessMethod()).setAccessToken(ACCESS_TOKEN); assertFalse(access.refreshToken()); } public void testRefreshToken_noRefreshToken2() throws Exception { AccessTokenTransport transport = new AccessTokenTransport(); Credential access = new Credential.Builder(BearerToken.queryParameterAccessMethod()).setTransport(transport) .setJsonFactory(JSON_FACTORY) .setTokenServerUrl(TOKEN_SERVER_URL) .setClientAuthentication(new BasicAuthentication(CLIENT_ID, CLIENT_SECRET)) .build() .setAccessToken(ACCESS_TOKEN); assertFalse(access.refreshToken()); assertEquals(ACCESS_TOKEN, access.getAccessToken()); assertNull(access.getRefreshToken()); assertNull(access.getExpirationTimeMilliseconds()); } public void testRefreshToken_refreshToken() throws Exception { AccessTokenTransport transport = new AccessTokenTransport(); Credential access = new Credential.Builder(BearerToken.queryParameterAccessMethod()).setTransport(transport) .setJsonFactory(JSON_FACTORY) .setTokenServerUrl(TOKEN_SERVER_URL) .setClientAuthentication(new BasicAuthentication(CLIENT_ID, CLIENT_SECRET)) .build() .setRefreshToken(REFRESH_TOKEN) .setAccessToken(ACCESS_TOKEN); assertTrue(access.refreshToken()); assertEquals(NEW_ACCESS_TOKEN, access.getAccessToken()); assertEquals(NEW_REFRESH_TOKEN, access.getRefreshToken()); assertNotNull(access.getExpirationTimeMilliseconds()); } public void testRefreshToken_request_401() throws Exception { AccessTokenTransport transport = new AccessTokenTransport(); transport.statusCode = 401; // 3 requests = 1 invalid token, 1 refresh token, and 1 retry subtestRefreshToken_request(transport, 3); } public void testRefreshToken_request_www_authenticate() throws Exception { AccessTokenTransport transport = new AccessTokenTransport(); transport.statusCode = 444; transport.wwwAuthenticate = "Bearer realm=\"https://www.google.com/accounts/AuthSubRequest\" error=invalid_token"; // WWW-Authenticate contains invalid_token error, so we expect 3 requests = 1 invalid token, 1 // refresh token, and 1 retry subtestRefreshToken_request(transport, 3); transport = new AccessTokenTransport(); transport.statusCode = 401; transport.wwwAuthenticate = "Bearer error=invalid_token"; // WWW-Authenticate contains invalid_token error, so we expect 3 requests = 1 invalid token, 1 // refresh token, and 1 retry subtestRefreshToken_request(transport, 3); transport = new AccessTokenTransport(); transport.statusCode = 401; transport.wwwAuthenticate = "doesn't contain b-e-a-r-e-r"; // WWW-Authenticate doesn't contain "Bearer" but the status code is 401, so we expect 3 requests // = 1 invalid token, 1 refresh token, and 1 retry subtestRefreshToken_request(transport, 3); transport = new AccessTokenTransport(); transport.statusCode = 401; transport.wwwAuthenticate = "Bearer blah blah blah"; // WWW-Authenticate contains "Bearer" but no invalid_token error, and although the error code is // 401, we expect only 1 failed request subtestRefreshToken_request(transport, 1); transport = new AccessTokenTransport(); transport.statusCode = 444; transport.wwwAuthenticate = "Bearer blah blah blah"; // WWW-Authenticate contains "Bearer" but no invalid_token error, we expect only 1 failed // request subtestRefreshToken_request(transport, 1); transport = new AccessTokenTransport(); transport.statusCode = 444; transport.wwwAuthenticate = "doesn't contain b-e-a-r-e-r"; // WWW-Authenticate doesn't contain "Bearer" and no 401, we expect only 1 failed request subtestRefreshToken_request(transport, 1); } private void subtestRefreshToken_request(AccessTokenTransport transport, int expectedCalls) throws Exception { Credential credential = new Credential.Builder(BearerToken.queryParameterAccessMethod()).setTransport(transport) .setJsonFactory(JSON_FACTORY) .setTokenServerUrl(TOKEN_SERVER_URL) .setClientAuthentication(new BasicAuthentication(CLIENT_ID, CLIENT_SECRET)) .build() .setRefreshToken(REFRESH_TOKEN) .setAccessToken(ACCESS_TOKEN); HttpRequestFactory requestFactory = transport.createRequestFactory(credential); HttpRequest request = requestFactory.buildDeleteRequest(HttpTesting.SIMPLE_GENERIC_URL); request.setThrowExceptionOnExecuteError(false); request.execute(); assertEquals(expectedCalls, transport.calls); } public void testRefreshToken_withoutRequiredParameters() { Credential access = new Credential(BearerToken.queryParameterAccessMethod()); try { access.setRefreshToken(REFRESH_TOKEN); fail("Expected an " + IllegalArgumentException.class); } catch (IllegalArgumentException e) { // Expected } } public void testRefreshToken_refreshTokenErrorWith400() throws Exception { AccessTokenTransport transport = new AccessTokenTransport(); transport.statusCode = 400; Credential access = new Credential.Builder(BearerToken.queryParameterAccessMethod()).setTransport(transport) .setJsonFactory(JSON_FACTORY) .setTokenServerUrl(TOKEN_SERVER_URL) .setClientAuthentication(new BasicAuthentication(CLIENT_ID, CLIENT_SECRET)) .build() .setExpiresInSeconds(3600L) .setAccessToken(ACCESS_TOKEN) .setRefreshToken(REFRESH_TOKEN); try { access.refreshToken(); fail("Expected " + TokenResponseException.class); } catch (TokenResponseException e) { // Expected } assertNull(access.getAccessToken()); assertEquals("refreshToken", access.getRefreshToken()); assertNull(access.getExpirationTimeMilliseconds()); } public void testRefreshToken_refreshTokenErrorWith500() throws Exception { AccessTokenTransport transport = new AccessTokenTransport(); transport.statusCode = 500; Credential access = new Credential.Builder(BearerToken.queryParameterAccessMethod()).setTransport(transport) .setJsonFactory(JSON_FACTORY) .setTokenServerUrl(TOKEN_SERVER_URL) .setClientAuthentication(new BasicAuthentication(CLIENT_ID, CLIENT_SECRET)) .build() .setExpiresInSeconds(3600L) .setAccessToken(ACCESS_TOKEN) .setRefreshToken(REFRESH_TOKEN); assertFalse(access.refreshToken()); assertNotNull(access.getAccessToken()); assertEquals("refreshToken", access.getRefreshToken()); assertNotNull(access.getExpirationTimeMilliseconds()); } public void testInvalidTokenErrorMatcher() { String withQuote = "error = \"invalid_token\""; String withoutQuote = "error = invalid_token"; assertTrue(BearerToken.INVALID_TOKEN_ERROR.matcher(withQuote).find()); assertTrue(BearerToken.INVALID_TOKEN_ERROR.matcher(withoutQuote).find()); } }