Google Maps within an app with restricted key doesn't work - android

I have an android app that renders a MapView and polylines, which works fine on the Android emulator if I use an IP restricted API key, but doesn't even display a map, and definitely no polylines, when I use an android restricted key with the expo built application running on my phone.
I have the following in my app.json:
{
"expo": {
"android": {
"package": "com.company.project",
"permissions": [ "CAMERA" ],
"config": {
"googleMaps": {
"apiKey": "ACTUAL_API_KEY_HERE"
}
}
},
...
The directions portion of the code is as follows:
const key = Constants.manifest.android.config.googleMaps.apiKey;
const resp = await fetch(`https://maps.googleapis.com/maps/api/directions/json?origin=${ startLoc }&destination=${ destinationLoc }&key=${ key }`);
const respJson = await resp.json();
I am building my app with the following command:
expo build:android
I then download the apk from expo.io.
The key is restricted to android apps in the GCP console. I have verified that the package name in my app.json matches the name in the restriction and I get the SHA-1 fingerprint from the "expo fetch:android:hashes" command.
As I said, this works fine in the emulator (with an IP restricted key) but not when running on my phone. Am I missing something?

Check you use meta data in Manifest file like that..
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="#string/google_maps_key" />

Related

How to add values to app.json for android in expo managed workflow?

I need to add those to Android files:
android:usesCleartextTraffic="true" and <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
But I'm using managed workflow and I don't know how to add those lines to app.json file.
I did this plugin which seems to work:
const { createRunOncePlugin, withAndroidManifest } = require('#expo/config-plugins');
const withAndroidManifestHavingBetterSecuritySettings = config => {
return withAndroidManifest(config, config => {
const androidManifest = config.modResults.manifest;
const mainApplication = androidManifest.application[0];
if(process.env.CHANNEL !== 'dev') {
androidManifest.$ = {
...androidManifest.$,
'xmlns:tools': 'http://schemas.android.com/tools',
};
mainApplication.$['tools:replace'] = 'android:usesCleartextTraffic';
mainApplication.$['android:usesCleartextTraffic'] = 'false';
}
return config;
});
};
module.exports = createRunOncePlugin(
withAndroidManifestHavingBetterSecuritySettings,
'withAndroidManifestHavingBetterSecuritySettings',
'1.0.0'
);
I had many issues related to merging of AndroidManifest files when "developmentClient": true in my eas.json file (related to me dev eas profile). I believe that it's related to the fact that the debug/AndroidManifest is a higher priority manifest than main/AndroidManifest (not sure though). So my solution was not to ignore the changes when building the dev profile. Hardening security settings in development builds do not seem useful anyhow.
So I struggled with this problem for a while now and the only solution I could come up with was setting the minimum sdk version of the android app from 21 to 28. This is not ideal as my application now does not support old android devices, but doing this defaults the usesClearTextTraffic flag to false.
If your app works fine while developing in expo, but after generating the APK some functions don't work, try this. In my case the APK crashed on login, but building in development with expo was working fine. The problem was that traffic is encrypted so that's why I ended up here trying to set clear text traffic. The problem in my case was with expoPushToken, in the APK it throws an exception I wasn't catching (building with expo worked fine as I said before, no exception). So, if the exception happens just catch it and set the token to empty string.
So, I had this:
import * as Notifications from "expo-notifications";
export async function getDevicePushTokenForAPP() {
const pushToken = await Notifications.getExpoPushTokenAsync();
return pushToken.data;
}
So then, I added the try and catch:
export async function getDevicePushTokenForAPP() {
try {
const pushToken = await Notifications.getExpoPushTokenAsync();
return pushToken.data;
} catch (e) {
return "";
}
}
Now if you build the APK again (expo build:android) it should work fine, in my case login worked. But please note this is for testing purposes only, I needed the APK to quickly show it to the client. (Note that you will need the bundle, not the apk, when uploading to the Playstore). This is a quick fix for you to test the APK; but with no token, push notifications won't work. The final fix is to add firebase to your project, it's mandatory now, so add firebase and with the firebase unique ID, your push notification will work in your APK.
My conclusion is that expo uses its own ID to communicate with firebase, that's why it works while developing but the APK doesn't go through expo and tries to connect to firebase directly, but crashes because there's no ID.
You should update your app.json like that:
"android": {
"usesCleartextTraffic": true,
uses-permission android:name
},

How to force Google to refetch .well-known/assetlinks.json in order to fix my Android App Link implementation

I have successfully implemented Android App links with My Local APK and it works. When I published the same APK in Google Play Store I figured out that I need to change SHA 256 fingerprint in my /.well-known/assetlinks.json with the fingerprint provided by Google. So I changed it. Unfortunately it seems that Google keeps the old copy of assetlinks.json which brakes my app links.
I have checked
https://developers.google.com/digital-asset-links/tools/generator and it said
"No app deep linking permission found for [your-app]"
I have checked
https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=[my-site]&relation=delegate_permission/common.handle_all_urls and it shows me the old version of assetlinks.json
My robots.txt
User-agent: *
Disallow:
My .htaccess file in directory /.well-known
Require all granted
RewriteEngine Off
<FilesMatch "\.(txt)$">
Require all granted
</FilesMatch>
<FilesMatch "\.(txt)$">
Allow from all
</FilesMatch>
My assetlinks.json
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target" : { "namespace": "android_app", "package_name": "my-app-id",
"sha256_cert_fingerprints": ["my-sha-256-provided-by-gogole-play-console"] }
}]
The result of https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=[my-site]&relation=delegate_permission/common.handle_all_urls
{
"statements": [
{
"source": {
"web": {
"site": "https://my-host."
}
},
"relation": "delegate_permission/common.handle_all_urls",
"target": {
"androidApp": {
"packageName": "my-app-id",
"certificate": {
"sha256Fingerprint": "the-old-sha-256-fingerprint"
}
}
}
}
],
"maxAge": "534347.929731888s",
"debugString": "********************* ERRORS *********************\nNone!\n********************* INFO MESSAGES *********************\n* Info: The following statements were considered when processing the request:\n\n---\nSource: Web asset with site https://my-host. (which is equivalent to 'https://my-host')\nRelation: delegate_permission/common.handle_all_urls\nTarget: Android app asset with package name my-app-id and certificate fingerprint the-old-sha-256 \nWhere this statement came from:\n Origin of the statement: Web asset with site https://my-host. (which is equivalent to 'https://my-host')\n Include directives followed (in order):\n \u003cNone\u003e\nMatches source query: Yes\nMatches relation query: Yes\nMatches target query: Yes\n\n--- End of statement list. ---\n\n\n"
}
The the-old-sha-256 is different from the SHA 256 in my actual assetlinks.json
P.S. my-app-id, my-host, [my-site] and so on are placeholders.
So... How to force Google to read my current assetlinks.json instead of using old cached version?
In your case, google cached your SHA256 and will take max limit
of eight days to update your SHA256. Just change the name of website
in the url given below, that will show you the SHA256 key google has
been cached.
https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://www.your-website-name.com&relation=delegate_permission/common.handle_all_urls
The assetlinks.json file may also be cached by Play Services on your
device and also on Google’s servers, so it may take a few days for
them to be updated from any changes you make to your web servers. And
SmartLock chrome/app login sharing needs your APK to be downloaded
from Google’s app store.

Unable to install apk after device provisioning (Android 8.0)

We are developing a COSU app using Android Management API and QR code provisioning. We're planning to distribute app with private Google play, but for now I'm trying to make it work with manual installation using apk. That's how it should work:
A client receives qr code from us, that applies policy without restrictions to the device.
After provisioning client downloads apk from some other source (probably via email) and installs it.
Client applies policy with restrictions from app with call to API.
So, I'm stuck on second step - an attempt to install downloaded apk leads to this error:
Default policy seems to be ok:
{
"name": "enterprises/<enterpriseName>/policies/policy_unlocked",
"version": "11",
"applications": [
{
"packageName": "com.axmor.fsinphone",
"installType": "AVAILABLE",
"defaultPermissionPolicy": "GRANT"
}
],
"persistentPreferredActivities": [
{
"receiverActivity": "com.axmor.fsinphone",
"actions": [
"android.intent.action.MAIN"
]
}
],
"systemUpdate": {
"type": "WINDOWED",
"startMinutes": 120,
"endMinutes": 240
},
"debuggingFeaturesAllowed": true
}
]
}
And this policy is applied to device:
"policyName": "enterprises/<enterpriseName>/policies/policy_unlocked",
"appliedPolicyName": "enterprises/<enterpriseName>/policies/policy_unlocked",
What I've tried:
Installing release apk and signed apk (same result)
Installing apk with adb from Android studio - this works, but it's not what I want.
(copying the solution here from comments)
To be able to install apps from outside the Play Store you need to set installUnknownSourcesAllowed to true in the Policy. More details in the policy references.

Should the GCM API Key kept secret?

As per the documentation:
Do not include the API key anywhere in your client code.
And it is the case in our current Android app -- the API Key is nowhere included in the code. However, for the new version 3.0.0 of com.google.gms:google-services library, it started throwing error Missing api_key/current_key without it, as discussed here: Missing api_key/current key with Google Services 3.0.0.
Also, Google's config generator https://developers.google.com/mobile/add?platform=android&cntapi=gcm includes the API Key in the google-services.json file.
Is it supposed to be kept secret? Or is it safe to include it in the client app?
The google-services.json file represents the configuration for all of the services available within Firebase. There are some services that require and "Android" API key. These are the API keys that you will find in the google-services.json file. Your app may or may not use these API keys depending on the Firebase APIs your app is using.
FCM has a "Server" API key that is used to send messages, this API key is NOT the key included in the google-services.json file. The server API key should never be included in your application. The google services plugin however does look for those Android API keys at build time and that could be the reason for your error, it is not because your FCM server API key is missing.
Answering my own question.
If I create a new test project on Firebase (https://console.firebase.google.com), it also includes the API Key into
google-services.json for Android app,
GoogleService-Info.plist for iOS app,
for Web App it even recommends to include the API key into my HTML.
Taken that HTML is definitely public, I'm pretty convinced now that it's not a secret.
If you are using GCM, your Android app need not know about the API key. I just had to include an empty field for api_key in the json file for GCM to work. As mentioned in tha answer here Missing api_key/current key with Google Services 3.0.0 and Maps API key in build.gradle, I just had to add a line like below in google-services.json for GCM to work:
"api_key": [
{
"current_key": ""
}
],
I think you shouldn't include the API key, since I think only your server needs API key to authenticate with Google for requesting it to send push messages to intended recepients. It would be a risk if anyone gets hold of it. So file can look like:
{
"project_info": {
...
},
"client": [
{
"client_info": {
...
},
"oauth_client": [
{
"client_id": "yourid.whatever.com",
...
}
],
"api_key": [
{
"current_key": ""
}
],
"services": {
...
}
}
],
"configuration_version": "1"
}
Hope this helps.

Where do I find the namespace of an app [Android]

I need to find the namespace of the app, so I can fill in the value for the assetlinks.json file.
https://www.domain1.com/.well-known/assetlinks.json
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.mycompany.app1",
"sha256_cert_fingerprints":
["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
}
}]
Tim, I was looking for this too because the Google docs never explained the namespace field in the json. As it turns out, you leave the value as android_app, this tells consumers these settings are specifically for an android_app. It is not to specify the specific namespace of your app or project (like I also assumed)
See https://firebase.google.com/docs/app-indexing/android/app
Target: Using the namespace value of android_app determines the android app that receives the URLs. Configure it using its package name and the SHA-256 fingerprint of the certificate you used to sign your app before deploying it to the Play Store. See Signing Your Applications to learn more about deployment and your signing certificate.
Thanks for the #johnw182 answer, just want to add, that wrong name leads to unpredictable errors and I spent a lot of time to find them.
But it is possible to validate the assetlinks.json file
Just change YOUR_WEB_SITE_ADDRESS to web-site with assetlinks.json file.
https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://YOUR_WEB_SITE_ADDRESS&relation=delegate_permission/common.handle_all_urls

Categories

Resources