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)
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.
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()
}
}
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 :
© 2022 - 2024 — McMap. All rights reserved.
SFAuthenticationSession
, for pre iOS 12 devices, doesn't seem to work that way ... – Fillmore