/*
* Copyright (c) 2010 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.googleapis.auth.clientlogin;
import com.google.api.client.googleapis.GoogleHeaders;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpExecuteInterceptor;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpResponseException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.UrlEncodedContent;
import com.google.api.client.util.Key;
import com.google.api.client.util.StringUtils;
import java.io.IOException;
/**
* Client Login authentication method as described in <a
* href="http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html" >ClientLogin for
* Installed Applications</a>.
*
* @since 1.0
* @author Yaniv Inbar
*/
public final class ClientLogin {
/**
* HTTP transport required for executing request in {@link #authenticate()}.
*
* @since 1.3
*/
public HttpTransport transport;
/**
* URL for the Client Login authorization server.
*
* <p>
* By default this is {@code "https://www.google.com"}, but it may be overridden for testing
* purposes.
* </p>
*
* @since 1.3
*/
public GenericUrl serverUrl = new GenericUrl("https://www.google.com");
/**
* Short string identifying your application for logging purposes of the form:
* "companyName-applicationName-versionID".
*/
@Key("source")
public String applicationName;
/**
* Name of the Google service you're requesting authorization for, for example {@code "cl"} for
* Google Calendar.
*/
@Key("service")
public String authTokenType;
/** User's full email address. */
@Key("Email")
public String username;
/** User's password. */
@Key("Passwd")
public String password;
/**
* Type of account to request authorization for. Possible values are:
*
* <ul>
* <li>GOOGLE (get authorization for a Google account only)</li>
* <li>HOSTED (get authorization for a hosted account only)</li>
* <li>HOSTED_OR_GOOGLE (get authorization first for a hosted account; if attempt fails, get
* authorization for a Google account)</li>
* </ul>
*
* Use HOSTED_OR_GOOGLE if you're not sure which type of account you want authorization for. If
* the user information matches both a hosted and a Google account, only the hosted account is
* authorized.
*
* @since 1.1
*/
@Key
public String accountType;
/** (optional) Token representing the specific CAPTCHA challenge. */
@Key("logintoken")
public String captchaToken;
/** (optional) String entered by the user as an answer to a CAPTCHA challenge. */
@Key("logincaptcha")
public String captchaAnswer;
/**
* Key/value data to parse a success response.
*
* <p>
* Sample usage, taking advantage that this class implements {@link HttpRequestInitializer}:
* </p>
*
* <pre>
public static HttpRequestFactory createRequestFactory(
HttpTransport transport, Response response) {
return transport.createRequestFactory(response);
}
* </pre>
*
* <p>
* If you have a custom request initializer, take a look at the sample usage for
* {@link HttpExecuteInterceptor}, which this class also implements.
* </p>
*/
public static final class Response implements HttpExecuteInterceptor, HttpRequestInitializer {
/** Authentication token. */
@Key("Auth")
public String auth;
/** Returns the authorization header value to use based on the authentication token. */
public String getAuthorizationHeaderValue() {
return GoogleHeaders.getGoogleLoginValue(auth);
}
public void initialize(HttpRequest request) {
request.setInterceptor(this);
}
public void intercept(HttpRequest request) {
request.getHeaders().setAuthorization(getAuthorizationHeaderValue());
}
}
/** Key/value data to parse an error response. */
public static final class ErrorInfo {
@Key("Error")
public String error;
@Key("Url")
public String url;
@Key("CaptchaToken")
public String captchaToken;
@Key("CaptchaUrl")
public String captchaUrl;
}
/**
* Authenticates based on the provided field values.
*
* @throws ClientLoginResponseException if the authentication response has an error code, such as
* for a CAPTCHA challenge.
* @throws IOException some other kind of I/O exception
*/
public Response authenticate() throws ClientLoginResponseException, IOException {
GenericUrl url = serverUrl.clone();
url.appendRawPath("/accounts/ClientLogin");
HttpRequest request =
transport.createRequestFactory().buildPostRequest(url, new UrlEncodedContent(this));
request.setParser(AuthKeyValueParser.INSTANCE);
request.setContentLoggingLimit(0);
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();
// check for an HTTP success response (2xx)
if (response.isSuccessStatusCode()) {
return response.parseAs(Response.class);
}
// On error, throw a ClientLoginResponseException with the parsed error details
ErrorInfo details = response.parseAs(ErrorInfo.class);
String detailString = details.toString();
StringBuilder message = HttpResponseException.computeMessageBuffer(response);
if (!com.google.common.base.Strings.isNullOrEmpty(detailString)) {
message.append(StringUtils.LINE_SEPARATOR).append(detailString);
}
throw new ClientLoginResponseException(response, details, message.toString());
}
}