0
votes

I want to restrict my API endpoints access only to my android app, but without google_account/password. I've the choice of those methods : https://developers.google.com/accounts/docs/OAuth2

For test, I succeeded to authenticate my android app to my API with this method: https://cloud.google.com/appengine/docs/python/endpoints/consume_android ==> This method allow you to authenticate your app with combo:

  • Login/password (Google account)
  • SHA1 and package name of your android APP

So if someone get my code (Decompiling apk) and modify my android code, they can't access to my API because SHA1 fingerprint of my app will change. (I tested it, and it works =) ) This method works fine, but I don't want Google login/password for authentication..

So I tried this method: https://developers.google.com/accounts/docs/OAuth2ServiceAccount I successfully authenticate my android app, BUT, if my android code is modified by someone else(So the SHA1 changed), my android app can still connect to my API !! So if someone get my package and decompile it, he'll changed freely code and successfully access to my API..

Here is my API Code:

@ApiMethod( name = "ListCampagnes", httpMethod = ApiMethod.HttpMethod.GET, path="list", clientIds = {CONSTANTES.ANDROID_CLIENT_ID, CONSTANTES.WEB_CLIENT_ID, CONSTANTES.SERVICE_CLIENT_ID, com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID}, audiences = {CONSTANTES.ANDROID_AUDIENCE})

public Collection<Campagne> getCampagnes(@Named("NumPortable")String NumPortable, User user) throws  UnauthorizedException {

    if (user == null) throw new UnauthorizedException("User is Not Valid");

        return CampagneCRUD.getInstance().findCampagne(NumPortable);
    }

Here is my android code:

GoogleCredential credentialToAppengine;
try {

    String p12Password = "notasecret";
    KeyStore keystore = KeyStore.getInstance("PKCS12");
    InputStream keyFileStream = getAssets().open("59ce5a08e110.p12");
    keystore.load(keyFileStream, p12Password.toCharArray());
    PrivateKey key = (PrivateKey)keystore.getKey("privatekey", p12Password.toCharArray());
    credentialToAppengine = new GoogleCredential.Builder().setTransport(AndroidHttp.newCompatibleTransport()).setJsonFactory(new JacksonFactory()).setServiceAccountId("301991144702-3v9ikfp4lsmokee1utkucj35847eddvg@developer.gserviceaccount.com").setServiceAccountPrivateKey(key).setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/userinfo.email")).build();
} catch (GeneralSecurityException e) {

    e.printStackTrace();
    return null;
} catch (Exception e) {

    e.printStackTrace();
    return null;
} 

Do I try an other method for authenticate my android App ? Or did I missing something in my API code ? Thanks a looot in advance,

1

1 Answers

0
votes

Authenticate Android End point without Google User Account is just impossible ! I tried every ways but still doesn't works !

So here is my way to resolv this problem, without any user interaction (Maybe not the right but that works, and you've got strong authentication (SHA1 + Google Account)):

HERE IS MY ANDROID CODE

Get and Build Valid Credential

  //Get all accounts from my Android Phone
         String validGoogleAccount = null;
         Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
         Account[] accounts = AccountManager.get(context).getAccounts();
         for (Account account : accounts) {
             if (emailPattern.matcher(account.name).matches()) {
                 //Just store mail if countain gmail.com
                 if (account.name.toString().contains("gmail.com")&&account.type.toString().contains("com.google")){
                     validGoogleAccount=account.name.toString();
                 }

             }
         }

        //Build Credential with valid google account
        GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(this,"server:client_id:301991144702-5qkqclsogd0b4fnkhrja7hppshrvp4kh.apps.googleusercontent.com");
        credential.setSelectedAccountName(validGoogleAccount);

Use this credential for secure calls

Campagneendpoint.Builder endpointBuilder = new Campagneendpoint.Builder(AndroidHttp.newCompatibleTransport(), new JacksonFactory(), credential);

HERE IS MY API BACKEND CODE: API Annotation

@Api(
        scopes=CONSTANTES.EMAIL_SCOPE,
        clientIds = {CONSTANTES.ANDROID_CLIENT_ID, 
                     CONSTANTES.WEB_CLIENT_ID,
                     com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID},
        audiences = {CONSTANTES.ANDROID_AUDIENCE},      
        name = "campagneendpoint",
        version = "v1"
     )

Method code:

public Collection<Campagne> getCampagnes(@Named("NumPortable")String NumPortable, User user) throws  UnauthorizedException {
        if (user == null) throw new UnauthorizedException("User is Not Valid");

      return CampagneCRUD.getInstance().findCampagne(NumPortable);
     }

For the moment, it only works on Android (I don't know how we gonna do on IOS..)..

Hope It will help you !