Skip to content

Commit e169247

Browse files
fix: add android:autoVerify="true" for Android App Links in Expo plugin (#1355)
1 parent 70c280e commit e169247

File tree

3 files changed

+116
-4
lines changed

3 files changed

+116
-4
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,9 @@ To use the SDK with Expo, configure the app at build time by providing the `doma
215215
| API | Description |
216216
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
217217
| domain | Mandatory: Provide the Auth0 domain that can be found at the [Application Settings](https://manage.auth0.com/#/applications) |
218-
| customScheme | Optional: Custom scheme to build the callback URL with. The value provided here should be passed to the `customScheme` option parameter of the `authorize` and `clearSession` methods. The custom scheme should be a unique, all lowercase value with no special characters. |
218+
| customScheme | Optional: Custom scheme to build the callback URL with. The value provided here should be passed to the `customScheme` option parameter of the `authorize` and `clearSession` methods. The custom scheme should be a unique, all lowercase value with no special characters. To use Android App Links, set this value to `"https"`. |
219+
220+
**Note:** When using `customScheme: "https"` for Android App Links, the plugin will automatically add `android:autoVerify="true"` to the intent-filter in your Android manifest to enable automatic verification of App Links.
219221

220222
Now you can run the application using `expo run:android` or `expo run:ios`.
221223

src/plugin/__tests__/withAuth0-test.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,101 @@ describe(addAndroidAuth0Manifest, () => {
212212
expect(dataElement?.$['android:scheme']).toBe('com.custom.scheme');
213213
expect(dataElement?.$['android:host']).toBe('sample.auth0.com');
214214
});
215+
216+
it(`should add android:autoVerify="true" when customScheme is https`, () => {
217+
const config = getConfig();
218+
const result = addAndroidAuth0Manifest(
219+
[
220+
{
221+
domain: 'sample.auth0.com',
222+
customScheme: 'https',
223+
},
224+
],
225+
config,
226+
'com.auth0.testapp'
227+
);
228+
229+
// Access the RedirectActivity to check if autoVerify is correctly added
230+
const mainApplication = AndroidConfig.Manifest.getMainApplicationOrThrow(
231+
result.modResults
232+
);
233+
const redirectActivity = mainApplication.activity?.find(
234+
(activity) =>
235+
activity.$['android:name'] ===
236+
'com.auth0.android.provider.RedirectActivity'
237+
);
238+
239+
const intentFilter = redirectActivity?.['intent-filter']?.[0];
240+
241+
expect(intentFilter?.$).toBeDefined();
242+
expect(intentFilter?.$?.['android:autoVerify']).toBe('true');
243+
244+
const dataElement = intentFilter?.data?.[0];
245+
expect(dataElement?.$['android:scheme']).toBe('https');
246+
expect(dataElement?.$['android:host']).toBe('sample.auth0.com');
247+
});
248+
249+
it(`should add android:autoVerify="true" when customScheme is http`, () => {
250+
const config = getConfig();
251+
const result = addAndroidAuth0Manifest(
252+
[
253+
{
254+
domain: 'sample.auth0.com',
255+
customScheme: 'http',
256+
},
257+
],
258+
config,
259+
'com.auth0.testapp'
260+
);
261+
262+
// Access the RedirectActivity to check if autoVerify is correctly added
263+
const mainApplication = AndroidConfig.Manifest.getMainApplicationOrThrow(
264+
result.modResults
265+
);
266+
const redirectActivity = mainApplication.activity?.find(
267+
(activity) =>
268+
activity.$['android:name'] ===
269+
'com.auth0.android.provider.RedirectActivity'
270+
);
271+
272+
const intentFilter = redirectActivity?.['intent-filter']?.[0];
273+
274+
expect(intentFilter?.$).toBeDefined();
275+
expect(intentFilter?.$?.['android:autoVerify']).toBe('true');
276+
277+
const dataElement = intentFilter?.data?.[0];
278+
expect(dataElement?.$['android:scheme']).toBe('http');
279+
expect(dataElement?.$['android:host']).toBe('sample.auth0.com');
280+
});
281+
282+
it(`should not add android:autoVerify when customScheme is not http/https`, () => {
283+
const config = getConfig();
284+
const result = addAndroidAuth0Manifest(
285+
[
286+
{
287+
domain: 'sample.auth0.com',
288+
customScheme: 'com.custom.scheme',
289+
},
290+
],
291+
config,
292+
'com.auth0.testapp'
293+
);
294+
295+
// Access the RedirectActivity
296+
const mainApplication = AndroidConfig.Manifest.getMainApplicationOrThrow(
297+
result.modResults
298+
);
299+
const redirectActivity = mainApplication.activity?.find(
300+
(activity) =>
301+
activity.$['android:name'] ===
302+
'com.auth0.android.provider.RedirectActivity'
303+
);
304+
305+
const intentFilter = redirectActivity?.['intent-filter']?.[0];
306+
307+
// autoVerify should not be present for non-http(s) schemes
308+
expect(intentFilter?.$?.['android:autoVerify']).toBeUndefined();
309+
});
215310
});
216311

217312
describe(addIOSAuth0ConfigInInfoPList, () => {

src/plugin/withAuth0.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,15 @@ export const addAndroidAuth0Manifest = (
3030
if (auth0Configs.length === 0) {
3131
throw new Error(`No auth0 domain specified in expo config`);
3232
}
33+
// Check if any config uses https/http scheme for App Links
34+
const hasAppLinks = auth0Configs.some(
35+
(config) =>
36+
config.customScheme === 'https' || config.customScheme === 'http'
37+
);
3338

3439
const intentFilterContent = [
3540
{
41+
...(hasAppLinks && { $: { 'android:autoVerify': 'true' as AndroidConfig.Manifest.StringBoolean } }),
3642
action: [{ $: { 'android:name': 'android.intent.action.VIEW' } }],
3743
category: [
3844
{ $: { 'android:name': 'android.intent.category.DEFAULT' } },
@@ -61,17 +67,26 @@ export const addAndroidAuth0Manifest = (
6167
'tools:node': 'replace',
6268
'android:exported': 'true',
6369
},
64-
'intent-filter': intentFilterContent,
70+
'intent-filter': intentFilterContent as AndroidConfig.Manifest.ManifestIntentFilter[],
6571
};
6672
mainApplication.activity = mainApplication.activity || [];
6773
mainApplication.activity.push(redirectActivity);
6874
}
69-
75+
7076
redirectActivity['intent-filter'] =
71-
redirectActivity['intent-filter'] || intentFilterContent;
77+
redirectActivity['intent-filter'] || intentFilterContent as AndroidConfig.Manifest.ManifestIntentFilter[];
7278
const intentFilter = redirectActivity['intent-filter'][0] || {};
79+
if (!intentFilter) {
80+
throw new Error('Failed to create intent filter');
81+
}
7382
intentFilter.data = intentFilter.data || [];
7483

84+
// Add android:autoVerify="true" for App Links
85+
if (hasAppLinks) {
86+
intentFilter.$ = intentFilter.$ || {};
87+
intentFilter.$['android:autoVerify'] = 'true' as AndroidConfig.Manifest.StringBoolean;
88+
}
89+
7590
// Add data elements for each auth0Config
7691
auth0Configs.forEach((config) => {
7792
if (config.domain == null) {

0 commit comments

Comments
 (0)