Google Books API 403 Access Not Configured - android

I'm trying to contact the Google Books API and perform a title search, which only requires a public API key and no OAUTH2. All I get is the following error:
{
"error": {
"errors": [
{
"domain": "usageLimits",
"reason": "accessNotConfigured",
"message": "Access Not Configured"
}
],
"code": 403,
"message": "Access Not Configured"
}
}
After having googled around for hours, it seems many others have the same problem but with other Google APIs. What I've done so far:
Registered a project in my Developer Console
Enabled the Books API
Signed my application to get the SHA1 certificate number
Chosen to get a public API key for Android in my Developer Console
Pasted the following string into the public API key form, in order to get the key: "SHA1 number;com.package", without quotes
Copy pasted the generated key into my code.
The code looks as follows:
private void callGoogleBooks(){
String key = MY_KEY;
String query = "https://www.googleapis.com/books/v1/volumes?q=flowers+inauthor:keyes&key=" + key;
Log.d("google books", callApi(query));
}
public String callApi(String query){
HttpClient httpClient = new DefaultHttpClient();
HttpGet getRequest = new HttpGet(query);
HttpResponse httpResponse = null;
try{
httpResponse = httpClient.execute(getRequest);
} catch(UnsupportedEncodingException e){
Log.d("ERROR", e.getMessage());
} catch(ClientProtocolException e){
Log.d("ERROR", e.getMessage());
} catch (IOException e){
Log.d("ERROR", e.getMessage());
}
if(httpResponse != null){
try{
HttpEntity httpEntity = httpResponse.getEntity();
InputStream is = httpEntity.getContent();
BufferedReader br = new BufferedReader(
new InputStreamReader(is, "utf-8"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while((line = br.readLine()) != null){
sb.append(line + "\n");
}
is.close();
String responseString = sb.toString();
return responseString;
} catch (Exception e){
Log.d("ERROR", e.getMessage());
}
}
return null;
}
Are there any obvious errors? Do I need to format or package my request differently?
Do I need to add anything to the manifest file?
When specifying the package when generating the public API key, do I need to specify the same package name as in my app structure? I read somewhere that it has to be unique, but changing it to something less likely to be a duplication resulted in the same error.
The error apparently has to do with "usageLimits", but I'm not even close to 1% of the 1000 calls allowed per day in my test project.
I've also tried to implement the Google Books Java Sample without using the code above, getting the same error message. I've also tried disabling and re-enabling the Books API, without any luck.
Thanks in advance.

This worked for me
String link = "https://www.googleapis.com/books/v1/volumes?q="+params;
InputStream is = null;
try
{
int timeoutConnection = 10000;
URL url = new URL(link);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(timeoutConnection);
con.setReadTimeout(timeoutConnection);
con.setRequestProperty("key", "API_KEY");
if(con.getResponseCode() != HttpURLConnection.HTTP_OK){
publishProgress("Error conneting.");
}
is=con.getInputStream();
}
from this thread: Google Books API for Android - Access Not Configured

Problem is when setting up your API key restriction for android app, you specified the package name and SHA-1 certificate fingerprint. Therefore your API key will only accept request from your app with package name and SHA-1 certificate fingerprint specified.
So how google know that request's sent FROM YOUR ANDROID APP? You MUST add your app's package name and SHA certificate in the header of each request with following keys:
Key: "X-Android-Package", value: your app package name
Key: "X-Android-Cert", value: SHA-1 certificate of your apk
FIRST, get your app SHA signature (you will need Guava library):
/**
* Gets the SHA1 signature, hex encoded for inclusion with Google Cloud Platform API requests
*
* #param packageName Identifies the APK whose signature should be extracted.
* #return a lowercase, hex-encoded
*/
public static String getSignature(#NonNull PackageManager pm, #NonNull String packageName) {
try {
PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
if (packageInfo == null
|| packageInfo.signatures == null
|| packageInfo.signatures.length == 0
|| packageInfo.signatures[0] == null) {
return null;
}
return signatureDigest(packageInfo.signatures[0]);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
private static String signatureDigest(Signature sig) {
byte[] signature = sig.toByteArray();
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] digest = md.digest(signature);
return BaseEncoding.base16().lowerCase().encode(digest);
} catch (NoSuchAlgorithmException e) {
return null;
}
}
Then, add package name and SHA certificate signature to request header:
java.net.URL url = new URL(REQUEST_URL);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
try {
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
connection.setRequestProperty("Accept", "application/json");
// add package name to request header
String packageName = mActivity.getPackageName();
connection.setRequestProperty("X-Android-Package", packageName);
// add SHA certificate to request header
String sig = getSignature(mActivity.getPackageManager(), packageName);
connection.setRequestProperty("X-Android-Cert", sig);
connection.setRequestMethod("POST");
// ADD YOUR REQUEST BODY HERE
// ....................
} catch (Exception e) {
e.printStackTrace();
} finally {
connection.disconnect();
}
Hope this help! :)

Related

Usage of Google Translate API in Android

I have been searching everywhere on the Internet for Google Translate API usage but I wasn't able find descent tutorial or explanation. So, here is what I have done:
In my Google API Console I have generated a key under Public API access with my SHA1 Fingerprint using this answer. Here is how my API console looks like:
In Android studio I build and send my request using OkHttp library with this code:
OkHttpClient client = new OkHttpClient();
String apiKey = "My API key";
String apiLangSource = "en";
String apiLangTarget = "de";
String apiWord = "Hello";
String googleApiUrl = "https://www.googleapis.com/language/translate/v2?key=" + apiKey + "&source=" + apiLangSource + "&target=" + apiLangTarget + "&q=" + apiWord;
Request request = new Request.Builder().url(googleApiUrl).build();
Log.d(TAG, "API STRING" + googleApiUrl);
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
Log.d(TAG , "HTTP CALL FAIL");
}
#Override
public void onResponse(Response response) throws IOException {
Log.d(TAG , response.body().string());
}
});
It runs fine but on response I get:
{
"error": {
"errors": [
{
"domain": "usageLimits",
"reason": "ipRefererBlocked",
"message": "There is a per-IP or per-Referer restriction configured on your API key and the request does not match these restrictions. Please use the Google Developers Console to update your API key configuration if request from this IP or referer should be allowed.",
"extendedHelp": "https://console.developers.google.com"
}
],
"code": 403,
"message": "There is a per-IP or per-Referer restriction configured on your API key and the request does not match these restrictions. Please use the Google Developers Console to update your API key configuration if request from this IP or referer should be allowed."
}
}
What is the problem here? Is my API set up correctly? Am I making the call correctly (I've seen some libraries but with guide)? Is this reasonable way of using this library? What exacty does this mean?
"There is a per-IP or per-Referer restriction configured on your API key and the request does not match these restrictions. Please use the Google Developers Console to update your API key configuration if request from this IP or referer should be allowed."
I think there are some demo calls available for free and this isn't the problem here.
Problem is when setting up your API key restriction for android app, you specified the package name and SHA-1 certificate fingerprint. Therefore your API key will only accept request from your app with package name and SHA-1 certificate fingerprint specified.
So how google know that request's sent FROM YOUR ANDROID APP? You MUST add your app's package name and SHA-1 in the header of each request with following keys:
Key: "X-Android-Package", value: your app package name
Key: "X-Android-Cert", value: SHA-1 certificate of your apk
FIRST, get your app SHA signature (you will need Guava library):
/**
* Gets the SHA1 signature, hex encoded for inclusion with Google Cloud Platform API requests
*
* #param packageName Identifies the APK whose signature should be extracted.
* #return a lowercase, hex-encoded
*/
public static String getSignature(#NonNull PackageManager pm, #NonNull String packageName) {
try {
PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
if (packageInfo == null
|| packageInfo.signatures == null
|| packageInfo.signatures.length == 0
|| packageInfo.signatures[0] == null) {
return null;
}
return signatureDigest(packageInfo.signatures[0]);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
private static String signatureDigest(Signature sig) {
byte[] signature = sig.toByteArray();
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] digest = md.digest(signature);
return BaseEncoding.base16().lowerCase().encode(digest);
} catch (NoSuchAlgorithmException e) {
return null;
}
}
Then, add package name and SHA certificate signature to request header:
java.net.URL url = new URL(REQUEST_URL);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
try {
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
connection.setRequestProperty("Accept", "application/json");
// add package name to request header
String packageName = mActivity.getPackageName();
connection.setRequestProperty("X-Android-Package", packageName);
// add SHA certificate to request header
String sig = getSignature(mActivity.getPackageManager(), packageName);
connection.setRequestProperty("X-Android-Cert", sig);
connection.setRequestMethod("POST");
// ADD YOUR REQUEST BODY HERE
// ....................
} catch (Exception e) {
e.printStackTrace();
} finally {
connection.disconnect();
}
Hope this help! :)

How to use Google public API access key in android application?

I want to use Google Translate API (v2) in my android application.
What I did:
created project in Google Developers Console
set up billing for this project
generated 2 public api access keys for android applications:
a. First one that accepts request from any application
b. Second one that accepts requests from my application only
I tried to translate text from the application via
https://www.googleapis.com/language/translate/v2?key=MY-KEY&target=de&q=Hello%20world
It works fine with the key from 3a) but does not work with the key from 3b). For 3b) server sends
{
"error": {
"errors": [
{
"domain": "usageLimits",
"reason": "ipRefererBlocked",
"message": "There is a per-IP or per-Referer restriction configured on your API key and the request does not match these restrictions. Please use the Google Developers Console to update your API key configuration if request from this IP or referer should be allowed.",
"extendedHelp": "https://console.developers.google.com"
}
],
"code": 403,
"message": "There is a per-IP or per-Referer restriction configured on your API key and the request does not match these restrictions. Please use the Google Developers Console to update your API key configuration if request from this IP or referer should be allowed."
}
}
I guess this is because google server does not receive any info about my application with this request, so it cannot acquire key 3b). If so, how to send this request correctly? Or, alternatively, what I did wrong somewhere else?
If so, how to send this request correctly?
When setting up your API key restriction for android app, you specified the package name and SHA-1 certificate fingerprint. So when you send an request to Google, you MUST add these information in the header of each request.
HOW?
As answered here, you need to get your package name and SHA certificate from your code, and then adding to request header.
Get SHA certificate:
/**
* Gets the SHA1 signature, hex encoded for inclusion with Google Cloud Platform API requests
*
* #param packageName Identifies the APK whose signature should be extracted.
* #return a lowercase, hex-encoded
*/
public static String getSignature(#NonNull PackageManager pm, #NonNull String packageName) {
try {
PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
if (packageInfo == null
|| packageInfo.signatures == null
|| packageInfo.signatures.length == 0
|| packageInfo.signatures[0] == null) {
return null;
}
return signatureDigest(packageInfo.signatures[0]);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
private static String signatureDigest(Signature sig) {
byte[] signature = sig.toByteArray();
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] digest = md.digest(signature);
return BaseEncoding.base16().lowerCase().encode(digest);
} catch (NoSuchAlgorithmException e) {
return null;
}
}
Adding to request header:
java.net.URL url = new URL(REQUEST_URL);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
try {
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
connection.setRequestProperty("Accept", "application/json");
// add package name to request header
String packageName = mActivity.getPackageName();
connection.setRequestProperty("X-Android-Package", packageName);
// add SHA certificate to request header
String sig = getSignature(mActivity.getPackageManager(), packageName);
connection.setRequestProperty("X-Android-Cert", sig);
connection.setRequestMethod("POST");
// ADD YOUR REQUEST BODY HERE
// ....................
} catch (Exception e) {
e.printStackTrace();
} finally {
connection.disconnect();
}
You can see full answer here.
Enjoy coding :)

REQUEST_DENIED using Google Places API

I am developing an application for Android using the text search Google Places API. The goal is to give an address and get the latitude and longitude back so I can mark it on a map.
To do so I send the following request to Google Places :
https://maps.googleapis.com/maps/api/place/textsearch/json?query=46+Rue+Emile+Raspail+Arcueil&sensor=true&key=MY_API_KEY
using this code :
public class GeoLocRDV {
private LatLng pos;
private static final String API_KEY = " xxxxx_MY_KEY ";
private static final String PLACES_TEXT_SEARCH_URL = "https://maps.googleapis.com/maps/api/place/textsearch/json?";
public GeoLocRDV(String rdvPlace){
String url;
try {
url = PLACES_TEXT_SEARCH_URL+"query=" + URLEncoder.encode(rdvPlace,"UTF-8") + "&sensor=true&key=" + API_KEY;
Log.e("DEBUG HTTP", url);
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response;
response = httpclient.execute(new HttpGet(url));
StatusLine statusLine = response.getStatusLine();
if(statusLine.getStatusCode() == HttpStatus.SC_OK){
ByteArrayOutputStream out = new ByteArrayOutputStream();
response.getEntity().writeTo(out);
out.close();
String responseString = out.toString();
Log.e("DEBUG RESP", responseString);
JSONObject jsonObj = new JSONObject(responseString);
JSONArray results = (JSONArray) jsonObj.get("results");
Log.e("DEBUG JSON", results.toString());
double rdvLat = (Double) results.getJSONObject(0).getJSONObject("geometry").getJSONObject("location").get("lat");
Log.e("DEBUG JSON lat", Float.toString((float) rdvLat));
double rdvLng = (Double) results.getJSONObject(0).getJSONObject("geometry").getJSONObject("location").get("lng");
Log.e("DEBUG JSON lng", Float.toString((float) rdvLng));
this.pos = new LatLng(rdvLat, rdvLng);
Log.e("DEBUG GEO", pos.toString());
}else{
response.getEntity().getContent().close();
}
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
Unfortunately I get this response :
{
"html_attributions" : [],
"results" : [],
"status" : "REQUEST_DENIED"
}
I tried to :
Switch the "sensor" parameter to "true" and "false"
Change https to http
Verify my key was correctly copied/pasted and corresponding to the one in my manifest
I also went to the api console, then to SERVICES, clicked Active services tab and verified 'Places API' is turned ON. Clicked on the ? and "try it out!" link next to it. It also returned me the same JSON (REQUEST_DENIED)
I read on StackOverflow I could try to change the port address to 443 to get response from Places API, but I don't know how to do it.
Finally, I specify that I activated Google Places service after getting my API KEY (because I am also using Maps API) and it is an Android key (not a server or browser), but it is not supposed to be an issue since a key works for a whole application.
I am running out of ideas to fix this request problem, so I hope someone can help me.
Thanks in advance.

How does RequestFactory know what Android user is logged in

I have an Android app that successfully uses RequestFactory to manipulate Entities in AppEngine Datastore.
In my AppEngine service, I want to use
UserService userService = UserServiceFactory.getUserService();
User user = userService.getCurrentUser();
to get information about the current Android user, but getCurrentUser() returns null.
How does my Android app let RequestFactory know who is logged in?
This question applies to dev mode and production mode.
Thanks.
There is a not-so-obvious answer found in the old AppEngine Connected Android Eclipse wizard code, which is no longer available in the GPE (see Util.java of this wizard generated code). It includes the following:
T requestFactory = RequestFactorySource.create(factoryClass);
requestFactory.initialize(new SimpleEventBus(),
new AndroidRequestTransport(uri, authCookie));
with
public AndroidRequestTransport(URI uri, String cookie) {
this.uri = uri;
this.cookie = cookie;
}
public void send(String payload, TransportReceiver receiver) {
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost();
post.setHeader("Content-Type", "application/json;charset=UTF-8");
post.setHeader("Cookie", cookie);
post.setURI(uri);
Throwable ex;
try {
post.setEntity(new StringEntity(payload, "UTF-8"));
HttpResponse response = client.execute(post);
if (200 == response.getStatusLine().getStatusCode()) {
String contents = readStreamAsString(response.getEntity().getContent());
receiver.onTransportSuccess(contents);
} else {
receiver.onTransportFailure(new ServerFailure(response.getStatusLine()
.getReasonPhrase()));
}
return;
} catch (UnsupportedEncodingException e) {
ex = e;
} catch (ClientProtocolException e) {
ex = e;
} catch (IOException e) {
ex = e;
}
receiver.onTransportFailure(new ServerFailure(ex.getMessage()));
}
Using that code from the GPE wizard did the trick for me.

Android SSL Certificate Authentication (with the Android KeyChain API) to a WCF Service

I have big problems with getting the certificates running under android.
I have an android client which connects to a WCF-Service.
I think the problem is, that the certificates are not transfered. I get an error message:
403 forbidden (in the response). I really hope you can help me.
=> in my android client
In internet explorer, it works just fine => status 200
I found this article:
http://android-developers.blogspot.de/2012/03/unifying-key-store-access-in-ics.html
"A common use of the private key is for SSL client authentication. This can be implemented by using an HttpsURLConnection with a custom X509KeyManager that returns the PrivateKey retrieved from the KeyChain API. The open source Email application for ICS uses KeyChain with an X509ExtendedKeyManager. To learn more, have a look at the source code (in SSLUtils.java)."
I have checked out the SSLUtils class and I am trying to use it.
Here is some code:
private void setHttpsAdvanced() {
HostAuth ht = new HostAuth();
ht.mPort = 443;
ht.mClientCertAlias = "jensZert";
HttpParams params = getHttpParams();
MyThreadSafeClientConnManager ccm = MyThreadSafeClientConnManager
.newInstance(params, true, 443);
try {
MyThreadSafeClientConnManager.makeScheme(true, false,
ht.mClientCertAlias);
ccm.registerClientCert(getApplicationContext(), ht);
// checkCertificate(ht.mClientCertAlias);
} catch (CertificateException e) {
Log.d(TAG, e.getMessage());
e.printStackTrace();
}
this.httpclient = new DefaultHttpClient(ccm, params);
connectionInfo = this.getConnectionInfo();
this.url = String.format("%1$s://%2$s/%3$s/%4$s",
connectionInfo.Protocol, connectionInfo.ServerName,
connectionInfo.WebserviceName, connectionInfo.Path);
httpGet = new HttpGet(url);
}
private String callTheWebserviceCertificate() {
this.setupClient();
String result = "";
HttpResponse response = null;
try {
response = (HttpResponse) this.httpclient.execute(httpGet);
result = EntityUtils.toString(response.getEntity());
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
result = e.getMessage() + "\n";
for (StackTraceElement el : e.getStackTrace()) {
result += el.toString() + "\n";
}
Log.d(TAG, result);
}
return result;
}
greetings,
jens

Categories

Resources