Skip to content

Use Universal Links as redirect URI on iOS

Wondering if there is safer way to handle external URLs in your application? Maybe, you are tired of constant redirect to Safari and prompt to open your app before actually navigating to the application? Is there a solution? Yes - Universal links. Does OAuth 2.0 possible with Universal Links? Yes. This article is about how to deal with all the above struggles.

Overview

Use Universal Links to link directly to content within your app and share data securely. You can connect to content deep inside your app with Universal Links. Users open your app in a specified context, allowing them to accomplish their goals efficiently.

Universal Links looks like regular https links. For example https://example.com might be a Universal Link.

To learn more about what are Universal Links and their features please visit Allowing Apps and Websites to Link to Your Content.

Before You Begin

In order to use regular https links as redirect URI you need to update your previously created integration made in Step 1: Create an Integration from Hosted Auth on iOS article.

To update existing integration you can use PATCH to connect/integrations/google endpoint with following payload:

{
"name": "Mobile Auth Demo",
"provider": "google",
"settings": {
"client_id": "{provider_client_id}",
"client_secret": "{provider_client_secret}"
},
"redirect_uris": [
"application-url-scheme-placeholder://auth-callback"
],
"scope": [
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/userinfo.email"
]
}

If you are migrating from custom URL schemes usage to Universal Links, make sure you delete your custom URL scheme from info.plist

Step 1: Enable support of Associated Domains

Associated domains establish a secure association between domains and your app so you can share credentials or provide features in your app from your website.

To associate a website with your app, you’ll need to have the associated domain file on your website and the appropriate entitlement in your app.

The following JSON code represents the contents of a simple association file that can handle any path in your specified domain:

{
"applinks": {
"details": [
{
"appID": "ABCDE12345.com.example.app",
"paths": ["*"]
}
],
"apps": []
}
}
  1. ABCDE12345 is Application Identifier Prefix which is Development Team ID.
  2. com.example.app is your Bundle Identifier.

After you construct the association file, place it in your site’s .well-known directory. The file’s URL should match the following format https://<fully qualified domain>/.well-known/apple-app-site-association.

To add your domain to the entitlement, click Add (+) at the bottom of the Domains table to add a placeholder domain. Replace the placeholder with the appropriate prefix for the service your app will support and your site’s domain. Make sure to only include the desired subdomain and the top-level domain. Don’t include path and query components or a trailing slash (/).

Associated Domains Setup

To learn more about Associated Domains Entitlements please visit Associated Domains Entitlements reference doc.

When the system opens your app as the result of a universal link, your app receives an NSUserActivity object with an activityType value of NSUserActivityTypeBrowsingWeb. The activity object’s webpageURL property contains the http or https URL that the user accesses.

This example code shows how to handle a universal link in iOS and tvOS:

func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool
{
// Application receives user activity to
// perform operations on inner data.
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else {
return false
}
// Check host for desired callbackHost from Constants.
// The same host as in integration creation.
if url.host == Constants.callbackHost {
OAuthSwift.handle(url: url)
return true
}
return false
}

If your app has opted into Scenes, and your app is not running, the system delivers the universal link to the scene(_:willConnectTo:options:) delegate method after launch, and to scene(_:continue:) when the universal link is tapped while your app is running or suspended in memory.

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
// Application receives user activity to
// perform operations on inner data.
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else {
return
}
// Check host for desired callbackHost from Constants.
// The same host as in integration creation.
if url.host == Constants.callbackHost {
OAuthSwift.handle(url: url)
}
}