Accessing oauth protected resource on Google App Engine - google-app-engine

I'm trying to access an OAuth-protected resource on Google App Engine using a Java/Groovy client. However the authentication is not working and my GET requests are just bringing back the Google Accounts login page HTML.
I get the same results with HTTPBuilder/signpost and with google-oauth-java-client.
Here's what I've done:
Set up an OAuth provider as described in http://ikaisays.com/2011/05/26/setting-up-an-oauth-provider-on-google-app-engine/
Created a 'hello world' servlet (actually a Gaelyk groovlet) mapped to http://<my-app>.appspot.com/rest/hello
Deployed the servlet to gae and confirmed I can GET via a browser.
Added a security constraint to my web.xml and redeployed.
<security-constraint>
<web-resource-collection>
<web-resource-name>Rest</web-resource-name>
<url-pattern>/rest/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
</security-constraint>
Confirmed that a browser GET requires a Google Accounts login and that after login I can access the servlet.
Did the 3-legged OAuth dance as described in http://groovy.codehaus.org/modules/http-builder/doc/auth.html to get the access and client secret tokens.
Use the tokens in a RESTClient as follows (following instructions in the link above)
def client = new RESTClient('http://<my-app>.appspot.com' )
def consumerKey = <my consumer key>
def consumerSecret = <my consumer secret>
def accessToken = <my access token>
def secretToken = <my secret token>
client.auth.oauth consumerKey, consumerSecret, accessToken, secretToken
def resp = client.get(path:'/rest/hello')
assert resp.data == 'Hello world'
The assert fails since the response is the Google Accounts login page.
I get the same behaviour when using google-oauth-java-client.
I've been through the process above several times, checking for copy/paste errors in the tokens and ensuring that I'm not getting the tokens mixed up.
This is with Groovy 1.8.2, OSX Java 1.6.0_29, HTTPBuilder 0.5.1, gaelyk 1.1.
Any ideas? Thanks.

OK, no response on this so here's how I worked around it.
I gave up on using oauth... google only claim 'experimental' status for this anyway so maybe it fundamentally doesn't work yet.
However I get good results using the ClientLogin protocol from my test client (equivalent to doing a manual login to Google Accounts like the one you do when accessing gmail)
I based this on the extremely useful article http://www.geekyblogger.com/2011/05/using-clientlogin-to-do-authentication.html. I had to extend in a few ways, code below:
import java.io.File;
import java.io.InputStream;
import java.io.LineNumberReader;
import java.io.StringReader;
import java.nio.charset.Charset;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import com.google.appengine.repackaged.com.google.common.io.Files;
import com.google.cloud.sql.jdbc.internal.Charsets;
public class Login {
public static void main(String[] args) throws Exception {
// This file contains my
// google password. Note that this has to be an app-specific
// password if you use 2-step verification
File passFile = new File("/Users/me/pass.txt");
String pass = Files.toString(passFile, Charsets.UTF_8);
String authCookie = loginToGoogle("myemail#gmail.com", pass,
"http://myapp.appspot.com");
DefaultHttpClient client = new DefaultHttpClient();
// A te
HttpGet get = new HttpGet("http://myapp.appspot.com/rest/blah");
get.setHeader("Cookie", authCookie);
HttpResponse response = client.execute(get);
response.getEntity().writeTo(System.out);
}
public static String loginToGoogle(String userid, String password,
String appUrl) throws Exception {
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(
"https://www.google.com/accounts/ClientLogin");
MultipartEntity reqEntity = new MultipartEntity();
reqEntity.addPart("accountType", new StringBody("HOSTED_OR_GOOGLE",
"text/plain", Charset.forName("UTF-8")));
reqEntity.addPart("Email", new StringBody(userid));
reqEntity.addPart("Passwd", new StringBody(password));
reqEntity.addPart("service", new StringBody("ah"));
reqEntity.addPart("source", new StringBody(
"YourCompany-YourApp-YourVersion"));
post.setEntity(reqEntity);
HttpResponse response = client.execute(post);
if (response.getStatusLine().getStatusCode() == 200) {
InputStream input = response.getEntity().getContent();
String result = IOUtils.toString(input);
String authToken = getAuthToken(result);
post = new HttpPost(appUrl + "/_ah/login?auth=" + authToken);
response = client.execute(post);
Header[] cookies = response.getHeaders("SET-COOKIE");
for (Header cookie : cookies) {
if (cookie.getValue().startsWith("ACSID=")) {
return cookie.getValue();
}
}
throw new Exception("ACSID cookie cannot be found");
} else
throw new Exception("Error obtaining ACSID");
}
private static String getAuthToken(String responseText) throws Exception {
LineNumberReader reader = new LineNumberReader(new StringReader(
responseText));
String line = reader.readLine();
while (line != null) {
line = line.trim();
if (line.startsWith("Auth=")) {
return line.substring(5);
}
line = reader.readLine();
}
throw new Exception("Could not find Auth token");
}
}

Related

Solr basic authentication without setting credentials for each request

Currently I am using SOLR basic authentication feature on my SOLR cloud using Solrj library. According to documentation and only way I found, the code looks like this -
SolrRequest<QueryResponse> req = new QueryRequest(solrQuery);
req.setBasicAuthCredentials("admin", "foobar");
QueryResponse rsp = req.process(solrClient, liveStreamCollection);
documentList = rsp.getResults();
I am wondering if there is a way to avoid setBasicAuthCredentials for each request and perform it only once per session on solrClient?
Edit
Above approach worked both for SolrClient and SolrCloudClient (both core and collection). To avoid passing basic credentials for each request, I tried to build and use a HttpClient like this.
AuthScope authScope = new AuthScope("192.168.x.x", 8983);
UsernamePasswordCredentials creds = new UsernamePasswordCredentials("admin", "foobar");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(authScope, creds);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.addInterceptorFirst(new PreemptiveAuthInterceptor());
builder.setDefaultCredentialsProvider(credsProvider);
CloseableHttpClient httpClient = builder.build();
solrClient = new HttpSolrClient("http://192.168.x.x:8983/solr/", httpClient);
And the PreemptiveAuthInterceptor class:
static class PreemptiveAuthInterceptor implements HttpRequestInterceptor {
#Override
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute(HttpClientContext.TARGET_AUTH_STATE);
// If no auth scheme available yet, try to initialize it preemptively
if (authState.getAuthScheme() == null) {
CredentialsProvider credsProvider = (CredentialsProvider)
context.getAttribute(HttpClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort());
Credentials creds = credsProvider.getCredentials(authScope);
if(creds == null){
}
authState.update(new BasicScheme(), creds);
}
}
}
This worked for a single machine Solr core but I don't know how to use this with Solr cloud(passing Zookeeper hosts etc). Any idea?
Thanks in advance!

Proper way to post to a servlet on Google App Engine from a Java client application

I have deployed an to App Engine. I setup SSL, and associated it with a custom domain. When I was developing the app locally, sending to a servlet via http://localhost:8080/servlet, worked as expected, but when I deploy it to App Engine, I have yet to get the appropriate result. I've tried many things, and the response codes I keep getting are either 404 or 500.
I startied with a simple HTTPUrlConnection and DataOutputstream to send a JSON to the servlet, and get an appropriate response. Like so:
URL url;
HttpURLConnection connection = null;
try {
url = new URL(targetURL);
connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("custom-Header", "XYZ");
connection.setRequestProperty("Content-Length", Integer.toString(urlParameters.length));
connection.setRequestProperty("Content-Language", "en-US");
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
//Send request
DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
wr.write(urlParameters);
wr.flush ();
wr.close ();
//Get Response
BufferedReader rd = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
StringBuffer response = new StringBuffer();
while((line = rd.readLine()) != null) {
response.append(line);
response.append('\r');
}
rd.close();
return response.toString();
}
finally {
if(connection != null) {
connection.disconnect();
}
}
This works locally.
I've now tried Apache Common's HttpAsyncClient, to check if it maybe a timing issue:
final ResponseObject responseObject = new ResponseObject(); //my simple POJO
try(CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom()
.setSSLStrategy(sslSessionStrategy)
.build()) {
httpclient.start();
HttpPost post = new HttpPost(url);
post.setHeader("Content-Type", "application/json");
post.setHeader("custom-Header", "XYZ");
post.setHeader("Content-Language", "en-US");
HttpEntity entity = new ByteArrayEntity(urlParameters);
post.setEntity(entity);
final CountDownLatch latch1 = new CountDownLatch(1);
httpclient.execute(post, new FutureCallback<HttpResponse>() {
public void completed(final HttpResponse response) {
int status = response.getStatusLine().getStatusCode();
if (status >= 200 && status < 300) {
HttpEntity entity = response.getEntity();
try {
responseObject.message = entity != null ? EntityUtils.toString(entity) : null;
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
responseObject.exception = new ClientProtocolException("Unexpected response status: " + status);
}
latch1.countDown();
}
public void failed(final Exception ex) {
responseObject.exception = ex;
latch1.countDown();
}
public void cancelled() {
latch1.countDown();
}
});
latch1.await();
if(responseObject.exception != null) {
throw responseObject.exception;
} else {
return responseObject.message;
}
}
This also works locally, but when trying to reach AppEngine, still no go.
Here's my simple web.xml:
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>my.servlet.package.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>everything</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<welcome-file-list>
<welcome-file>home.jsp</welcome-file>
</welcome-file-list>
Locally, I post to http://localhost:8080/login. As for App Engine, I posted to the ff:
https://myapp.appspot.com/login
https://myapp.customdomain.com/login
I've tried changing up the url-pattern. I started with /login, then did login, then explicitly tried the both App Engine and custom domain urls (i.e. myapp.appspot.com/login and myapp.mydomain.com/login). I also tried having an actual jsp or html page to post to, after trying not having an actual page associated with the servlet i.e. login.jsp or login.html.
When I used HttpAsyncClient (my choice due to SSLContext), the best result I got was the HTML of the page associated with the servlet, but never the response I need from the Servlet.
Any ideas?
Found the answer on one of Google Cloud Platform's scattered docs:
https://cloud.google.com/appengine/docs/standard/java/how-requests-are-handled
https://cloud.google.com/appengine/docs/standard/java/how-requests-are-routed
Basically, you'll have to prepend your project's appspot url e.g. myapp.appspot.com, with the version ID of any actively serving instance of your app. You can find such IDs under the Versions page of your App Engine console. For example: https://versionId.myapp.appspot.com.

Google Plus credentials for application Login

I am creating an app and I want to confirm user using the Google credentials in Java environment. It can be done using google API but I am not sure how to code it as a servlet.
I found a code snippet to authorize the credentials but the AuthorizationCodeInstalledApp() is throwing an error and I am not sure which api to use.
private static Credential authorize() throws Exception {
// load client secrets
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY,
new InputStreamReader(Test.class.getResourceAsStream("/client_secrets.json")));
if (clientSecrets.getDetails().getClientId().startsWith("Enter")
|| clientSecrets.getDetails().getClientSecret().startsWith("Enter ")) {
System.out.println(
"Enter Client ID and Secret from https://code.google.com/apis/console/?api=plus "
+ "into plus-cmdline-sample/src/main/resources/client_secrets.json");
System.exit(1);
}
// set up authorization code flow
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
httpTransport, JSON_FACTORY, clientSecrets,
Collections.singleton(PlusScopes.PLUS_ME)).setDataStoreFactory(
dataStoreFactory).build();
// authorize
return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
}
Hope someone can help me with this and also let me know the process it'll be great...
If you really are writing a servlet to run under Google AppEngine, it is much much easier than that.
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
...
#Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException {
UserService userService = UserServiceFactory.getUserService();
User currentUser = userService.getCurrentUser();
if (currentUser == null) {
resp.sendRedirect(userService.createLoginURL(req.getRequestURI()));
}
else {
// show the view
}
}

i need to make a java.net url fetch with a json request. Please tell me how can i call that using java in google appengine

This is my HTTP URL
POST HTTPS://www.googleapis.com/admin/directory/v1/group
MY json request
{
"email": "sales_group#example.com",
"name": "Sales Group",
"description": "This is the Sales group."
}
I am using Directory API to create groups.
I never used URL fetch so far so i am not familiar with that.
Please help me how can i do that..
THIS IS MY ANSWER I POSTED AFTER 2 HOURS. stackoverflow did not allow me to to answer my own question before 8 hours since i have less than 10 reputation, so forgive me for long question.
I tried this..
i was struggling a bit in passing json as parameter. here is my code
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.logging.Logger;
import javax.servlet.http.*;
import com.google.appengine.labs.repackaged.org.json.JSONException;
import com.google.appengine.labs.repackaged.org.json.JSONObject;
#SuppressWarnings("serial")
public class DirectoryApiExampleServlet extends HttpServlet {
static Logger log = gger.getLogger(DirectoryApiExampleServlet.class.getName());
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
URL url = new URL("https://www.googleapis.com/admin/directory/v1/groups");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
JSONObject json = new JSONObject();
try {
json.put("email", "abc#gmail.com");
json.put("name", "Test Group");
json.put("description", "this is a test group");
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
writer.write(json.toString());
writer.flush();
writer.close();
log.info("connection.getResponseCode()"+connection.getResponseCode());
}
}
But it is giving 401 response which is not expected.
Where am i making mistake???
There is documentation available on how to make a POST request. An example is also provided. Check out the following section : https://developers.google.com/appengine/docs/java/urlfetch/usingjavanet#Using_HttpURLConnection
You need to add authorization header with Access Token
Authorization : Bearer ya29.vQE48oltidkR

User is null in the Google Cloud Api

I followed the instructions in this tutorial.
https://developers.google.com/appengine/docs/java/endpoints/getstarted/auth
when i deployed my code. and went to test my app.
with the following url
http://chandru-compute.appspot.com/_ah/api/explorer
My
helloworld.greetings.multiply and
helloworld.greetings.getGreeting works as expected.
But i have issues with the helloworld.greetings.authed method.
The user object is always null.
Here is the code.
package com.google.devrel.samples.helloendpoints;
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import javax.inject.Named;
import java.util.ArrayList;
/**
* Defines v1 of a helloworld API, which provides simple "greeting" methods.
*/
#Api(
name = "helloworld",
version = "v1",
clientIds = {com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID}
)
public class Greetings {
public static ArrayList<Greeting> greetings = new ArrayList<Greeting>();
static {
greetings.add(new Greeting("hello world!"));
greetings.add(new Greeting("goodbye world!"));
}
public Greeting getGreeting(#Named("id") Integer id) {
return greetings.get(id);
}
#ApiMethod(name = "greetings.multiply", httpMethod = "post")
public Greeting insertGreeting(#Named("times") Integer times, Greeting greeting) {
Greeting response = new Greeting();
StringBuilder responseBuilder = new StringBuilder();
for (int i = 0; i < times; i++) {
responseBuilder.append(greeting.getMessage());
}
response.setMessage(responseBuilder.toString());
return response;
}
#ApiMethod(name = "greetings.authed", path = "greeting/authed")
public Greeting authedGreeting(User user) {
//Greeting response = new Greeting("hello " + user.getEmail());
Greeting response;
if (user == null) {
UserService userService = UserServiceFactory.getUserService();
User user2 = userService.getCurrentUser();
String text = null;
if (user2 != null){
text = user2.getEmail();
}
response = new Greeting("hello world : Email2" + text );
} else {
response = new Greeting("hello world : Email " + user.getEmail() );
}
return response;
}
}
I had same problem, it helped for me to add
scopes = {"https://www.googleapis.com/auth/userinfo.email"}
into my Greetings #Api annotation. So the whole final #Apilook like
#Api(
name = "helloworld",
version = "v1",
clientIds = { com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID },
scopes = {"https://www.googleapis.com/auth/userinfo.email"}
)
Then deploy, reload Api Explorer page and also turn on "Authorize requests using OAuth 2.0" option with same scope.
I am getting the same problem. And if you throw an OAuthRequestException Exception and test the service via the API Explorer console, you will get a message saying This method requires you to be authenticated. You may need to activate the toggle above to authorize your request using OAuth 2.0. When you try to enable the OAuth 2.0 toggle it requests in a new window to Select OAuth 2.0 scopes, and I have not been able to find which scopes are needed or figure out how I can test a cloud end-point service with authorization from the API Explorer console.
First of all, in the API explorer, you need to authenticate the request with OAuth using the Authorize requests using OAuth 2.0 toggle in the user interface.
If the user is still null check that among the client ids there is the ID for the API explorer
#Api(
name = "myAPIName",
version = "v1",
clientIds = { com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID }
)
This is the only thing that is needed to obtain a not null User argument.

Resources