Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#1360): Document how to configure Android App Link verification #1366

Merged
merged 20 commits into from
May 9, 2024
Merged
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions content/en/apps/features/integrations/android.md
Original file line number Diff line number Diff line change
@@ -11,11 +11,12 @@ The [CHT Android application](https://github.com/medic/cht-android) can be launc

## Sending a URL

{{< figure src="android-prompt.png" link="android-prompt.png" class="right col-8 col-lg-4" >}}
{{< figure src="../../../guides/android/branding/android-12-prompt.png" link="../../../guides/android/branding/android-12-prompt.png" class="right col-6 col-md-4 col-lg-2" >}}
{{< figure src="android-prompt.png" link="android-prompt.png" class="right col-4 col-lg-3" >}}

When the user clicks on a link to a CHT instance from an SMS, email, WhatsApp, or any other app, Android will prompt the user to choose whether to open the URL in the Android app or the browser. If a CHT app is not installed then the URL will be opened in the browser.

Users can choose "Always" to skip this prompt in future. The prompt may look different depending on the version of Android being used.
The prompt may look different depending on the version of Android being used. On Android <12, users can choose "Always" to skip this prompt in the future. Starting with Android 12, users can associate the CHT instance's domain with the Android app, but this requires additional configuration in your CHT instance. See the docs on [Android App Links verification]({{< ref "apps/guides/android/branding#android-app-links-verification" >}}) for more information.

## Using an intent

@@ -27,9 +28,3 @@ Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
```

## Version notes

|Feature|CHT Core version|
|---|---|
|Released |3.10.0|
79 changes: 79 additions & 0 deletions content/en/apps/guides/android/branding.md
Original file line number Diff line number Diff line change
@@ -235,3 +235,82 @@ Releasing a new flavor requires the following steps:
### 6. Publish the app

The last step is to publish it in the Play Store, or whatever option best suit your needs. Checkout the [Publishing]({{< ref "apps/guides/android/publishing" >}}) page to see all the options available and instructions.

## Android App Links verification
*Supported for CHT Core 4.7.0+ and CHT Android 1.3.0+*

Starting with Android 12, Android supports associating an app with a domain and automatically verifying this association. This allows deep links to immediately open content in the app. To get this working, you need to host a Digital Asset Links JSON file at `https://<domain.name>/.well-known/assetlinks.json` containing some information about your app to associate it with your domain. More information is available on the [official Android docs](https://developer.android.com/training/app-links/verify-android-applinks).

### Hosting `assetlinks.json` with the CHT

Since CHT Core version 4.7.0, the CHT supports serving `assetlinks.json` by adding it to your app settings.
All you have to do to make the CHT serve your assetlinks at `/.well-known/assetlinks.json` is to:
1. Ensure your flavor of cht-android [has a valid keystore]({{< ref "apps/guides/android/branding#3-generate-a-new-keystore" >}}).
2. Use the `keytool` utility (included with your Java SDK) to get your app's cert fingerprint:
```
keytool -list -v -keystore ./path/to/release-key.keystore
# or alternatively:
keytool -printcert -jarfile ./path/to/project.apk
```
3. Set the cert fingerprint in the [`assetlinks` configuration]({{< ref "apps/reference/app-settings/assetlinks" >}}) for your CHT instance and deploy it to your server with cht-conf.

### Verifying it works

There are different ways to verify your setup works and we'll go through a few of them in the next steps.

#### Using Android Debug Bridge `adb`

1. To install the `adb` command, follow the instructions under the [Development Environment > Debug tool adb]({{< ref "contribute/code/android/development-setup#debug-tool-adb" >}}) section.
2. With the phone connected to your computer, open a command line session and write the following command: `adb shell pm get-app-links <package_name>` where `<package_name>` is your application ID.

The output of this command should look like this:
```
<package_name>:
ID: 01234567-89ab-cdef-0123-456789abcdef
Signatures: ["62:BF:C1:78:24:D8:4D:5C:B4:E1:8B:66:98:EA:14:16:57:6F:A4:E5:96:CD:93:81:B2:65:19:71:A7:80:EA:4D"]
Domain verification state:
mobile.webapp.medicmobile.org: verified
```

The domain verification state for your CHT instance's domain should show `verified`.

#### Manually testing on the device

{{< figure src="android-12-prompt.png" link="android-12-prompt.png" class="right col-6 col-md-4 col-lg-2" >}}

Another way of verifying your Android app has been properly associated to your CHT instance's domain is by opening
the Android app on a device. You can run this test on a real device or with the emulator in Android Studio.

Opening the app for the first time should take you straight to the login page __without__ prompting you to link a domain to the app as shown in the following screenshot:

Additionally, clicking a link to your CHT instance should open the app immediately instead of opening the CHT instance in the default browser.

### Use case - a single Android app for many CHT instances

For specific large deployment scenarios, you might publish a single Android app to serve multiple CHT instances.
In this case, each CHT instance's app settings will need to be configured with the same `assetlinks.json` because
they share the same Android app and hence the same `package_name` and `sha256_cert_fingerprints` properties.

When building your Android app, you will need to ensure the app's manifest has `<intent-filter android:autoverify="true">`
with each CHT instance's domain listed in it like so:

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove" />
<application>
<activity android:name="AppUrlIntentActivity" android:launchMode="singleInstance" android:exported="true">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="first-cht-app.org" android:pathPattern=".*"/>
<data android:scheme="https" android:host="second-cht-app.org" android:pathPattern=".*"/>
<data android:scheme="https" android:host="third-cht-app.org" android:pathPattern=".*"/>
</intent-filter>
</activity>
</application>
</manifest>
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 21 additions & 14 deletions content/en/apps/reference/app-settings/_index.md
Original file line number Diff line number Diff line change
@@ -9,7 +9,13 @@ keywords: settings

The settings which control CHT apps are defined in the `app_settings.json` file, and stored in the `settings` doc in the database. Some settings can be modified in the [**App Management**]({{% ref "apps/features/admin" %}}) app, which updates the same settings file in the database.

The settings get compiled into the `app_settings.json` file with the `compile-app-settings` action in the `cht-conf` tool. Manually configurable settings are added to the `app_settings` folder at the root of the config folder. The `app_settings/base_settings.json` file can be manually edited to modify individual settings. [`forms`]({{% ref "apps/reference/app-settings/forms" %}}) and [`schedules`]({{% ref "apps/reference/app-settings/schedules" %}}) sections can be defined in separate files named `app_settings/forms.json` and `app_settings/schedules.json` respectively with the settings in the files overriding what might be already present in the `app_settings/base_settings.json` or `app_settings.json` files.
The settings get compiled into the `app_settings.json` file with the `compile-app-settings` action in the `cht-conf` tool.
Manually configurable settings are added to the `app_settings` folder at the root of the config folder.
The `app_settings/base_settings.json` file can be manually edited to modify individual settings.
[`forms`]({{% ref "apps/reference/app-settings/forms" %}}), [`schedules`]({{% ref "apps/reference/app-settings/schedules" %}}),
and [`assetlinks`]({{% ref "apps/reference/app-settings/assetlinks" %}}) sections can be defined in separate files named
`app_settings/forms.json`, `app_settings/schedules.json`, and `app_settings/assetlinks.json` respectively with the settings
in the files overriding what might be already present in the `app_settings/base_settings.json` or `app_settings.json` files.

Most sections are described on their own in the [Reference Documentation]({{< ref "apps/reference" >}}).

@@ -29,20 +35,21 @@ The following settings do not need to be specified. They should only be defined
### `app_settings.json`

| Setting | Description | Default | Version |
|----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|---------|
| phone_validation | <ul><li>"full": full validation of a phone number for a region using length and prefix information.</li><li>"partial": quickly guesses whether a number is a possible phone number by using only the length information, much faster than a full validation.</li><li>"none": allows almost any values but still fails for any phone that contains a-z chars.</li></ul> | "full" | 3.1.0 |
|----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|---------|
| phone_validation | <ul><li>"full": full validation of a phone number for a region using length and prefix information.</li><li>"partial": quickly guesses whether a number is a possible phone number by using only the length information, much faster than a full validation.</li><li>"none": allows almost any values but still fails for any phone that contains a-z chars.</li></ul> | "full" | 3.1.0 |
| uhc.contacts_default_sort | <ul><li>"alpha": Sort contacts alphanumerically</li><li>"last_visited_date": sort contacts by the date they were most recently visited.</li></ul> | "alpha" | 2.18.0 |
| uhc.visit_count.month_start_date | The date of each month when the visit count is reset to 0. | 1 | 2.18.0 |
| uhc.visit_count.visit_count_goal | The monthly visit count goal. | 0 | 2.18.0 |
| outgoing_deny_list | All outgoing messages will be denied (unsent) if the recipient phone number starts with an entry in this list. A comma delimited list. (eg. `outgoing_deny_list="253,ORANGE"` will deny all messages sent to `253 543 4448` and `ORANGE NET`) | "" | |
| outgoing_deny_shorter_than | Deny all messages to recipient phone numbers which are shorter than this value. Intended to avoid [message loops]({{% ref "apps/guides/messaging/message-loops" %}}) with short codes used by gateways (eg. `60396`). An integer. | 6 | 3.3.0 |
| outgoing_deny_with_alphas | When `true`, deny all messages to recipient phone numbers containing letters (eg. `Safaricom`). Intended to avoid [message loops]({{% ref "apps/guides/messaging/message-loops" %}}) with non-numeric senders used by gateways. A boolean. | true | 3.3.0 |
| outgoing_deny_with_alphas | When `true`, deny all messages to recipient phone numbers containing letters (eg. `Safaricom`). Intended to avoid [message loops]({{% ref "apps/guides/messaging/message-loops" %}}) with non-numeric senders used by gateways. A boolean. | true | 3.3.0 |
| task_day_limit | The number of days before a task is due to show the due date. | 4 | 3.9.0 |
| app_url | The URL of the app, eg: "https://demo.app.medicmobile.org" | | 3.10.0 |
| task_days_overdue | Display number of overdue days in tasks list | false | 3.13.0 |
| languages | Array of objects with `locale` and `enabled` properties representing respectively the 2 or 3 letter language code and whether that language should be enabled. <br/>If unset it falls back to the previous behavior of relying on the `enabled` property of each translation document `messages-XX.properties`. This fallback behavior is now deprecated and will be removed in the next major version (5.0). This `languages` configuration property will be required for CHT 5.0+. | | 4.2.0 |
| place_hierarchy_types | Array of contact types' IDs, should match the ones defined in `contact_types`. This is used to define the Place Filter's options in Reports tab. | | 2.15.0 |
| uhc.visit_count.month_start_date | The date of each month when the visit count is reset to 0. | 1 | 2.18.0 |
| uhc.visit_count.visit_count_goal | The monthly visit count goal. | 0 | 2.18.0 |
| outgoing_deny_list | All outgoing messages will be denied (unsent) if the recipient phone number starts with an entry in this list. A comma delimited list. (eg. `outgoing_deny_list="253,ORANGE"` will deny all messages sent to `253 543 4448` and `ORANGE NET`) | "" | |
| outgoing_deny_shorter_than | Deny all messages to recipient phone numbers which are shorter than this value. Intended to avoid [message loops]({{% ref "apps/guides/messaging/message-loops" %}}) with short codes used by gateways (eg. `60396`). An integer. | 6 | 3.3.0 |
| outgoing_deny_with_alphas | When `true`, deny all messages to recipient phone numbers containing letters (eg. `Safaricom`). Intended to avoid [message loops]({{% ref "apps/guides/messaging/message-loops" %}}) with non-numeric senders used by gateways. A boolean. | true | 3.3.0 |
| outgoing_deny_with_alphas | When `true`, deny all messages to recipient phone numbers containing letters (eg. `Safaricom`). Intended to avoid [message loops]({{% ref "apps/guides/messaging/message-loops" %}}) with non-numeric senders used by gateways. A boolean. | true | 3.3.0 |
| task_day_limit | The number of days before a task is due to show the due date. | 4 | 3.9.0 |
| app_url | The URL of the app, eg: "https://demo.app.medicmobile.org" | | 3.10.0 |
| task_days_overdue | Display number of overdue days in tasks list | false | 3.13.0 |
| languages | Array of objects with `locale` and `enabled` properties representing respectively the 2 or 3 letter language code and whether that language should be enabled. <br/>If unset it falls back to the previous behavior of relying on the `enabled` property of each translation document `messages-XX.properties`. This fallback behavior is now deprecated and will be removed in the next major version (5.0). This `languages` configuration property will be required for CHT 5.0+. | | 4.2.0 |
| place_hierarchy_types | Array of contact types' IDs, should match the ones defined in `contact_types`. This is used to define the Place Filter's options in Reports tab. | | 2.15.0 |
| assetlinks | Array of [Digital Asset Links](https://developers.google.com/digital-asset-links/v1/getting-started) definitions. This is used to associate your CHT instance's domain to your Android app to verify app links. | | 4.7.0 |

## SMS Workflows

44 changes: 44 additions & 0 deletions content/en/apps/reference/app-settings/assetlinks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: ".assetlinks"
linkTitle: ".assetlinks"
weight: 5
description: >
**Assetlinks**: Defining the Digital Asset Links JSON file associating your domain with your Android app.
relatedContent: >
apps/features/integrations/android
apps/guides/android/branding
keywords: android assetlinks
---

*Requires CHT Core 4.7.0+, CHT Conf 3.22.0+, and CHT Android 1.3.0+*

When using a [custom flavor of cht-android]({{< ref "apps/guides/android/branding" >}}) to connect to your CHT instance, the ecosystem supports using [deep links]({{< ref "apps/features/integrations/android#sending-a-url" >}}) to open specific content in the app. (E.g. [token login links]({{< ref "apps/concepts/access#magic-links-for-logging-in-token-login" >}})). Security measures in Android require these deep links [be verified](https://developer.android.com/training/app-links/verify-android-applinks) either automatically or manually. This `assetlinks` configuration enables auto-verification for your CHT links in your Android app. The provided JSON file will be served at `https://<your CHT instance>/.well-known/assetlinks.json`. If you do not provide this configuration, users will be prompted to manually associate the CHT domain with your app.

For more information, see the [docs for building a new CHT Android flavor]({{< ref "apps/guides/android/branding#android-app-links-verification" >}}).

Specify your digital asset links in the `app_settings/assetlinks.json` file. The `compile-app-settings` action in the `cht-conf` will automatically include this configuration in your `app_settings.json` file. Then, running the `upload-app-settings` action will deploy it to the server.

## `app_settings.json .assetlinks[]`

| property | type | description | required |
|-----------------------------------|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|
| `relation[]` | `Array<string>` | The array should contain only one element: the string `delegate_permission/common.handle_all_urls`. | yes |
| `target` | `object` | Contains fields to identify associated apps. | yes |
| `target.namespace` | `string` | Must be set to `android_app`. | yes |
| `target.package_name` | `string` | The [application ID]({{< ref "apps/guides/android/branding#2-new-brand" >}}) declared in the app's `build.gradle` file. | yes |
| `target.sha256_cert_fingerprints` | `Array<string>` | The SHA256 fingerprints of your app’s signing certificate. You can get it with the Java utility `keytool`, see how exactly in our [Android guide]({{< ref "apps/guides/android/branding#hosting-assetlinksjson-with-the-cht" >}}). | yes |

## Code Sample

This sample associates the Android app `org.medicmobile.webapp.mobile` to a CHT instance and grants it link-opening rights to the Android app:

```json
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "org.medicmobile.webapp.mobile",
"sha256_cert_fingerprints": ["62:BF:C1:78:24:D8:4D:5C:B4:E1:8B:66:98:EA:14:16:57:6F:A4:E5:96:CD:93:81:B2:65:19:71:A7:80:EA:4D"]
}
}]
```