Does AsWebAuthenticationsession support universal links?

2.7k Views Asked by At

I use AsWebAuthenticationsession to authenticate from another application to my application. I open AsWebAuthenticationsession, and it redirects my application's universal links. The problem is when it redirects my application universal link, it asks to open the App Store. When it redirects I want to close the session. But AsWebAuthenticationsession only takes a custom URL Scheme. How can I handle it securely (because custom URL Schemes are not secure: RFC8252 7.1)

3

There are 3 best solutions below

1
On

I can confirm this works as of iOS 14 or later, haven't tested on earlier versions though.

When you initialize your ASWebAuthenticationSession you can pass in callbackURLScheme: "https".

When the authentication provider redirects to your universal link, your app delegate's application(_:continue:restorationHandler:) will fire with the correct redirect url, however the ASWebAuthenticationSession's completion handler does not fire and therefore the authentication dialog remains on the screen.

You will need to save a reference to the ASWebAuthenticationSession and cancel() it manually to dismiss it instead.

0
On

As today (2024) the only way I made it work (specially with SwiftUI) was creating a lambda serverless service on AWS, I registered the redirect URL with a path auth\appLogin that handle the response from the OAuth 2.0 in my case the response type was code so I needed to call another API to get the accesss_token then respond with a redirect as URL Scheme registered on my app:

           resolve({
                statusCode: 302,
                headers: {
                    'Location': `myapp://callback?access_token=${JSON.parse(body).access_token}`
                }
            });

And this is my SwiftUI code

       if let authURL = URL(string: "\(baseURL)?response_type=\(responseType)&client_id=\(clientId)&redirect_uri=\(redirectUrl)&scope=\(scopesString)") {
           let scheme = "myapp" // Your app's custom URL scheme
           let session = ASWebAuthenticationSession(url: authURL, callbackURLScheme: scheme) { callbackURL, error in
               guard error == nil, let callbackURL = callbackURL else {
                   print("Authorization failed: \(String(describing: error))")
                   return
               }
               
               // Handle the authorization code from the callbackURL
               let queryItems = URLComponents(string: callbackURL.absoluteString)?.queryItems
               if let access_token = queryItems?.first(where: { $0.name == "access_token" })?.value {
                   print("Authorization token: \(access_token)")
               }
           }
           
           session.presentationContextProvider = contextProvider
           session.start()
       }

Also remember to register your URL Scheme :

urlschememyapp

0
On

You can try with this method

To add a singleton class to handle this callback

SceneDelegate.swift

@available(iOS 13.0, *)

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {

    // handle here

    OAuthManager.instance.callBackUserActivity(userActivity: userActivity)

}

OAuthManager.swift

import Foundation

import AuthenticationServices



protocol UserActivityListener {

func callBackUserActivity( userActivity : NSUserActivity )

}



class OAuthManager {

public static let instance = OAuthManager()

var asWebSession: ASWebAuthenticationSession?

}

extension OAuthManager : UserActivityListener {

func callBackUserActivity(userActivity: NSUserActivity) {

    // Get URL components from the incoming user activity.

    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,

          let incomingURL = userActivity.webpageURL,

          let components = NSURLComponents(url: incomingURL,    resolvingAgainstBaseURL: true) else {

        return

    }

    

    // Check for specific URL components that you need.

    guard let path = components.path,

          let params = components.queryItems else {

        return

    }
    // cancel your ASWebAuthenticationSession
    asWebSession?.cancel()


    print("path = \(userActivity.webpageURL)")



    if let token = params.first(where: { $0.name == "token" })?.value {

        print("token = \(token)")

    }

  }


 }

YourViewController.swift

class YourViewController: UIViewController {
// set your instance
var oauthManager = OAuthManager.instance


private func startSignInAsWebAuthSession() {
    let callbackURLScheme = "YOURAPP"
    guard let authURL = URL(string: "YOUR URL LINK") else { return }
        
        self.oauthManager.asWebSession = ASWebAuthenticationSession.init(url: authURL, callbackURLScheme: callbackURLScheme,completionHandler: { callbackURL, error in
            // we dont listen to the call back, this web authentication session only open for login only
        })
        
        oauthManager.asWebSession?.prefersEphemeralWebBrowserSession = true
        oauthManager.asWebSession?.presentationContextProvider = self
        oauthManager.asWebSession?.start()
}
}

extension YourViewController:    ASWebAuthenticationPresentationContextProviding {
   func presentationAnchor(for session: ASWebAuthenticationSession) ->     ASPresentationAnchor {
       ASPresentationAnchor()
    }
  }