Mobile-Only Operations with Deep Links and Custom URIs
Out-of-band operations normally involve two devices: the user starts an operation in a browser on a desktop machine and confirms it in the Access App on a phone. In the mobile-only scenario the browser and the Access App run on the same device, so a QR code cannot be used, and no push notification is required. Instead, the relying party application renders a link. Tapping that link opens the Access App and hands over the out-of-band payload.
This page describes how to configure the nevisAdmin 4 side of that scenario. The link itself is generated by the nevisFIDO Link Dispatcher, and its structure and behavior on each mobile platform are documented in the Access App Link channel.
When you need a link
Mobile authentication does not always involve a link. The configuration on this page is only required for flows that dispatch one: out-of-band registration and authentication rendered as a link, usernameless authentication, and the Out-of-band Device Management App, which does not deploy at all until Link Type is set. Flows that dispatch only a push notification or a QR code, and applications that integrate the Mobile SDK in band, need none of it.
The shipped login templates request a dispatcher that returns both a QR code and a link. They then use the browser user agent to decide what to show: the QR code is always rendered, and the tappable link is revealed in addition when the user agent looks like a phone or tablet.
This is presentation only. The page does not detect whether the Access App is installed. A desktop user is expected to scan the QR code with a phone, and on the phone it is the operating system, not the page, that decides whether the link opens the app or falls back to the browser.
Choosing a link type
The Link Type setting of the nevisFIDO UAF Instance pattern controls which kind of link nevisFIDO generates:
| Link Type | Meaning |
|---|---|
Deep Link | An app link using the standard https:// scheme. Recommended. |
Custom URI | A link using an application-specific scheme, for example myaccessapp://. |
undefined | The pattern generates no link dispatcher configuration. |
Nevis recommends app links. They give a better end-user experience, they do not require an extra permission prompt on iOS, and they let you fall back to a web page when the app is not installed. They are also more secure: any application can register a custom URI, whereas an app link is verified against the domain.
Custom URIs remain fully supported and are the right choice when the app link prerequisites cannot be met, for example on Android devices without Google Play Services, or with browsers that do not support app links. They are suitable only for mobile-only scenarios such as this one, because a custom URI can only be opened on the device where the Access App is installed and cannot bridge from a desktop browser to a phone. See Limitations and Restrictions for the full comparison.
The link type and the concrete domain or URI scheme are built into the Access App at build time. They must be supplied during the app order process, see App Link Domain / Customer URI scheme. Changing them later requires a new app release.
Prerequisites
- The nevisFIDO Setup is completed.
- Out-of-band registration and/or authentication are configured, using the Out-of-band Mobile Registration Service and Out-of-band Mobile Authentication patterns.
- A relying party application is available that can call the
Dispatch Token Service
requesting the
linkdispatcher, and render the returned link. - For deep links only: a second hostname that is not the one serving the relying party
application. A different subdomain is sufficient, for example
login.example.comfor the application andlinks.example.comfor the links. This is an iOS requirement: when a universal link is tapped in Safari from a page on the same host, iOS opens it in Safari instead of the app. See the Access App Prerequisites.
Who supplies which artifact
Deep links involve several artifacts that are easy to confuse. nevisAdmin 4 hosts files that you upload, but it does not create any of them. The following table shows who produces each one.
| Artifact | Produced by | Configured in |
|---|---|---|
| App link domain, or custom URI scheme | You | Access App order, then Deep Link or Custom URI Link |
| App identifiers: iOS Team and Bundle Identifier, Android Package Name and SHA256 fingerprint | Result of the Access App build | Used to write the app link files |
apple-app-site-association | You, written manually | Deep Link Resources |
assetlinks.json | You, generated or written manually | Deep Link Resources |
| Fallback installation page and its static resources | You | Deep Link Resources |
| App Store and Google Play listing URLs | You, once the app is published | Referenced from the installation page |
| App Store and Google Play badge artwork | Apple and Google, downloaded by you | Deep Link Resources, referenced from the installation page |
| Hosting of all uploaded files | nevisAdmin 4 | Deep Link Host |
The two groups of files serve different purposes. The app link files make the operating system open the app. The installation page is what the user sees when the app does not open, and it is the only place where App Store and Google Play links and badges belong. Nevis supplies none of them: publishing the app to the stores is the customer's responsibility, see Ordering an Access App.
Configuration workflow
Some of these steps depend on each other. In particular, you cannot build the app link files before the Access App has been built, because they contain identifiers that are only known at that point.
-
Choose the link type. Set Link Type on the nevisFIDO UAF Instance pattern to
Deep LinkorCustom URI. -
Define the link.
- For
Deep Link, enter a complete URL in the Deep Link field, for examplehttps://links.example.com/open. Include a path component: if you leave it at/, the browser fallback lands on the root of the host, where there is usually no suitable content. - For
Custom URI, enter the URI in the Custom URI Link field, for examplemyaccessapp://x-callback-url/authenticate.
- For
-
Expose the deep link domain. Make sure the host used in the Deep Link resolves to a Virtual Host in your project. You can either add a dedicated Virtual Host pattern, or add the deep link hostname as an additional Frontend Address on an existing one. See the Patterns Reference for the pattern's settings.
-
Order the Access App. Follow Ordering an Access App and supply the backend URL and the deep link domain or URI scheme. When the app has been built, note down the following, you need it to build the app link files:
System Information to obtain iOS Team Identifier, Bundle Identifier Android Package Name, SHA256 Fingerprint -
Create and upload the app link files. See Hosting the app link files below.
-
Build and upload the fallback installation page. See The fallback installation page below. If the app is not yet published, see Before the store listings exist.
-
Assign the deep link host. Set Deep Link Host to the Virtual Host from step 3.
-
Connect the relying party application. See Connecting the relying party application below.
-
Deploy the configuration with nevisAdmin 4.
Hosting the app link files
For deep links to work, the mobile operating system must fetch verification files from the deep link
domain. The simplest way to publish them is the
Deep Link Resources field of the
nevisFIDO UAF Instance pattern. nevisAdmin 4 recognizes them by their file name and hosts them
under /.well-known/ on the Deep Link Host, so you do not need a separate web server. This is a
convenience: if the files are already published elsewhere, see
Hosting the files elsewhere. The same field also takes the fallback
installation page and its static resources, which are hosted at the root of the Deep Link Host
instead, see The fallback installation page.
| Platform | File name | Hosted at |
|---|---|---|
| iOS | apple-app-site-association (no file extension) | /.well-known/apple-app-site-association |
| Android | assetlinks.json (with the extension) | /.well-known/assetlinks.json |
The contents of both files are documented, with working examples, in the Access App appendix:
How to obtain each file
The two platforms differ in how much help you get.
-
Android. Generate
assetlinks.jsonwith Google's Statement List Generator. It asks for the hosting site domain, which is the domain of your Deep Link, the application package name, and the SHA256 fingerprint of the certificate the app was signed with. For a Nevis branded Access App the package name isch.nevis.security.accessapp. You can also write the file by hand, it is a short JSON document.noteIf Google Play App Signing is used, the certificate that ends up signing the delivered app is Google's, not yours. Take the SHA256 fingerprint from the Google Play Console, otherwise the fingerprint in
assetlinks.jsondoes not match the installed app and verification fails. The same distinction applies to the FacetID, see FacetID Calculation. -
iOS. There is no generator. Write
apple-app-site-associationmanually from the Team Identifier and Bundle Identifier of the built app, combined as<team id>.<bundle id>. The App Link Files field of the pattern shows the required structure.
nevisAdmin 4 hosts every uploaded file under its file name alone. The two app link files go to
/.well-known/, and every other file goes directly to the root of the Deep Link Host, so a file
uploaded as app-store-badge.svg is served at https://links.example.com/app-store-badge.svg.
Your installation page therefore has to reference its resources as flat root-relative paths, such as
/app-store-badge.svg. A page that expects /assets/img/app-store-badge.svg finds nothing. If you
need real directories, host the page with the
Hosting Service pattern instead.
If several apps share one deep link domain, see Multiple Android applications sharing the same app link domain and Multiple iOS applications sharing the same app link domain.
The Access App registers only a scheme and a host: on Android the intent filter declares
https plus the deep link domain with autoVerify enabled, and on iOS the entitlement is
applinks:<domain>. Neither carries a path restriction.
Adding a path to your Deep Link therefore does not by itself restrict which URLs open the app.
The restriction is expressed entirely in the components block of the apple-app-site-association
file. An over-broad rule captures unrelated URLs on that host, which is the practical reason to use a
dedicated subdomain for links.
The fallback installation page
If the app is not installed, or no rule in the app link file matches, the operating system opens the deep link in the browser instead. Without an installation page the user lands on an empty response and the operation simply stops. The installation page is the recovery path, and it is where the App Store and Google Play links belong. Upload it and its static resources into Deep Link Resources, which hosts them at the root of the Deep Link Host, see Installation Page.
To serve the page from Deep Link Resources, name the uploaded file after the path of your
Deep Link, without a file extension. If the deep link is https://links.example.com/open, the file
must be named open. nevisAdmin 4 hosts uploads flat, under the file name alone, so this only works
for a deep link with a single path segment.
A deep link with a longer path, such as https://links.example.com/app/open, is a valid
configuration. It simply means the fallback page has to come from somewhere else, for example a
Hosting Service pattern. In that
case nevisAdmin 4 reports Missing deep link target, which is advisory here, see
Deployment warnings.
The page has no file extension, so verify that it is actually served as HTML in your environment, see Verifying the deployment. If it is not, host it with the Hosting Service pattern, which gives you full control over paths and content types.
Linking to the store listings
Both stores address an application by an identifier you get when the listing is created.
| Store | Link format | Where the identifier comes from |
|---|---|---|
| Google Play | https://play.google.com/store/apps/details?id=<package name> | The application package name, the same one used in assetlinks.json |
| App Store | https://apps.apple.com/app/id<apple id> | The numeric Apple ID of the listing, shown in App Store Connect |
For Google Play you can also build campaign-tagged links, see Link to your Google Play app listing. Serve both links on the same page and let the user choose, rather than detecting the platform in the page: detection is easy to get wrong, and a user who lands on the wrong listing has no way forward.
Using the official store badges
Neither Nevis nor nevisAdmin 4 supplies badge artwork. Download it from Apple and Google, add the image files to Deep Link Resources, and reference them from your page.
| Store | Where to get the artwork |
|---|---|
| App Store | App Store Marketing Guidelines, which also provide marketing tools that generate an embeddable badge for a chosen language and style |
| Google Play | Google Play badges, part of Google's Partner Marketing Hub |
Both badges are trademarked artwork licensed for a specific purpose, so a few constraints apply.
- Use only the artwork provided by Apple and Google. Do not redraw, recolor, rotate, animate, or
otherwise modify a badge, and do not translate the words
App StoreorGoogle Play. Both companies publish localized versions, so pick the localization instead of making your own. - Apple requires a minimum badge height of 40 pixels onscreen, with clear space around it of one quarter of the badge height, reducible to one tenth in tight layouts. Apple licenses the artwork under the App Store Marketing Artwork License Agreement, which applies only while you are a member of the Apple Developer Program and only for applications published on the App Store. Since the customer owns the developer accounts and the app distribution, the customer also holds this license.
- Prefer the SVG artwork where the official download offers it, so the badges stay sharp on high-density mobile screens.
The Deep Link Resources field imposes no restriction on file type or file size, so the real constraints come from the badge licenses and from what your target devices display, not from nevisAdmin 4. Keep the uploads small, since the fallback page is often loaded on mobile data at the moment an operation has already failed once. Nevis documentation does not redistribute the artwork, because the licenses do not permit it, so always download the current files from the sources above.
Before the store listings exist
The application has to be built and published before you have store URLs, but the deep link configuration is usually needed earlier for testing. Do not block on the stores. Deploy an interim installation page that explains what the application is and how the user obtains it in your organization, for example through a pilot distribution or an internal application catalog, so the fallback path works end to end. Do not publish placeholder badges that link nowhere: they look like a broken application, and they use the trademarked artwork outside its licensed terms. When the listings go live, upload the final page and badge images and deploy again. No other pattern setting changes, so this is a content-only update.
Connecting the relying party application
The relying party application requests the link from nevisFIDO and renders it for the end user. This application is not provided by Nevis, deploy it independently. See also Protecting a Web Application.
Add a Web Application pattern for it, or open the existing one, and set the following:
- Virtual Hosts, Basic Settings tab: assign the Virtual Host that serves the application. For deep links this is deliberately not the Deep Link Host, see the iOS same-host restriction in the Prerequisites.
- Frontend Paths, Basic Settings tab: specify the desired frontend path.
- Authentication Realm, Basic Settings tab: assign the same realm as the one assigned to your Out-of-band Mobile Registration Service pattern.
- Application Access Token, Basic Settings tab: assign the same token as the one assigned to that pattern.
- Backend Addresses, Backend Connection tab: point to the relying party application's backend.
The format of the response the application receives from nevisFIDO is described under Dispatch Token Response Format.
Verifying the deployment
A misconfigured app link rarely produces an error message. From the user's point of view the link simply behaves like an ordinary web link, so these problems are easy to miss in testing and easy to misread as a nevisFIDO problem. Watch for these symptoms:
| Symptom | Usual cause |
|---|---|
| The browser stays on the page, or navigates to a blank page | The operating system did not accept the association file, so it treated the link as a normal navigation |
| The fallback page is empty or unstyled | The installation page is missing, is named differently from the deep link path, or references resources by a path that does not exist because uploads are hosted flat |
| An application chooser appears instead of the Access App | The link was not verified as an app link, so Android offered every application that can handle https |
| Nothing happens at all | A custom URI whose scheme is not registered by any installed application |
| The app opens on iOS but not after an update to the app link file | iOS caches the association file, so the app has to be reinstalled |
Deployment warnings
nevisAdmin 4 reports three warnings when the uploaded resources do not line up with the rest of the configuration. All three are warnings rather than failures, so the deployment succeeds and the problem only becomes visible on a real device.
| Warning | Most likely cause | How to resolve |
|---|---|---|
No Apple App Site Association file found. | The file name is matched exactly, and browsers commonly rename this file on download because it has no extension, for example to apple-app-site-association.json or apple-app-site-association.txt | Rename the upload to exactly apple-app-site-association, with no file extension |
No Android App Links file found. | The same exact match, usually a download renamed to assetlinks(1).json, or a file saved as AssetLinks.json | Rename the upload to exactly assetlinks.json, all lowercase, keeping the extension |
Missing deep link target '<path>'. | No uploaded file is named after the path of the Deep Link | Advisory, see below |
The match is case sensitive in all three cases.
Treat the first two as blocking whenever you rely on Deep Link Resources for hosting: the operating system cannot verify the app link without those files.
The third one is advisory. nevisAdmin 4 compares the path of the Deep Link with / plus the
name of each uploaded file, purely to tell you whether the browser fallback has something to serve
from this pattern. Uploads are hosted regardless of the outcome, and the message therefore also
appears in configurations that are perfectly valid, in particular when the fallback page is hosted
elsewhere, when the deep link path has more than one segment or a trailing slash, or when the path
comes from a variable. If the fallback URL is served by other means, ignore it.
If Deep Link Resources is left empty, nevisAdmin 4 skips this generation step entirely and none
of the three warnings appears. If no Deep Link Host is assigned either, the pattern only emits an
information message. A project with Link Type set to Deep Link, no Deep Link Host and no
uploaded resources therefore deploys without a single warning, whether the files are served from
somewhere else or not served at all. Confirm the files are actually served, as described below,
rather than relying on a clean deployment.
Deep Link Resources is offered for convenience and is never the only way to publish these files. You can serve them with a Hosting Service pattern assigned to the same Virtual Host, or from infrastructure outside Nevis entirely. What matters to the mobile operating system is only that the files are reachable over HTTPS at the expected URLs on the deep link domain. If you host them elsewhere, leave Deep Link Resources empty and ignore all three warnings, which describe the built-in hosting only.
The three messages are raised as warnings only during an actual deployment. A validation or preview
run reports the same conditions as information messages, which is easy to scroll past, so check the
deployment log rather than only the validation panel. If you uploaded everything and instead see
Configuration is unused. Set 'Link Type' to 'Deep Link'., then Link Type is still Custom URI or
undefined, and nothing configured under Deep Link, Deep Link Host or Deep Link Resources is
generated at all.
Checks after deployment
-
Check that the app link files are served correctly, without a redirect and with
Content-Type: application/json:curl -i https://links.example.com/.well-known/apple-app-site-associationcurl -i https://links.example.com/.well-known/assetlinks.jsonBoth must return
200and the raw JSON. Serve them directly at these URLs, without redirects, as the platform requirements expect. A redirect to a login page is a common failure, and it means the/.well-known/path is covered by an authentication realm. -
Check that the fallback page and its resources are served, and that the page is delivered as HTML. The page file has no extension, so confirm the content type rather than assuming it:
curl -i https://links.example.com/opencurl -i https://links.example.com/app-store-badge.svg -
Open the relying party application in a mobile browser without the Access App installed. The rendered link must lead to your installation page, and both store badges must render and point at the correct listings.
-
Install the Access App and open the link again. The app must now open and continue the operation.
noteiOS fetches and caches the
apple-app-site-associationfile when the app is installed or updated. After changing the file, reinstall the app to pick up the new rules. -
If the app does not open on Android, check that opening supported links is enabled for the app in the Android settings, see Android - Enable Opening Links in Application Settings.
-
Before going to production, check that the Facets setting of the nevisFIDO UAF Instance lists your own iOS and Android facet identifiers rather than the wildcard defaults, which production Access Apps and release SDK applications reject. This is not part of the link configuration, but it fails at the same point in a rollout.
For a broader list of device and browser specific behavior, see Limitations and Restrictions and the Access App Known Issues.
For debugging, a generated deep link looks like this:
https://links.example.com/open?dispatchTokenResponse=<base64url_payload>
The dispatchTokenResponse query parameter carries the base64url-encoded out-of-band payload. The
full structure, including the optional x-callback parameters, is documented under
App links structure and
Custom URI structure.