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": [] }}
ABCDE12345
is Application Identifier Prefix which is Development Team ID.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 (/).
To learn more about Associated Domains Entitlements please visit Associated Domains Entitlements reference doc.
Step 2: Handle Universal Links In Your App
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) }}