1 // Copyright 2010 Google Inc. All Rights Reserved.
3 package com
.google
.appengine
.tools
.remoteapi
;
5 import org
.apache
.commons
.httpclient
.Cookie
;
7 import java
.io
.IOException
;
8 import java
.net
.URLEncoder
;
9 import java
.util
.Arrays
;
10 import java
.util
.HashMap
;
11 import java
.util
.List
;
15 * Handles logging into App Engine using
16 * <a href="http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html"
17 * >ClientLogin</a>. This class does not rely on any particular mechanism for
18 * sending HTTP requests to ClientLogin. It instead exposes template methods
19 * for subclasses to implement using different mechanisms.
22 abstract class ClientLogin
{
25 * Authenticates the user using ClientLogin. This requires two HTTP requests,
26 * one to get a token from Google and one to exchange it for cookies from App Engine.
28 * @deprecated ClientLogin authentication is deprecated and will soon be shut down.
29 * Use OAuth2.0 instead to obtain credentials.
32 public List
<Cookie
> login(String host
, String email
, String password
) throws IOException
{
33 if (email
== null || email
.isEmpty()) {
34 throw new IllegalArgumentException("email not set");
36 if (password
== null || password
.isEmpty()) {
37 throw new IllegalArgumentException("password not set");
39 List
<String
[]> postParams
= getClientLoginPostParams(email
, password
);
40 PostResponse authResponse
= executePost(
41 "https://www.google.com/accounts/ClientLogin", postParams
);
42 String token
= processAuthResponse(authResponse
, email
);
43 String url
= "https://" + host
+ "/_ah/login"
44 + "?auth=" + URLEncoder
.encode(token
, "UTF-8")
45 + "&continue=http://localhost/";
46 return getAppEngineLoginCookies(url
);
50 * Provides the parameter names and values that need to be posted to the
51 * ClientLogin url. Each element of the returned {@link List} is an array of
52 * size 2 where the String at index 0 is the parameter name and the String at
53 * index 1 is the parameter value.
55 private static List
<String
[]> getClientLoginPostParams(String email
, String password
) {
57 new String
[] {"Email", email
},
58 new String
[] {"Passwd", password
},
59 new String
[] {"service", "ah"},
60 new String
[] {"source", "Google-remote_api-java-1.0"},
61 new String
[] {"accountType", "HOSTED_OR_GOOGLE"});
64 private String
processAuthResponse(PostResponse authResponse
, String email
)
65 throws LoginException
{
66 if (authResponse
.statusCode
== 200 || authResponse
.statusCode
== 403) {
67 Map
<String
, String
> responseMap
= parseClientLoginResponse(authResponse
.body
);
68 if (authResponse
.statusCode
== 200) {
69 return responseMap
.get("Auth");
71 String reason
= responseMap
.get("Error");
72 if ("BadAuthentication".equals(reason
)) {
73 String info
= responseMap
.get("Info");
74 if (info
!= null && !info
.isEmpty()) {
75 reason
= reason
+ " " + info
;
78 throw new LoginException("Login failed. Reason: " + reason
);
80 } else if (authResponse
.statusCode
== 401) {
81 throw new LoginException("Email \"" + email
+ "\" and password do not match.");
83 throw new LoginException("Bad authentication response: " + authResponse
.statusCode
);
88 * Parses the key-value pairs in the ClientLogin response.
90 private static Map
<String
, String
> parseClientLoginResponse(String body
) {
91 Map
<String
, String
> response
= new HashMap
<String
, String
>();
92 for (String line
: body
.split("\n")) {
93 int eqIndex
= line
.indexOf("=");
95 response
.put(line
.substring(0, eqIndex
), line
.substring(eqIndex
+ 1));
102 * Executes an HTTP Post to the provided url with the provided params.
104 abstract PostResponse
executePost(String url
, List
<String
[]> postParams
) throws IOException
;
107 * Logs into App Engine and returns the cookies needed to make authenticated requests.
109 abstract List
<Cookie
> getAppEngineLoginCookies(String url
) throws IOException
;
112 * Represents a post response.
114 static class PostResponse
{
115 final int statusCode
;
117 PostResponse(int statusCode
, String body
) {
118 this.statusCode
= statusCode
;