How can I implement SSL Certificate Pinning while using React Native - android

I need to implement SSL Certificate Pinning in my react native application.
I know very little about SSL/TLS let alone pinning.
I am also not a native mobile developer, though I know Java and learned Objective-C on this project enough to get around.
I started searching for how to execute this task.
Doesn't React Native already implement this?
No, My initial search lead me to this proposal which has received no activity since August 2nd 2016.
From it I learned that react-native uses OkHttp which does support Pinning, but I wouldn't be able to pull it off from Javascript, which is not really a requirement but a plus.
Implement it in Javascript.
While react seems like it uses the nodejs runtime, it is more like a browser than node, meaning it does not support all native modules, specifically the https module, for which I had implemented certificate pinning following this article. Thus could not carry it into react native.
I tried using rn-nodeify but the modules didn't work. This has been true since RN 0.33 to RN 0.35 which I'm currently on.
Implement using phonegap plugin
I thought of using a phongape-plugin however since I have a dependency on libraries that require react 0.32+ I can't use react-native-cordova-plugin
Just do it natively
While I'm not a native app developer I can always take a crack at it, only a matter of time.
Android has certificate pinning
I learned that android supports SSL Pinning however was unsuccessful as it seems that this approach does not work Prior to Android 7. As well as only working for android.
The bottom line
I have exhausted several directions and will continue to pursue more native implementation, maybe figure out how to configure OkHttp and RNNetworking then maybe bridging back to react-native.
But is there already any implementations or guide for IOS and android?

After exhausting the current spectrum of available options from Javascript I decided to simply implement certificate pinning natively it all seems so simple now that I'm done.
Skip to headers titled Android Solution and IOS Solution if you don't want to read through the process of reaching the solution.
Android
Following Kudo's recommendation I thought out to implement pinning using okhttp3.
client = new OkHttpClient.Builder()
.certificatePinner(new CertificatePinner.Builder()
.add("publicobject.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
.add("publicobject.com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
.add("publicobject.com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
.add("publicobject.com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
.build())
.build();
I first started by learning how to create a native android bridge with react nativecreating a toast module. I then extended it with a method for sending a simple request
#ReactMethod
public void showURL(String url, int duration) {
try {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
Toast.makeText(getReactApplicationContext(), response.body().string(), duration).show();
} catch (IOException e) {
Toast.makeText(getReactApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
Succeeding in sending a request I then turned to sending a request pinned.
I used these packages in my file
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.CertificatePinner;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
Kudo's approach wasn't clear on where I would get the public keys or how to generate them. luckily okhttp3 docs in addition to providing a clear demonstration of how to use the CertificatePinner stated that to get the public keys all I would need to do is send a request with an incorrect pin, and the correct pins will appear in the error message.
After taking a moment to realise that OkHttpClent.Builder() can be chained and I can include the CertificatePinner before the build, unlike the misleading example in Kudo's proposal (probably and older version) I came up with this method.
#ReactMethod
public void getKeyChainForHost(String hostname, Callback errorCallbackContainingCorrectKeys,
Callback successCallback) {
try {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
OkHttpClient client = (new OkHttpClient.Builder()).certificatePinner(certificatePinner).build();
Request request = new Request.Builder()
.url("https://" + hostname)
.build();
Response response =client.newCall(request).execute();
successCallback.invoke(response.body().string());
} catch (Exception e) {
errorCallbackContainingCorrectKeys.invoke(e.getMessage());
}
}
Then replacing the public keychains I got in the error yielded back the page's body, indicating I had made a successful request, I change one letter of the key to make sure it was working and I knew I was on track.
I finally had this method in my ToastModule.java file
#ReactMethod
public void getKeyChainForHost(String hostname, Callback errorCallbackContainingCorrectKeys,
Callback successCallback) {
try {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/+Jg+cke8HLJNzDJB4qc1Aus14rNb6o+N3IrsZgZKXNQ=")
.add(hostname, "sha256/aR6DUqN8qK4HQGhBpcDLVnkRAvOHH1behpQUU1Xl7fE=")
.add(hostname, "sha256/HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=")
.build();
OkHttpClient client = (new OkHttpClient.Builder()).certificatePinner(certificatePinner).build();
Request request = new Request.Builder()
.url("https://" + hostname)
.build();
Response response =client.newCall(request).execute();
successCallback.invoke(response.body().string());
} catch (Exception e) {
errorCallbackContainingCorrectKeys.invoke(e.getMessage());
}
}
Android Solution Extending React Native's OkHttpClient
Having figured out how to send pinned http request was good, now I can use the method I created, but ideally I thought it would be best to extend the existing client, so as to immediately gain the benefit of implementing.
This solution is valid as of RN0.35 and I don't know how it will fair in the future.
While looking into ways of extending the OkHttpClient for RN I came across this article explaining how to add TLS 1.2 support through replacing the SSLSocketFactory.
reading it I learned react uses an OkHttpClientProvider for creating the OkHttpClient instance used by the XMLHttpRequest Object and therefore if we replace that instance we would apply pinning to all the app.
I added a file called OkHttpCertPin.java to my android/app/src/main/java/com/dreidev folder
package com.dreidev;
import android.util.Log;
import com.facebook.react.modules.network.OkHttpClientProvider;
import com.facebook.react.modules.network.ReactCookieJarContainer;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.CertificatePinner;
public class OkHttpCertPin {
private static String hostname = "*.efghermes.com";
private static final String TAG = "OkHttpCertPin";
public static OkHttpClient extend(OkHttpClient currentClient){
try {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/+Jg+cke8HLJNzDJB4qc1Aus14rNb6o+N3IrsZgZKXNQ=")
.add(hostname, "sha256/aR6DUqN8qK4HQGhBpcDLVnkRAvOHH1behpQUU1Xl7fE=")
.add(hostname, "sha256/HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=")
.build();
Log.d(TAG, "extending client");
return currentClient.newBuilder().certificatePinner(certificatePinner).build();
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
return currentClient;
}
}
This package has a method extend which takes an existing OkHttpClient and rebuilds it adding the certificatePinner and returns the newly built instance.
I then modified my MainActivity.java file following this answer's advice by adding the following methods
.
.
.
import com.facebook.react.ReactActivity;
import android.os.Bundle;
import com.dreidev.OkHttpCertPin;
import com.facebook.react.modules.network.OkHttpClientProvider;
import okhttp3.OkHttpClient;
public class MainActivity extends ReactActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rebuildOkHtttp();
}
private void rebuildOkHtttp() {
OkHttpClient currentClient = OkHttpClientProvider.getOkHttpClient();
OkHttpClient replacementClient = OkHttpCertPin.extend(currentClient);
OkHttpClientProvider.replaceOkHttpClient(replacementClient);
}
.
.
.
This solution was carried out in favor of completely reimplementing the OkHttpClientProvider createClient method, as inspecting the provider I realized that the master version had implemented TLS 1.2 support but was not yet an available option for me to use, and so rebuilding was found to be the best means of extending the client. I'm wondering how this approach will fair as I upgrade but for now it works well.
Update It seems that starting 0.43 this trick no longer works. For timebound reasons I will freeze my project at 0.42 for now, until the reason for why rebuilding stopped working is clear.
Solution IOS
For IOS I had thought I would need to follow a similar method, again starting with Kudo's proposal as my lead.
Inspecting the RCTNetwork module I learned that NSURLConnection was used, so instead of trying to create a completely new module with AFNetworking as suggested in the proposal I discovered TrustKit
following its Getting Started Guide I simply added
pod 'TrustKit'
to my podfile and ran pod install
the GettingStartedGuide explained how I can configure this pod from my pList.file but preferring to use code than configuration files I added the following lines to my AppDelegate.m file
.
.
.
#import <TrustKit/TrustKit.h>
.
.
.
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Initialize TrustKit
NSDictionary *trustKitConfig =
#{
// Auto-swizzle NSURLSession delegates to add pinning validation
kTSKSwizzleNetworkDelegates: #YES,
kTSKPinnedDomains: #{
// Pin invalid SPKI hashes to *.yahoo.com to demonstrate pinning failures
#"efghermes.com" : #{
kTSKEnforcePinning:#YES,
kTSKIncludeSubdomains:#YES,
kTSKPublicKeyAlgorithms : #[kTSKAlgorithmRsa2048],
// Wrong SPKI hashes to demonstrate pinning failure
kTSKPublicKeyHashes : #[
#"+Jg+cke8HLJNzDJB4qc1Aus14rNb6o+N3IrsZgZKXNQ=",
#"aR6DUqN8qK4HQGhBpcDLVnkRAvOHH1behpQUU1Xl7fE=",
#"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY="
],
// Send reports for pinning failures
// Email info#datatheorem.com if you need a free dashboard to see your App's reports
kTSKReportUris: #[#"https://overmind.datatheorem.com/trustkit/report"]
},
}
};
[TrustKit initializeWithConfiguration:trustKitConfig];
.
.
.
I got the public key hashes from my android implementation and it just worked (the version of TrustKit I received in my pods is 1.3.2)
I was glad IOS turned out to be a breath
As a side note TrustKit warned that it's Auto-swizzle won't work if the NSURLSession and Connection are already swizzled. that said it seems to be working well so far.
Conclusion
This answer presents the solution for both Android and IOS, given I was able to implement this in native code.
One possible improvement may be to implement a common platform module where setting public keys and configuring the Network providers of both android and IOS can be managed in javascript.
Kudo's proposal mentioned simply adding the public keys to the js bundle may however expose a vulnerability, where somehow the bundle file can be replaced.
I don't know how that attack vector can function, but certainly the extra step of signing the bundle.js as proposed may protect the js bundle.
Another approach may be to simply encode the js bundle into a 64 bit string and include it in the native code directly as mentioned in this issue's conversation. This approach has the benefit of obfuscating as well hardwiring the js bundle into the app, making it inaccessible for attackers or so I think.
If you read this far I hope I enlightened you on your quest for fixing your bug and wish you enjoy a sunny day.

You can use this lib https://github.com/nlt2390/react-native-pinning-ssl
It verifies SSL connection using SHA1 keys, not certificates.

Related

Android https post with hard coded URL and post data in Json format

Apollogies for a question that I can see has alot of hits however I can't find any recent answers. Given how fast android dev moves I thought I would ask a new question.
New to kotlin and android dev, one off app.
Using Android Studio & Kotlin - How to make an app that executes a HTTPS POST with hard coded URL and POST data in JSON format once the app is opened. No user action required other than opening the app. The app needs no UI.
Found what I was looking for in ktor: https://ktor.io/docs/request.html#body
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
val response: HttpResponse = client.post("http://localhost:8080/post") {
setBody("Body content")
}
val response: HttpResponse = client.post("http://localhost:8080/customer") {
contentType(ContentType.Application.Json)
setBody(Customer(3, "Jet", "Brains"))
}

Spring Boot/Security - can I use X509 Certificate as an extra layer in authentication?

I am building an Android App which communicates with my REST API that is protected by Spring Security.
Since the Android App is "public" and no keys etc is secure I want to create diffrent obstacles and make things complicated to protect my API as much as possible.
One way in which I would like to add more security is to make sure that the one calling my API has a certificate. I don't want to create thousands of certificates in my APIs trust-store so I just want to make sure that the caller have one single certificate that I hid away in a keystore in my Android app.
In the examples I have found it seems like a "normal" X509Certificate authentication in Spring Security requires a unique certificate for every user and then this certificate replaces Basic auth or JWT auth. I would like to have individual client JWT tokens but make sure that every call brings my ONE Android App certificate to make (more) sure that someone is calling my API from my Android app.
Is this possible or is it just not what it is for?
When you create a RestTemplate you can configure it with a keystore and trust-store so in that end it should be easy. But as for protecting my REST API it seems more difficult since I want both certificate + JWT token or Basic auth.
I am not using XML configuration for my securityconfig. I instead extend WebSecurityConfigurerAdapter. It would be great if this was configurable in the configure(HttpSecurity http) method, but I'm thinking that maybe I could achieve this in a OncePerRequestFilter somehow? Perhaps configure a filter before my JwtAuthFilter?
Edit:
In all the examples I have found for configuration of spring security they always seems to use the certificate as an authentication. I just want to configure so that when someone call example.com/api/** it checks so that the certificate is approved by my custom trust store (so that I "know" it is probably a call from my app) but if someone call example.com/website it should use the default java trust store.
If someone call example.com/api/** I would like my server to
check certificate and kill the connection if the certificate is not approved in my custom truststore.
If certificate is ok, establish https (or move on if I can't kill the connection before it have already established https-connection) to user auth with Basic-/JWT-authentication.
I think I figured it out. Here is how I configured it and it seems to work.
The "/**" endpoint is the website which should work with any browser without any specific certificate, but it requires Admin authority (you need to login as admin).
The "/api/**" and "/connect/**" endpoints require the correct certificate, the correct API-key and valid Basic- or JWT-token authentification.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/loginForm")
.loginProcessingUrl("/authenticateTheUser")
.permitAll()
.and()
.logout()
.permitAll().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
http.requestMatchers()
.antMatchers("/connect/**","/api/**")
.and()
.addFilterBefore(new APIKeyFilter(null), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JwtAuthorizationFilter(), BasicAuthenticationFilter.class)
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers("/connect/**").hasAnyRole("MASTER,APPCALLER,NEGOTIATOR,MEMBER")
.antMatchers("/api/**").hasAnyRole("MASTER,MEMBER,ANONYMOUS");
}
The ApiKeyFilter class is the one that check the api-key and also make sure that the certificate used in the call is approved in my server trust-store. The api-key check is all that I had to configure, the extended X509AuthenticationFilter will automatically check the request certificate. My ApiKeyFilter looks like this:
public class APIKeyFilter extends X509AuthenticationFilter {
private String principalRequestHeader = "x-api-key";
private String apiKey = "XXXX";
public APIKeyFilter(String principalRequestHeader) {
if (principalRequestHeader != null) {
this.principalRequestHeader = principalRequestHeader;
}
setAuthenticationManager(new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if(authentication.getPrincipal() == null) {
throw new BadCredentialsException("Access Denied.");
}
String rApiKey = (String) authentication.getPrincipal();
if (authentication.getPrincipal() != null && apiKey.equals(rApiKey)) {
return authentication;
} else {
throw new BadCredentialsException("Access Denied.");
}
}
});
}
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
return request.getHeader(principalRequestHeader);
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
X509Certificate[] certificates = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
if (certificates != null && certificates.length > 0) {
return certificates[0].getSubjectDN();
}
return super.getPreAuthenticatedCredentials(request);
}
}
Cred goes to these resources that helped me put things together:
Spring Boot - require api key AND x509, but not for all endpoints
spring security http antMatcher with multiple paths

spring security with angularJS web app and android app

I have angualrJS web app that recieves alerts from mobile app and marks the location of the mobile app user on google map. Once that mark is clicked info windo opens with name of user etc.
BackEnd is java maven project, and using spring boot.
My question is:
When I add spring boot security, to authenticate the page this stops all communication and no alerts show at all....
Any suggestions:
At the moment this is the application.js
var app = angular.module('sos',['ngRoute','mgcrea.ngStrap']);
app.config(['$httpProvider','$logProvider','$routeProvider',
function($httpProvider,$logProvider,$routeProvider) {
$logProvider.debugEnabled(true);
$routeProvider
.when('/', {
controller: 'MainController',
templateUrl: 'index.html',
controllerAs: 'controller'
})
.when('/download',{
templateUrl: 'download.html'
})
There is also a websocket connection that also stops and shows error 500 when I enable spring security...
This is web socket configuration:
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer{
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(geoLocationHandler(),
"/GeoLocationHandler").setAllowedOrigins("*");
}
#Bean
public GeoLocationHandler geoLocationHandler(){
return new GeoLocationHandler();
}
Any suggestions why spring security stops the alerts and markers showing on the map on the web app? And the spring security would make websocket fail giving error 500?
I tried to extend WebSecurityConfigAdapter and add mathcers to home page etc didnt work, I tried cors filters didnt work, I tried csrf filters didnt work as well...Any suggestion would be appreciated....
Can u try this in your
WebSecurityConfig.java file?
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().cors().disable().httpBasic().and().authorizeRequests()
.antMatchers(PUBLIC_MATCHERS).permitAll().anyRequest().authenticated();
(my snippet , try it and then remove what you don't like)
I think that eventually you don't let anyone to GET.
Here I give you also my Global method , in case you need it.
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userSecurityService).passwordEncoder(passwordEncoder());
}
Ok, after research I found that I needed to include http method get to be able to recive alerts on the the web app, so my websecurityConfiguration is like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
//.and()
.httpBasic()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/*").permitAll()
.antMatchers(HttpMethod.GET, "/api/geolocation/*").permitAll()
.antMatchers(HttpMethod.GET, "/GeoLocationHandler").permitAll()
.antMatchers(HttpMethod.GET, "/api/responser/*").permitAll()
.antMatchers(HttpMethod.GET, "/api/user/").authenticated()
.anyRequest().authenticated();
}
Otherwise the webapp will not accept any alerts....
Hope this helps someone needing it....

Endpoint.Builder missing for generated cloud endpoint

I'm attempting to follow the example tutorial at https://developers.google.com/eclipse/docs/endpoints-addentities and I'm stuck figuring out how to get the GameEndpoint.Builder class to generate within Eclipse.
After following this and generating the cloud endpoints as described, I have a GameEndpoint class created, but there is no GameEndpoint.Builder class. So obviously I have this error
GameEndpoint.Builder cannot be resolved to a type
I'm stumped at this point. How do I generate the GameEndpoint.Builder class within Eclipse, or what would prevent it?
Code
public class NewGameTask extends AsyncTask<Context, Integer, Long> {
protected Long doInBackground(Context... contexts) {
GameEndpoint.Builder endpointBuilder = new GameEndpoint.Builder(
AndroidHttp.newCompatibleTransport(), new JacksonFactory(),
new HttpRequestInitializer() {
public void initialize(HttpRequest httpRequest) {
}
});
GameEndpoint endpoint = CloudEndpointUtils.updateBuilder(
endpointBuilder).build();
try {
Game game = new Game();
game.setStart(Calendar.getInstance());
Game result = endpoint.insertGame(game);
} catch (IOException e) {
e.printStackTrace();
}
return (long) 0;
}
}
I figured out my issue after watching this video from Google I/O 2013 which is using Android Studio, but it was the same thing as Eclipse mostly.
My mistake in following https://developers.google.com/eclipse/docs/endpoints-addentities was that you need to put your entity class into the MyApp-AppEngine project and NOT your MyApp project.
That was the source of confusion. In case it helps those in the future, here is a short breakdown of what I did.
Put the Entity class you want to add to App Engine into your MyApp-AppEngine project.
Right click your class and go to Google > Generate Cloud Endpoint Client Library
Right click your MyApp-AppEngine project and go to Google > Generate Cloud Enpoint Client Library
New references will be made in your MyApp project which you reference in your project for usage.
Note This answer is based on Android Studio, but am sure it's pretty much the same as Eclipse.
I also had this issue but later found the cause.Turns out I was importing the Endpoint class I generated instead of the endpoint Api package. Let me be clear.When you add the endpoint module to your project, you get the MyBean and MyEndpoint classes in the endpoint package. If you take a look at the guide to connecting your client to your backend, the EndpointsAsyncTask class uses:
private static MyApi myApiService = null;
Note how it uses MyApi instead of MyBean Now I was wondering where it got that from but I just have to take a look at my backend libraries:
The library marked 1 is the library first added to your project when you follow the guide previously mentioned. When I added a new class Student and autogenerated the cloud endpoint class, the second library was also added.
Long, boring story short; It is this library you should be importing and not the class.
import com.package-name.backend.studentApi.StudentApi;
and then using:
private static StudentApi myApiService = null;
...
StudentApi.Builder builder = new StudentApi.Builder(...)
instead of:
import com.package-name.backend.StudentEndpoint;
...
private static StudentEndpoint myApiService = null;
StudentEndpoint.Builder builder = new StudentEndpoint.Builder(...)
I got the same problem in Android Studio. I generated my Endpoint class from my entity java bean but when creating the AsyncTask, now way to get the Builder.
Actually (if I take a Game java bean like you) the Builder is not depending on the GameEndPoint but on the generated GameApi class.
In other words, I had to add these two imports in the AsyncTask class:
import com.examplepackage.backend.gameApi.GameApi;
import com.examplepackage.backend.gameApi.model.Game;
while the Game java bean that you wrote and the generated GameEndpoint are under package com.examplepackage.backend

GoogleJsonResponseException: 404 Not Found using google apps endpoint engine backend

I followed the tutorial below.
https://developers.google.com/eclipse/docs/running_and_debugging_2_0
which basically added a GAE backend to my existing app. I then try out the example below, run it on the local development server and I get the exception below which occurs after
Note result = endpoint.insertNote(note).execute();
is called.
com.google.api.client.googleapis.json.GoogleJsonResponseException: 404 Not Found
My code is below.
package com.cloudnotes;
import java.io.IOException;
import java.util.Date;
import android.os.AsyncTask;
import android.content.Context;
import com.cloudnotes.noteendpoint.Noteendpoint;
import com.cloudnotes.noteendpoint.model.Note;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.json.jackson.JacksonFactory;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new EndpointsTask().execute(getApplicationContext());
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
public class EndpointsTask extends AsyncTask<Context, Integer, Long> {
protected Long doInBackground(Context... contexts) {
Noteendpoint.Builder endpointBuilder = new Noteendpoint.Builder(
AndroidHttp.newCompatibleTransport(),
new JacksonFactory(),
new HttpRequestInitializer() {
public void initialize(HttpRequest httpRequest) { }
});
Noteendpoint endpoint = CloudEndpointUtils.updateBuilder(
endpointBuilder).build();
try {
Note note = new Note().setDescription("Note Description");
String noteID = new Date().toString();
note.setId(noteID);
note.setEmailAddress("E-Mail Address");
Note result = endpoint.insertNote(note).execute();
} catch (IOException e) {
e.printStackTrace();
}
return (long) 0;
}
}
}
Another possible cause for this problem is not setting the correct applicationId in appengine-web.xml:
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>APPLICATION_ID_HERE</application>
...
</appengine-web-app>
Another possible reason for 404 if you call the method with a null parameter and the method does not accept null parameters.
I had a similar issue with a method with 4 String parameters and I sent null for one of them and all I got was a stupid 404.
Another possible reason for 404 on the development(local) server is that you have a parameter with some strange characters (like a url for example) that the development(local) server does not handle correctly event if it works fine on the app engine live servers. Possible solution: instead of multiple params use a simple java object.
I had the same problem and this is how I fixed it.
A little background
I had two versions of the API deployed to App Engine which I believe it should be OK. The admin console didn't show any problem and I was getting the 200 OK code after deploying version 2 of the API in log file.
/_ah/spi/BackendService.getApiConfigs 200 118ms 8kb
but no matter what I tried I was always getting the 404 Not Found error.
I am not sure what the problem was but I think Web Server at App Engine was redirecting all the requests to the version 1. It could be because both versions had the following settings in their web.xml.
<servlet-mapping>
<servlet-name>SystemServiceServlet</servlet-name>
<url-pattern>/_ah/spi/*</url-pattern>
</servlet-mapping>
Solution
I removed version 1 and redeployed version 2. Then everything just worked fine.
This comes down to the app engine default version being different from the one you've deployed. Go here:
https://appengine.google.com/deployment?app_id=s~your-project-id
and change your default version
this happen because your deploy back-end is not fully deployed. just re-deploy your back-end and make sure you got the message deploy succeed. you can check the detail deploy process on the top page header. also, you can test by visiting the url to check:
https://developers.google.com/apis-explorer/?base=https://[YOUR_PROJECT_ID].appspot.com/_ah/api#p/
Try to update your plugin and dependency libraries to the latest version. See https://stackoverflow.com/a/37639701/857346.
In build gradle do:
minifyEnabled false

Categories

Resources